Compare commits
2 Commits
main
...
ae3b61c6fb
| Author | SHA1 | Date | |
|---|---|---|---|
| ae3b61c6fb | |||
| 610468effc |
@@ -6,6 +6,9 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
|
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
|
||||||
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
|
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
|
||||||
|
<WasmAllowUndefinedSymbols>true</WasmAllowUndefinedSymbols>
|
||||||
|
<RunAOTCompilation>true</RunAOTCompilation>
|
||||||
|
<WasmShellGenerateAOTProfile>true</WasmShellGenerateAOTProfile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
31
Dockerfile
31
Dockerfile
@@ -1,5 +1,5 @@
|
|||||||
# Build stage
|
# Build stage
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS launch
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
# Install Python (required for AOT compilation)
|
# Install Python (required for AOT compilation)
|
||||||
@@ -7,21 +7,28 @@ RUN apt-get update && \
|
|||||||
apt-get install -y python3 && \
|
apt-get install -y python3 && \
|
||||||
ln -s /usr/bin/python3 /usr/bin/python
|
ln -s /usr/bin/python3 /usr/bin/python
|
||||||
|
|
||||||
# Install required workloads
|
|
||||||
RUN dotnet workload install wasm-tools
|
# Update workloads and SDK
|
||||||
|
RUN dotnet --list-sdks
|
||||||
|
RUN dotnet --list-runtimes
|
||||||
|
RUN dotnet workload update
|
||||||
|
|
||||||
# Copy project and solution files
|
# Copy project and solution files
|
||||||
COPY *.csproj ./
|
COPY *.csproj *.sln ./
|
||||||
RUN dotnet restore
|
|
||||||
|
# Install required workloads
|
||||||
|
RUN dotnet workload install wasm-tools
|
||||||
|
|
||||||
# Copy the rest of the application
|
# Copy the rest of the application
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Publish the app
|
# Restore dependencies
|
||||||
RUN dotnet publish -c Release -o /app/publish
|
RUN dotnet restore
|
||||||
|
|
||||||
|
# Publish the app (optional, if you want to test publish output)
|
||||||
|
# RUN dotnet publish -c Release -o /app/publish
|
||||||
|
|
||||||
|
# Runtime stage: Use the SDK image to run the app
|
||||||
|
ENTRYPOINT ["dotnet", "run", "--no-launch-profile", "--urls", "http://0.0.0.0:5000"]
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
# Runtime stage: Use Nginx to serve static files
|
|
||||||
FROM nginx:alpine
|
|
||||||
COPY --from=build /app/publish/wwwroot /usr/share/nginx/html
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
||||||
EXPOSE 80
|
|
||||||
|
|||||||
8
Models/PipelineStep.cs
Normal file
8
Models/PipelineStep.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace BattSim.Models;
|
||||||
|
|
||||||
|
public struct PipelineStep
|
||||||
|
{
|
||||||
|
public bool Expanded;
|
||||||
|
public bool Completed;
|
||||||
|
public bool isProcessing;
|
||||||
|
}
|
||||||
294
Pages/Home.razor
294
Pages/Home.razor
@@ -3,268 +3,44 @@
|
|||||||
@using Radzen.Blazor
|
@using Radzen.Blazor
|
||||||
@using BattSim.Models
|
@using BattSim.Models
|
||||||
@using BattSim.Services
|
@using BattSim.Services
|
||||||
|
@using BattSim.Pages.Nodes
|
||||||
|
|
||||||
<PageTitle>Thuisbatterij Simulator</PageTitle>
|
<PageTitle>Wattif</PageTitle>
|
||||||
|
|
||||||
<h1 style="color: white">Thuisbatterij Simulator</h1>
|
<h1 style="color: white">Wattif - Simuleer verbeteringen aan je energiesysteem</h1>
|
||||||
|
|
||||||
<div class="pipeline-container">
|
<div class="pipeline-container">
|
||||||
<!-- Pipeline with 3 connected boxes -->
|
<!-- Pipeline with 3 connected boxes -->
|
||||||
<div class="pipeline-row">
|
<div class="pipeline-row">
|
||||||
<div class="pipeline-connector"></div>
|
<div class="pipeline-connector"></div>
|
||||||
|
|
||||||
<!-- Box 1: Data Upload -->
|
<!-- Node Components -->
|
||||||
<div class="pipeline-box @GetPipelineStepClasses(0)">
|
<FluviusUploadNode
|
||||||
<div class="box-header" @onclick="() => ToggleBox(0)">
|
FluviusDataRaw="FluviusDataRaw"
|
||||||
<div class="step-number">1</div>
|
StepData="StepData[0]"
|
||||||
<div class="step-title">Fluvius Data</div>
|
UploadedFileName="_uploadedFileName"
|
||||||
<div class="status-indicator"></div>
|
UploadError="_uploadError"
|
||||||
</div>
|
OnFileUploadedCallback="OnFileUploaded"
|
||||||
<div class="box-content">
|
OnToggleBoxCallback="() => ToggleBox(0)" />
|
||||||
<div class="box-content-inner">
|
|
||||||
<p class="box-description">Upload je Fluvius kwartierdata om te beginnen</p>
|
|
||||||
|
|
||||||
<div class="form-section">
|
<BatterySimulationNode
|
||||||
<div class="file-input-wrapper">
|
FluviusDataRaw="FluviusDataRaw"
|
||||||
<InputFile type="file" id="fileUpload" accept=".csv" onchange="async (e) => await OnFileUploaded(e)" />
|
SimulationData="SimulationData"
|
||||||
<label for="fileUpload" class="file-input-label">
|
StepData="StepData[1]"
|
||||||
@if (StepData[0].isProcessing)
|
BatteryCapacity="BatteryCapacity"
|
||||||
{
|
Efficiency="Efficiency"
|
||||||
<div class="loading-indicator">
|
OnSimulateBatteryCallback="SimulateBattery"
|
||||||
<div class="spinner"></div>
|
OnToggleBoxCallback="() => ToggleBox(1)" />
|
||||||
<span style="margin-left: 10px;">Data aan het inladen...</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@if (FluviusDataRaw.Length == 0)
|
|
||||||
{
|
|
||||||
<text>Kies CSV bestand</text>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<text>OK @FluviusDataRaw.Length kwartieren geladen</text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</label>
|
|
||||||
@if (FluviusDataRaw.Length > 0 && !StepData[0].isProcessing)
|
|
||||||
{
|
|
||||||
<p class="file-name">@_uploadedFileName</p>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (StepData[0].Completed)
|
<CostCalculationNode
|
||||||
{
|
SimulationData="SimulationData"
|
||||||
<div class="success-message">
|
FluviusDataRaw="FluviusDataRaw"
|
||||||
Data succesvol ingeladen en verwerkt!
|
StepData="StepData[2]"
|
||||||
</div>
|
Calculator="calculator"
|
||||||
}
|
NormalCost="normalCost"
|
||||||
@if (_uploadError != null)
|
SimulatedCost="simulatedCost"
|
||||||
{
|
OnCalculateCallback="Calculate"
|
||||||
<div class="error-message">
|
OnToggleBoxCallback="() => ToggleBox(2)" />
|
||||||
@_uploadError
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="info-text">
|
|
||||||
<p><strong>Tip:</strong> Download een volledig jaar aan kwartierdata voor de meest accurate resultaten.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Box 2: Battery Simulation -->
|
|
||||||
<div class="pipeline-box @GetPipelineStepClasses(1)">
|
|
||||||
<div class="box-header" @onclick="() => ToggleBox(1)">
|
|
||||||
<div class="step-number">2</div>
|
|
||||||
<div class="step-title">Batterij Simulatie</div>
|
|
||||||
<div class="status-indicator"></div>
|
|
||||||
</div>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-inner">
|
|
||||||
<p class="box-description">Test verschillende batterijconfiguraties</p>
|
|
||||||
|
|
||||||
@if (FluviusDataRaw.Length == 0)
|
|
||||||
{
|
|
||||||
<div class="info-text">
|
|
||||||
<p>Upload eerst je Fluvius data (stap 1) om deze stap te kunnen uitvoeren.</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="form-section">
|
|
||||||
<div style="display: flex; gap: 1rem;">
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Batterijcapaciteit (kWh):</label>
|
|
||||||
<InputNumber @bind-value="BatteryCapacity" T="double" min="0.1" max="100" step="0.1"/>
|
|
||||||
</div>
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Round-trip Efficiëntie (%):</label>
|
|
||||||
<InputNumber @bind-value="Efficiency" T="double" min="0" max="100" step="1"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p style="font-size: 0.85rem; color: var(--text-clr); margin-top: 1rem;">
|
|
||||||
Efficiëntie is het percentage van opgeslagen energie dat terug kan worden gebruikt.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: center; margin-top: 1.5rem;">
|
|
||||||
<button @onclick="SimulateBattery" @onclick:stopPropagation disabled="@(StepData[1].isProcessing || FluviusDataRaw.Length == 0)">
|
|
||||||
@if (StepData[1].isProcessing)
|
|
||||||
{
|
|
||||||
<text>Simuleren...</text>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<text>Simuleer Batterij</text>
|
|
||||||
}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (StepData[1].Completed)
|
|
||||||
{
|
|
||||||
<div class="success-message">
|
|
||||||
Simulatie voltooid! @SimulationData.Length kwartieren gesimuleerd.
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Box 3: Cost Calculation -->
|
|
||||||
<div class="pipeline-box @GetPipelineStepClasses(2)">
|
|
||||||
<div class="box-header" @onclick="() => ToggleBox(2)">
|
|
||||||
<div class="step-number">3</div>
|
|
||||||
<div class="step-title">Kosten Berekenen</div>
|
|
||||||
<div class="status-indicator"></div>
|
|
||||||
</div>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-inner">
|
|
||||||
<p class="box-description">Vergelijk kosten met en zonder batterij</p>
|
|
||||||
|
|
||||||
<!-- Contractuele Kosten -->
|
|
||||||
<div style="margin-bottom: 1.5rem;">
|
|
||||||
<h4 style="color: var(--green-clr); margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-clr); padding-bottom: 0.5rem;">
|
|
||||||
Contractuele Kosten
|
|
||||||
</h4>
|
|
||||||
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Vaste vergoeding (€/jaar):</label>
|
|
||||||
<InputNumber @bind-value="calculator.BasePayment" T="double" min="0" step="1"/>
|
|
||||||
</div>
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Databeheer tarief (€/jaar):</label>
|
|
||||||
<InputNumber @bind-value="calculator.DataManagementCost" T="double" min="0" step="0.01"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Vaste Kosten Subbox (Collapsed by Default) -->
|
|
||||||
<details class="collapsible-section" style="margin-top: 1rem;">
|
|
||||||
<summary class="collapsible-header">Vaste Kosten</summary>
|
|
||||||
<div class="collapsible-content">
|
|
||||||
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
||||||
<!-- Vlaamse energieheffing (moved here) -->
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Vlaamse energieheffing (€/jaar):</label>
|
|
||||||
<InputNumber @bind-value="calculator.FlemishEnergyTariff" T="double" min="0" step="0.01"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Heffingen Groene Stroom -->
|
|
||||||
<div style="margin-top: 1rem;">
|
|
||||||
<h5 style="color: var(--green-clr); margin-bottom: 0.5rem;">Heffingen Groene Stroom</h5>
|
|
||||||
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Groene stroomcertificaten (c€/kWh):</label>
|
|
||||||
<InputNumber @bind-value="calculator.GreenCertificateCost" T="double" min="0" step="0.0001"/>
|
|
||||||
</div>
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Warmtekracht certificaten (c€/kWh):</label>
|
|
||||||
<InputNumber @bind-value="calculator.HeatingCertificateCost" T="double" min="0" step="0.0001"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Net- en distributiekosten -->
|
|
||||||
<div style="margin-top: 1rem;">
|
|
||||||
<h5 style="color: var(--green-clr); margin-bottom: 0.5rem;">Net- en distributiekosten</h5>
|
|
||||||
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Capaciteitstarief (€/kWh/jaar):</label>
|
|
||||||
<InputNumber @bind-value="calculator.CapacityCost" T="double" min="0" step="0.01"/>
|
|
||||||
</div>
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Afnametarief (c€/kWh):</label>
|
|
||||||
<InputNumber @bind-value="calculator.UsageTariff" T="double" min="0" step="0.0001"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Heffingen en Toeslagen -->
|
|
||||||
<div style="margin-top: 1rem;">
|
|
||||||
<h5 style="color: var(--green-clr); margin-bottom: 0.5rem;">Heffingen en Toeslagen</h5>
|
|
||||||
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Energiebijdrage (c€/kWh):</label>
|
|
||||||
<InputNumber @bind-value="calculator.EnergyContribution" T="double" min="0" step="0.0001"/>
|
|
||||||
</div>
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Bijzondere accijns (c€/kWh):</label>
|
|
||||||
<InputNumber @bind-value="calculator.SpecialTariffs" T="double" min="0" step="0.0001"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Energiekosten -->
|
|
||||||
<div style="margin-bottom: 1.5rem;">
|
|
||||||
<h4 style="color: var(--green-clr); margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-clr); padding-bottom: 0.5rem;">
|
|
||||||
Energiekosten
|
|
||||||
</h4>
|
|
||||||
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Energiekost (c€/kWh):</label>
|
|
||||||
<InputNumber @bind-value="calculator.EnergyCost" T="double" min="0" step="0.0001"/>
|
|
||||||
</div>
|
|
||||||
<div class="input-group" style="flex: 1;">
|
|
||||||
<label>Terugleveringsvergoeding (c€/kWh):</label>
|
|
||||||
<InputNumber @bind-value="calculator.ReturnCost" T="double" min="0" step="0.0001"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: center; margin-top: 1.5rem;">
|
|
||||||
<button @onclick="Calculate" @onclick:stopPropagation disabled="@(StepData[2].isProcessing || SimulationData.Length == 0)">
|
|
||||||
@if (StepData[2].isProcessing)
|
|
||||||
{
|
|
||||||
<text>Berekenen...</text>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<text>Bereken Kosten</text>
|
|
||||||
}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (StepData[2].Completed)
|
|
||||||
{
|
|
||||||
<div class="results">
|
|
||||||
<p><strong>Originele kosten:</strong> <span class="result-value">€@normalCost.ToString("0.00")</span></p>
|
|
||||||
<p><strong>Gesimuleerde kosten:</strong> <span class="result-value">€@simulatedCost.ToString("0.00")</span></p>
|
|
||||||
<p><strong>Besparing:</strong> <span class="result-value">€@((normalCost - simulatedCost).ToString("0.00"))</span></p>
|
|
||||||
@if (normalCost > 0)
|
|
||||||
{
|
|
||||||
<p><strong>Besparingspercent:</strong> <span class="result-value">@(((normalCost - simulatedCost) / normalCost * 100).ToString("0.00"))%</span></p>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -338,12 +114,6 @@
|
|||||||
double normalCost = 0.0;
|
double normalCost = 0.0;
|
||||||
double simulatedCost = 0.0;
|
double simulatedCost = 0.0;
|
||||||
|
|
||||||
struct PipelineStep{
|
|
||||||
public bool Expanded;
|
|
||||||
public bool Completed;
|
|
||||||
public bool isProcessing;
|
|
||||||
}
|
|
||||||
|
|
||||||
PipelineStep[] StepData = [
|
PipelineStep[] StepData = [
|
||||||
new PipelineStep(), // Step 1
|
new PipelineStep(), // Step 1
|
||||||
new PipelineStep(), // Step 2
|
new PipelineStep(), // Step 2
|
||||||
@@ -358,14 +128,6 @@
|
|||||||
double BatteryCapacity = 7.5;
|
double BatteryCapacity = 7.5;
|
||||||
double Efficiency = 90;
|
double Efficiency = 90;
|
||||||
|
|
||||||
private string GetPipelineStepClasses(int step){
|
|
||||||
var classes = new List<string>();
|
|
||||||
if (StepData[step].Expanded) classes.Add("expanded");
|
|
||||||
if (StepData[step].Completed) classes.Add("completed");
|
|
||||||
if (StepData[step].isProcessing) classes.Add("running");
|
|
||||||
return string.Join(" ", classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetTimeButtonClass(string period)
|
private string GetTimeButtonClass(string period)
|
||||||
{
|
{
|
||||||
var baseClass = "time-toggle-button";
|
var baseClass = "time-toggle-button";
|
||||||
|
|||||||
104
Pages/Nodes/BatterySimulationNode.razor
Normal file
104
Pages/Nodes/BatterySimulationNode.razor
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
@using Radzen
|
||||||
|
@using Radzen.Blazor
|
||||||
|
@using BattSim.Models
|
||||||
|
@using BattSim.Services
|
||||||
|
|
||||||
|
<div class="pipeline-box @GetPipelineStepClasses(StepData)">
|
||||||
|
<div class="box-header" @onclick="ToggleBox">
|
||||||
|
<div class="step-number">2</div>
|
||||||
|
<div class="step-title">Batterij Simulatie</div>
|
||||||
|
<div class="status-indicator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-inner">
|
||||||
|
<p class="box-description">Test verschillende batterijconfiguraties</p>
|
||||||
|
|
||||||
|
@if (FluviusDataRaw.Length == 0)
|
||||||
|
{
|
||||||
|
<div class="info-text">
|
||||||
|
<p>Upload eerst je Fluvius data (stap 1) om deze stap te kunnen uitvoeren.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="form-section">
|
||||||
|
<div style="display: flex; gap: 1rem;">
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Batterijcapaciteit (kWh):</label>
|
||||||
|
<InputNumber @bind-value="BatteryCapacity" T="double" min="0.1" max="100" step="0.1" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Round-trip Efficiëntie (%):</label>
|
||||||
|
<InputNumber @bind-value="Efficiency" T="double" min="0" max="100" step="1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 0.85rem; color: var(--text-clr); margin-top: 1rem;">
|
||||||
|
Efficiëntie is het percentage van opgeslagen energie dat terug kan worden gebruikt.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: center; margin-top: 1.5rem;">
|
||||||
|
<button @onclick="SimulateBattery" @onclick:stopPropagation disabled="@(StepData.isProcessing || FluviusDataRaw.Length == 0)">
|
||||||
|
@if (StepData.isProcessing)
|
||||||
|
{
|
||||||
|
<text>Simuleren...</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<text>Simuleer Batterij</text>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (StepData.Completed)
|
||||||
|
{
|
||||||
|
<div class="success-message">
|
||||||
|
Simulatie voltooid! @SimulationData.Length kwartieren gesimuleerd.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public EnergyData[] FluviusDataRaw { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EnergyData[] SimulationData { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public PipelineStep StepData { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public double BatteryCapacity { get; set; } = 7.5;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public double Efficiency { get; set; } = 90;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback OnSimulateBatteryCallback { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback OnToggleBoxCallback { get; set; }
|
||||||
|
|
||||||
|
private string GetPipelineStepClasses(PipelineStep step)
|
||||||
|
{
|
||||||
|
var classes = new List<string>();
|
||||||
|
if (step.Expanded) classes.Add("expanded");
|
||||||
|
if (step.Completed) classes.Add("completed");
|
||||||
|
if (step.isProcessing) classes.Add("running");
|
||||||
|
return string.Join(" ", classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SimulateBattery()
|
||||||
|
{
|
||||||
|
OnSimulateBatteryCallback.InvokeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleBox()
|
||||||
|
{
|
||||||
|
OnToggleBoxCallback.InvokeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
180
Pages/Nodes/CostCalculationNode.razor
Normal file
180
Pages/Nodes/CostCalculationNode.razor
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
@using Radzen
|
||||||
|
@using Radzen.Blazor
|
||||||
|
@using BattSim.Models
|
||||||
|
@using BattSim.Services
|
||||||
|
|
||||||
|
<div class="pipeline-box @GetPipelineStepClasses(StepData)">
|
||||||
|
<div class="box-header" @onclick="ToggleBox">
|
||||||
|
<div class="step-number">3</div>
|
||||||
|
<div class="step-title">Kosten Berekenen</div>
|
||||||
|
<div class="status-indicator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-inner">
|
||||||
|
<p class="box-description">Vergelijk kosten met en zonder batterij</p>
|
||||||
|
|
||||||
|
<!-- Contractuele Kosten -->
|
||||||
|
<div style="margin-bottom: 1.5rem;">
|
||||||
|
<h4 style="color: var(--green-clr); margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-clr); padding-bottom: 0.5rem;">
|
||||||
|
Contractuele Kosten
|
||||||
|
</h4>
|
||||||
|
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Vaste vergoeding (€/jaar):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.BasePayment" T="double" min="0" step="1" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Databeheer tarief (€/jaar):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.DataManagementCost" T="double" min="0" step="0.01" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Vaste Kosten Subbox (Collapsed by Default) -->
|
||||||
|
<details class="collapsible-section" style="margin-top: 1rem;">
|
||||||
|
<summary class="collapsible-header">Vaste Kosten</summary>
|
||||||
|
<div class="collapsible-content">
|
||||||
|
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Vlaamse energieheffing (€/jaar):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.FlemishEnergyTariff" T="double" min="0" step="0.01" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Heffingen Groene Stroom -->
|
||||||
|
<div style="margin-top: 1rem;">
|
||||||
|
<h5 style="color: var(--green-clr); margin-bottom: 0.5rem;">Heffingen Groene Stroom</h5>
|
||||||
|
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Groene stroomcertificaten (c€/kWh):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.GreenCertificateCost" T="double" min="0" step="0.0001" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Warmtekracht certificaten (c€/kWh):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.HeatingCertificateCost" T="double" min="0" step="0.0001" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Net- en distributiekosten -->
|
||||||
|
<div style="margin-top: 1rem;">
|
||||||
|
<h5 style="color: var(--green-clr); margin-bottom: 0.5rem;">Net- en distributiekosten</h5>
|
||||||
|
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Capaciteitstarief (€/kWh/jaar):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.CapacityCost" T="double" min="0" step="0.01" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Afnametarief (c€/kWh):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.UsageTariff" T="double" min="0" step="0.0001" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Heffingen en Toeslagen -->
|
||||||
|
<div style="margin-top: 1rem;">
|
||||||
|
<h5 style="color: var(--green-clr); margin-bottom: 0.5rem;">Heffingen en Toeslagen</h5>
|
||||||
|
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Energiebijdrage (c€/kWh):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.EnergyContribution" T="double" min="0" step="0.0001" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Bijzondere accijns (c€/kWh):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.SpecialTariffs" T="double" min="0" step="0.0001" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Energiekosten -->
|
||||||
|
<div style="margin-bottom: 1.5rem;">
|
||||||
|
<h4 style="color: var(--green-clr); margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-clr); padding-bottom: 0.5rem;">
|
||||||
|
Energiekosten
|
||||||
|
</h4>
|
||||||
|
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Energiekost (c€/kWh):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.EnergyCost" T="double" min="0" step="0.0001" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group" style="flex: 1;">
|
||||||
|
<label>Terugleveringsvergoeding (c€/kWh):</label>
|
||||||
|
<InputNumber @bind-value="Calculator.ReturnCost" T="double" min="0" step="0.0001" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: center; margin-top: 1.5rem;">
|
||||||
|
<button @onclick="Calculate" @onclick:stopPropagation disabled="@(StepData.isProcessing || SimulationData.Length == 0)">
|
||||||
|
@if (StepData.isProcessing)
|
||||||
|
{
|
||||||
|
<text>Berekenen...</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<text>Bereken Kosten</text>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (StepData.Completed)
|
||||||
|
{
|
||||||
|
<div class="results">
|
||||||
|
<p><strong>Originele kosten:</strong> <span class="result-value">€@NormalCost.ToString("0.00")</span></p>
|
||||||
|
<p><strong>Gesimuleerde kosten:</strong> <span class="result-value">€@SimulatedCost.ToString("0.00")</span></p>
|
||||||
|
<p><strong>Besparing:</strong> <span class="result-value">€@((NormalCost - SimulatedCost).ToString("0.00"))</span></p>
|
||||||
|
@if (NormalCost > 0)
|
||||||
|
{
|
||||||
|
<p><strong>Besparingspercent:</strong> <span class="result-value">@(((NormalCost - SimulatedCost) / NormalCost * 100).ToString("0.00"))%</span></p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public EnergyData[] SimulationData { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EnergyData[] FluviusDataRaw { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public PipelineStep StepData { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EnergyCostCalculator Calculator { get; set; } = new();
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public double NormalCost { get; set; } = 0.0;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public double SimulatedCost { get; set; } = 0.0;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback OnCalculateCallback { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback OnToggleBoxCallback { get; set; }
|
||||||
|
|
||||||
|
private string GetPipelineStepClasses(PipelineStep step)
|
||||||
|
{
|
||||||
|
var classes = new List<string>();
|
||||||
|
if (step.Expanded) classes.Add("expanded");
|
||||||
|
if (step.Completed) classes.Add("completed");
|
||||||
|
if (step.isProcessing) classes.Add("running");
|
||||||
|
return string.Join(" ", classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Calculate()
|
||||||
|
{
|
||||||
|
OnCalculateCallback.InvokeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleBox()
|
||||||
|
{
|
||||||
|
OnToggleBoxCallback.InvokeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
103
Pages/Nodes/FluviusUploadNode.razor
Normal file
103
Pages/Nodes/FluviusUploadNode.razor
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
@using Radzen
|
||||||
|
@using Radzen.Blazor
|
||||||
|
@using BattSim.Models
|
||||||
|
@using BattSim.Services
|
||||||
|
|
||||||
|
<div class="pipeline-box @GetPipelineStepClasses(StepData)">
|
||||||
|
<div class="box-header" @onclick="ToggleBox">
|
||||||
|
<div class="step-number">1</div>
|
||||||
|
<div class="step-title">Fluvius Data</div>
|
||||||
|
<div class="status-indicator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-inner">
|
||||||
|
<p class="box-description">Upload je Fluvius kwartierdata om te beginnen</p>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="file-input-wrapper">
|
||||||
|
<InputFile type="file" id="fileUpload" accept=".csv" onchange="async (e) => await OnFileUploaded(e)" />
|
||||||
|
<label for="fileUpload" class="file-input-label">
|
||||||
|
@if (StepData.isProcessing)
|
||||||
|
{
|
||||||
|
<div class="loading-indicator">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<span style="margin-left: 10px;">Data aan het inladen...</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@if (FluviusDataRaw.Length == 0)
|
||||||
|
{
|
||||||
|
<text>Kies CSV bestand</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<text>OK @FluviusDataRaw.Length kwartieren geladen</text>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</label>
|
||||||
|
@if (FluviusDataRaw.Length > 0 && !StepData.isProcessing)
|
||||||
|
{
|
||||||
|
<p class="file-name">@UploadedFileName</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (StepData.Completed)
|
||||||
|
{
|
||||||
|
<div class="success-message">
|
||||||
|
Data succesvol ingeladen en verwerkt!
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (UploadError != null)
|
||||||
|
{
|
||||||
|
<div class="error-message">
|
||||||
|
@UploadError
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="info-text">
|
||||||
|
<p><strong>Tip:</strong> Download een volledig jaar aan kwartierdata voor de meest accurate resultaten.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public EnergyData[] FluviusDataRaw { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public PipelineStep StepData { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string UploadedFileName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string? UploadError { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<InputFileChangeEventArgs> OnFileUploadedCallback { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback OnToggleBoxCallback { get; set; }
|
||||||
|
|
||||||
|
private string GetPipelineStepClasses(PipelineStep step)
|
||||||
|
{
|
||||||
|
var classes = new List<string>();
|
||||||
|
if (step.Expanded) classes.Add("expanded");
|
||||||
|
if (step.Completed) classes.Add("completed");
|
||||||
|
if (step.isProcessing) classes.Add("running");
|
||||||
|
return string.Join(" ", classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnFileUploaded(InputFileChangeEventArgs e)
|
||||||
|
{
|
||||||
|
await OnFileUploadedCallback.InvokeAsync(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleBox()
|
||||||
|
{
|
||||||
|
OnToggleBoxCallback.InvokeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
**Watt if?** is a simulation tool designed to help Flemish homeowners evaluate the impact of energy improvements such as batteries, heat pumps, solar panels, and air conditioning on their **energy bills** and **carbon emissions**. Currently, Wattif works exclusively with data from **Fluvius**, the Flemish energy distribution company.
|
**Watt if?** is a simulation tool designed to help Flemish homeowners evaluate the impact of energy improvements such as batteries, heat pumps, solar panels, and air conditioning on their **energy bills** and **carbon emissions**. Currently, Wattif works exclusively with data from **Fluvius**, the Flemish energy distribution company.
|
||||||
|
|
||||||
You can find it at : [wattif.douwco.be](https://wattif.douwco.be/)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
wattif:
|
web:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
ports:
|
ports:
|
||||||
- "5000:80"
|
- "5000:5000"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
|
||||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf
|
|
||||||
|
|||||||
18
nginx.conf
18
nginx.conf
@@ -1,18 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
include /etc/nginx/mime.types;
|
|
||||||
types {
|
|
||||||
application/wasm wasm;
|
|
||||||
application/manifest+json webmanifest;
|
|
||||||
application/octet-stream dll pdb webcil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user