Skip to content

Commit

Permalink
More Changes to PV yield estimation #363
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianK13 committed Jan 17, 2025
1 parent db0511a commit 003ad36
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 66 deletions.
250 changes: 184 additions & 66 deletions src/components/ThreeViewer/Overlay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import {
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
import { NumberInputField, NumberInputRoot } from '@/components/ui/number-input'
import { Slider } from '@/components/ui/slider'
import { Switch } from '@/components/ui/switch'
import { Box, List, SimpleGrid } from '@chakra-ui/react'
import { Box, List, SimpleGrid, Text } from '@chakra-ui/react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { createPVSystem } from './Meshes/PVSystems'
Expand Down Expand Up @@ -96,6 +97,55 @@ function Overlay({
)
}

const ControlHelperDialog = () => {
const touchDeviceText = window.isTouchDevice ? 'touch.' : ''
const { t } = useTranslation()
return (
<DialogRoot>
<DialogTrigger asChild>
<Button>{t('mapControlHelp.button')}</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>{t(`mapControlHelp.title`)}</DialogTitle>
</DialogHeader>
<DialogBody>
<List.Root>
<List.Item>
{t(`mapControlHelp.${touchDeviceText}leftMouse`)}
</List.Item>
<List.Item>
{t(`mapControlHelp.${touchDeviceText}rightMouse`)}
</List.Item>
<List.Item>
{t(`mapControlHelp.${touchDeviceText}wheel`)}
</List.Item>
<List.Item>
{t(`mapControlHelp.${touchDeviceText}doubleClick`)}
</List.Item>
</List.Root>
</DialogBody>
<DialogCloseTrigger />
</DialogContent>
</DialogRoot>
)
}

const OverlayWrapper = ({ children }) => {
return (
<Box pointerEvents='none' zIndex={100}>
<SimpleGrid
margin='10px'
minChildWidth='sm'
gap='40px'
pointerEvents='auto'
>
{children}
</SimpleGrid>
</Box>
)
}

return (
<OverlayWrapper>
{selectedPVSystem.length > 0 && (
Expand Down Expand Up @@ -149,23 +199,143 @@ const SelectionNotificationPV = ({
setPVSystems,
}) => {
const { t } = useTranslation()
const SavingCalculation = ({ selectedPVSystem, setSelectedPVSystem }) => {
const SavingCalculationDialog = ({
selectedPVSystem,
setSelectedPVSystem,
}) => {
const { t } = useTranslation()
const [annualConsumption, setAnnualConsumption] = useState('3000')
const [storageCapacity, setStorageCapacity] = useState('0')
const [electricityPrice, setElectricityPrice] = useState('30')
const [selfConsumption, setSelfConsumption] = useState(0)
const [annualSavings, setAnnualSavings] = useState(0)

// Helper function to normalize input with different decimal separators
const normalizeInput = (value) => {
return value.replace(',', '.')
}

// Helper function to handle numeric input changes
const handleNumericChange = (setter) => (e) => {
const value = e.target.value
if (value === '' || /^[0-9]*[.,]?[0-9]*$/.test(value)) {
setter(value)
}
}

let pvProduction
if (selectedPVSystem.length > 0) {
pvProduction = Math.round(
selectedPVSystem.reduce(
(previous, current) => previous + current.annualYield,
0,
),
)
}

async function handleCalculateSaving() {
async function calculateSaving({
pvProduction,
consumptionHousehold,
storageCapacity,
electricityPrice,
setSelfConsumption,
setAnnualSavings,
}) {
const response = await fetch(
'https://www.openpv.de/data/savings_calculation/cons_prod.json',
)
const data = await response.json()

const normalizedConsumption = data['Consumption']
const normalizedProduction = data['Production']

const result = {}
let currentStorageLevel = 0
for (const timestamp in normalizedConsumption) {
const consumptionValue =
(normalizedConsumption[timestamp] * consumptionHousehold) / 1000
const productionValue =
(normalizedProduction[timestamp] * pvProduction) / 1000

let selfConsumption = 0
let excessProduction = 0

if (productionValue > consumptionValue) {
selfConsumption = consumptionValue
excessProduction = productionValue - consumptionValue

// Charge the storage
const availableStorageSpace = storageCapacity - currentStorageLevel
const chargedAmount = Math.min(
excessProduction,
availableStorageSpace,
)
currentStorageLevel += chargedAmount
} else {
const productionDeficit = consumptionValue - productionValue

// Use storage if available
const usedFromStorage = Math.min(
productionDeficit,
currentStorageLevel,
)
currentStorageLevel -= usedFromStorage

selfConsumption = productionValue + usedFromStorage
}

result[timestamp] = selfConsumption
}

let selfConsumedElectricity = Object.values(result).reduce(
(acc, val) => acc + val,
0,
)

setSelfConsumption(Math.round(selfConsumedElectricity))
setAnnualSavings(
Math.round((selfConsumedElectricity * electricityPrice) / 100),
)
}

await calculateSaving({
pvProduction: pvProduction,
consumptionHousehold: parseFloat(normalizeInput(annualConsumption)),
storageCapacity: parseFloat(normalizeInput(storageCapacity)),
electricityPrice: parseFloat(normalizeInput(electricityPrice)),
setSelfConsumption: setSelfConsumption,
setAnnualSavings: setAnnualSavings,
})
}

return (
<DialogRoot>
<DialogRoot size='lg'>
<DialogTrigger asChild>
<Button variant='outline' size='sm'>
Open Dialog
</Button>
<Button>{t('savingsCalculation.button')}</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogTitle>{t('savingsCalculation.button')}</DialogTitle>
</DialogHeader>
<DialogBody>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<p>{t('savingsCalculation.disclaimer')}</p>
<SimpleGrid columns={2} gap='10px'>
<Text as='b' color={'black'}>
{t('savingsCalculation.results.production')}
</Text>

<Text as='b' color={'black'}>
{pvProduction} kWh
</Text>
<NumberInputRoot
maxW='200px'
value={annualConsumption}
onValueChange={(e) => setAnnualConsumption(e.value)}
>
<NumberInputField />
</NumberInputRoot>
</SimpleGrid>
</DialogBody>
<DialogFooter>
<DialogActionTrigger asChild>
Expand All @@ -179,19 +349,14 @@ const SelectionNotificationPV = ({
)
}
return (
<DialogRoot open={true} placement='bottom'>
<DialogRoot open={true} placement='bottom' size='xs'>
<DialogContent>
<DialogHeader>
<DialogTitle>{t('savingsCalculation.notificationLabel')}</DialogTitle>
</DialogHeader>
<DialogBody>
<SimpleGrid
margin='10px'
minChildWidth='sm'
gap='40px'
pointerEvents='auto'
>
<SavingCalculation
<SimpleGrid gap='5px'>
<SavingCalculationDialog
selectedPVSystem={selectedPVSystem}
setSelectedPVSystem={setSelectedPVSystem}
/>
Expand All @@ -212,51 +377,4 @@ const SelectionNotificationPV = ({
)
}

const ControlHelperDialog = () => {
const touchDeviceText = window.isTouchDevice ? 'touch.' : ''
const { t } = useTranslation()
return (
<DialogRoot>
<DialogTrigger asChild>
<Button>{t('mapControlHelp.button')}</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>{t(`mapControlHelp.title`)}</DialogTitle>
</DialogHeader>
<DialogBody>
<List.Root>
<List.Item>
{t(`mapControlHelp.${touchDeviceText}leftMouse`)}
</List.Item>
<List.Item>
{t(`mapControlHelp.${touchDeviceText}rightMouse`)}
</List.Item>
<List.Item>{t(`mapControlHelp.${touchDeviceText}wheel`)}</List.Item>
<List.Item>
{t(`mapControlHelp.${touchDeviceText}doubleClick`)}
</List.Item>
</List.Root>
</DialogBody>
<DialogCloseTrigger />
</DialogContent>
</DialogRoot>
)
}

const OverlayWrapper = ({ children }) => {
return (
<Box pointerEvents='none' zIndex={100}>
<SimpleGrid
margin='10px'
minChildWidth='sm'
gap='40px'
pointerEvents='auto'
>
{children}
</SimpleGrid>
</Box>
)
}

export default Overlay
21 changes: 21 additions & 0 deletions src/components/ui/number-input.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NumberInput as ChakraNumberInput } from '@chakra-ui/react'
import * as React from 'react'

export const NumberInputRoot = React.forwardRef(
function NumberInput(props, ref) {
const { children, ...rest } = props
return (
<ChakraNumberInput.Root ref={ref} variant='outline' {...rest}>
{children}
<ChakraNumberInput.Control>
<ChakraNumberInput.IncrementTrigger />
<ChakraNumberInput.DecrementTrigger />
</ChakraNumberInput.Control>
</ChakraNumberInput.Root>
)
},
)

export const NumberInputField = ChakraNumberInput.Input
export const NumberInputScrubber = ChakraNumberInput.Scrubber
export const NumberInputLabel = ChakraNumberInput.Label

0 comments on commit 003ad36

Please sign in to comment.