1627 lines
58 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.interplug.qcast.biz.pwrGnrSimulation;
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.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<PwrGnrSimRoofResponse> 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<PwrGnrSimRoofResponse> roofGroupModuleList =
pwrGnrSimMapper.selectRoofItemList(pwrGnrSimRequest);
// 모듈 아이템이 없음.
if (roofGroupModuleList.isEmpty()) {
log.error("모듈 아이템이 없음.");
return exceptionRes;
} else {
exceptionRes.setRoofModuleList(roofGroupModuleList);
}
// 4. 계산을 위해 order by 변경 (총용량 DESC) -계산용
List<PwrGnrSimRoofResponse> moduleGroupList =
roofGroupModuleList.stream()
.sorted(
Comparator.comparingDouble(PwrGnrSimRoofResponse::getTotSpecification)
.reversed()) // 내림차순 정렬
.collect(Collectors.toList());
// 5. PCS Item 조회
List<PwrGnrSimRoofResponse> 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);
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());
} else if (k == 1) {
pwrGnrSimReq.setModule2(m.getItemId());
pwrGnrSimReq.setModuleAmp2(m.getAmp());
pwrGnrSimReq.setModuleSpecification2(m.getSpecification());
pwrGnrSimReq.setModuleTempCoeff2(m.getTempCoeff());
pwrGnrSimReq.setModuleTempLoss2(m.getTempLoss());
} else {
pwrGnrSimReq.setModule3(m.getItemId());
pwrGnrSimReq.setModuleAmp3(m.getAmp());
pwrGnrSimReq.setModuleSpecification3(m.getSpecification());
pwrGnrSimReq.setModuleTempCoeff3(m.getTempCoeff());
pwrGnrSimReq.setModuleTempLoss3(m.getTempLoss());
}
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));
// 지붕별 모듈정보 셋팅
int j = 0;
for (PwrGnrSimRoofResponse m : moduleGroupList) {
if (data.getRoofSurfaceId().equals(m.getRoofSurfaceId())) {
dSpecification += m.getTotSpecification();
if (j == 0) {
dModuleInput1[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);
} 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);
return pwrGnrSimRes;
}
/**
* 실제 계산로직
*
* @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;
}
/** 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[] readValuesFromFile(String fileName) {
List<Double> 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<String[]> 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<double[]> 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[]>을 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("<tr>");
sb.append("<td ")
.append(i == 8 ? "class='top'" : "")
.append(">")
.append(maxValue * i)
.append("</td>");
sb.append("</tr>");
}
sb.append("<tr>");
sb.append("<td class='zero'>0</td>");
elm = doc.getElementById("htmlY");
elm.append(sb.toString());
// chart 데이터
String[] color = {
"#B5D4F5", "#FFE899", "#FBC3AB", "#D1D1D1", "#FFE899", "#B5D0F0", "#C1E4B8", "#A3C9EE",
"#F7BBA2", "#BEBEBE", "#A8BBE0", "#B5D7A3"
};
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < onlyData.length; i++) {
double scaledValues = ((double) onlyData[i] / (maxValue * 8)) * referenceValue;
sb2.append("<td class='bar-cell'>");
sb2.append("<table class='chart-line' style='border-collapse: collapse;'>")
.append("<tbody>")
.append("<tr>")
.append("<td class='top' style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("<tr>")
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("<tr>")
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("<tr>")
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("<tr>")
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("<tr>")
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("<tr>")
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("<tr>")
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("<tr>")
.append("<td style='border-bottom: 1px solid #ddd;'></td>")
.append("</tr>")
.append("</tbody>")
.append("</table>");
sb2.append(
"<div class='bar' style='height: "
+ scaledValues
+ "px; background-color: "
+ color[i]
+ "'></div>");
sb2.append("</td>");
}
elm = doc.getElementById("htmlX");
elm.append(sb2.toString());
}
// 예측발전량
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 13; i++) {
sb.append("<td>" + StringUtils.defaultString(pwrGnrSimList[i]) + "</td>");
}
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("<tr>");
sb.append("<td>" + StringUtils.defaultString(listItem.getRoofSurface()) + "</td>");
sb.append("<td>" + StringUtils.defaultString(listItem.getSlopeAngleTxt()) + "</td>");
sb.append("<td>" + StringUtils.defaultString(listItem.getAzimuth()) + "</td>");
sb.append("<td>" + StringUtils.defaultString(listItem.getItemNo()) + "</td>");
sb.append("<td>" + StringUtils.defaultString(listItem.getAmount()) + "</td>");
sb.append("</tr>");
}
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("<tr>");
sb.append("<td>" + StringUtils.defaultString(listItem.getItemNo()) + "</td>");
sb.append("<td>" + StringUtils.defaultString(listItem.getAmount()) + "</td>");
sb.append("</tr>");
}
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<PwrGnrSimRoofResponse> 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<PwrGnrSimRoofResponse> 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);
}
}
}