'use client' import 'chart.js/auto' import { Bar } from 'react-chartjs-2' import dayjs from 'dayjs' import { v4 as uuidv4 } from 'uuid' import { useEffect, useState, useRef, useContext } from 'react' import { useRecoilState } from 'recoil' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { pwrGnrSimTypeState } from '@/store/simulatorAtom' import { useAxios } from '@/hooks/useAxios' import { useMessage } from '@/hooks/useMessage' import { convertNumberToPriceDecimal } from '@/util/common-utils' import { usePlan } from '@/hooks/usePlan' import { usePopup } from '@/hooks/usePopup' import { QcastContext } from '@/app/QcastProvider' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' export default function Simulator() { // global 로딩바 const { setIsGlobalLoading } = useContext(QcastContext) const { floorPlanState } = useContext(FloorPlanContext) const { objectNo, pid } = floorPlanState const { selectedPlan } = usePlan() const chartRef = useRef(null) // 캔버스 메뉴 넘버 셋팅 const { closeAll } = usePopup() const { get } = useAxios() const { getMessage } = useMessage() const { setSelectedMenu } = useCanvasMenu() // 차트 관련 const [chartData, setChartData] = useState([]) const data = { labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], datasets: [ { label: 'kWh', data: chartData.slice(0, 12).map((item) => { const num = Number(item.replaceAll(',', '')) return isNaN(num) ? 0 : num }), backgroundColor: (context) => { const chart = context.chart const { ctx, chartArea } = chart if (!chartArea) { // This case happens on initial chart load return null } const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top) gradient.addColorStop(0, '#4FC3F7') // Light blue at bottom gradient.addColorStop(0.3, '#2FA8E0') // Original blue gradient.addColorStop(0.7, '#1976D2') // Medium blue gradient.addColorStop(1, '#0D47A1') // Dark blue at top return gradient }, borderColor: '#2FA8E0' , borderWidth: 1, }, ], } const options = { plugins: { legend: { position: 'top', }, }, scales: { x: { grid: { display: false, }, }, y: { beginAtZero: true, grid: { display: true, }, }, }, } useEffect(() => { setSelectedMenu('simulation') /* 초기화 작업 */ setChartData([]) setObjectDetail({}) setModuleInfoList([]) setPcsInfoList([]) setHatsudenryouAll([]) setHatsudenryouAllSnow([]) setHatsudenryouPeakcutAll([]) setHatsudenryouPeakcutAllSnow([]) if (objectNo && pid && selectedPlan) { fetchObjectDetail(objectNo, selectedPlan?.planNo??pid) fetchSimulatorNotice() setPwrGnrSimType('D') setPwrRecoil({ ...pwrRecoil, type: 'D' }) closeAll() } }, [objectNo, pid, selectedPlan]) // 물건 상세 정보 조회 const [objectDetail, setObjectDetail] = useState({}) // 모듈배치정보 조회 const [moduleInfoList, setModuleInfoList] = useState([]) // 파워컨디셔너 조회 const [pcsInfoList, setPcsInfoList] = useState([]) // 타입별 list 조회 const [hatsudenryouAll, setHatsudenryouAll] = useState([]) const [hatsudenryouAllSnow, setHatsudenryouAllSnow] = useState([]) const [hatsudenryouPeakcutAll, setHatsudenryouPeakcutAll] = useState([]) const [hatsudenryouPeakcutAllSnow, setHatsudenryouPeakcutAllSnow] = useState([]) const fetchObjectDetail = async (objectNo, currentPid) => { setIsGlobalLoading(true) const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${currentPid}` const resultData = await get({ url: apiUrl }) if (resultData) { setObjectDetail(resultData) if (resultData.hatsudenryouAll) { setHatsudenryouAll(resultData.hatsudenryouAll) } if (resultData.hatsudenryouAllSnow) { setHatsudenryouAllSnow(resultData.hatsudenryouAllSnow) } if (resultData.hatsudenryouPeakcutAll) { setHatsudenryouPeakcutAll(resultData.hatsudenryouPeakcutAll) } if (resultData.hatsudenryouPeakcutAllSnow) { setHatsudenryouPeakcutAllSnow(resultData.hatsudenryouPeakcutAllSnow) setChartData(resultData.hatsudenryouPeakcutAllSnow) } if (resultData.pcsList) { setPcsInfoList(resultData.pcsList) } if (resultData.roofModuleList) { setModuleInfoList(resultData.roofModuleList) } } setIsGlobalLoading(false) } // 시뮬레이션 안내사항 조회 const [content, setContent] = useState('') const fetchSimulatorNotice = async () => { get({ url: '/api/pwrGnrSimulation/guideInfo' }).then((res) => { if (res.data) { setContent(res.data.replaceAll('\n', '
')) } else { setContent(getMessage('common.message.no.data')) } }) } // 차트 데이터 변경 시, list type 셋팅 const [pwrGnrSimType, setPwrGnrSimType] = useState('D') const [pwrRecoil, setPwrRecoil] = useRecoilState(pwrGnrSimTypeState) const handleChartChangeData = (type) => { setPwrRecoil({ ...pwrRecoil, type: type }) switch (type) { case 'A': setChartData(hatsudenryouAll) break case 'B': setChartData(hatsudenryouPeakcutAll) break case 'C': setChartData(hatsudenryouAllSnow) break case 'D': setChartData(hatsudenryouPeakcutAllSnow) break } } return (
{/* 물건번호 */}
{getMessage('simulator.title.sub1')}
{objectDetail.objectNo} {`${objectDetail.planNo ? `(Plan No: ${objectDetail.planNo})` : ''}`}
{/* 작성일 */}
{getMessage('simulator.title.sub2')}
{objectDetail.drawingEstimateCreateDate ? `${dayjs(objectDetail.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
{/* 시스템용량 */}
{getMessage('simulator.title.sub3')}
{objectDetail.capacity ? `${convertNumberToPriceDecimal(objectDetail.capacity)} kW` : ''}
{/* 연간예측발전량 */}
{getMessage('simulator.title.sub4')}
{chartData[chartData.length - 1]}
{/* 도도부현 */}
{getMessage('simulator.title.sub5')}
{objectDetail.prefName}
{/* 일사량 관측지점 */}
{getMessage('simulator.title.sub6')}
{objectDetail.areaName}
{/* 적설조건 */}
{getMessage('simulator.title.sub7')}
{objectDetail.snowfall ? `${objectDetail.snowfall}cm` : ''}
{/* 풍속조건 */}
{getMessage('simulator.title.sub8')}
{objectDetail.standardWindSpeedId}
{/* chart */}

{getMessage('simulator.table.sub9')}

{/* 예측발전량 */}
{chartData.length > 0 ? ( {chartData.map((data) => ( ))} ) : ( )}
1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月 {getMessage('simulator.table.sub6')}
{data}
{getMessage('common.message.no.data')}
{moduleInfoList.length > 0 ? ( moduleInfoList.map((moduleInfo) => { return ( {/* 지붕면 */} {/* 경사각 */} {/* 방위각(도) */} {/* 태양전지모듈 */} {/* 매수 */} ) }) ) : ( )}
{getMessage('simulator.table.sub1')} {getMessage('simulator.table.sub2')} {getMessage('simulator.table.sub3')} {getMessage('simulator.table.sub4')} {getMessage('simulator.table.sub5')}
{moduleInfo.roofSurface} {convertNumberToPriceDecimal(moduleInfo.slopeAngle)} {moduleInfo.classType == 0 ? '寸' : 'º'} {convertNumberToPriceDecimal(moduleInfo.azimuth)}
{moduleInfo.itemNo}
{convertNumberToPriceDecimal(moduleInfo.amount)}
{getMessage('common.message.no.data')}
{moduleInfoList.length > 0 && (
{getMessage('simulator.table.sub6')}
{moduleInfoList.reduce((acc, moduleInfo) => convertNumberToPriceDecimal(Number(acc) + Number(moduleInfo.amount)), 0)}
)}
{pcsInfoList.length > 0 ? ( pcsInfoList.map((pcsInfo) => { return ( {/* 파워컨디셔너 */} {/* 대 */} ) }) ) : ( )}
{getMessage('simulator.table.sub7')} {getMessage('simulator.table.sub8')}
{pcsInfo.itemNo}
{convertNumberToPriceDecimal(pcsInfo.amount)}
{getMessage('common.message.no.data')}
{getMessage('simulator.notice.sub1')}
{getMessage('simulator.notice.sub2')}
{/* 시뮬레이션 안내사항 */}
) }