Dans la foulée, on peut supprimer l'encodage base64 vu que le stockage des images utilisera des tags <img src=url/> plutôt que des base64
LX2ZVNAYX2FRCEIJ6XBCBILB3O7YT52AP56QDWCCGLATFCEB7KHQC
632SZPUQC6KX3CDZLYWV22OSFIOG66CJ7PONRNTXYTFLQX3JFFXQC
5FNNIOUNDS5SG75A4ZR6VY7DS6WU2GNNCOSSHX34RFTRB7LJ3D2QC
PI6D6Y5EHSXHSILBYHKRKXWXZ6H6JOR5ZTP44CXBW4LUUIREY7OQC
RLLNOBJM5LB446HWBUNDLNMG35PTUFRBRRVNHMFTX7CMQC4JSXUQC
7MCTB5G2W6IB7JQXGO2HCMRHBUYIOKXI4MBODKLAJIK66EMLC5ZQC
P7KZ25R4CNS4PZITRWPBVXRFSLY5TKCOFO6U3SDKX45ITNVU773QC
C56DYYC72SFNR6BSKCOVDY2QW6XNP66EUGGKCBROTCFJVQDB4FUAC
QHVOZBISIQ3E3P5F6GGKWVDSXC4OA23HCIH2OMXEVGXKSF5W4APAC
3ZF3ND2IS3EKQ5LPGCAN5P3N7X4W5NFIEM3KLO4RISONNSLILT4QC
XTPX4PYLPGJAEK52LYOYHLVRCL7N5FFAEY4E3OL23LOWLYPLCILQC
KNH5OGCTL3YCOQQPA32M76SGWHGHAPQRIZ7S2CBC5DGIEV32SOYQC
YLOKW6ERT2XEQG4T6GBPSBCQWVIHWAKBESCXJBKMQBSDXV3C2ACQC
UR5RFMWCD455OQZMTDQ2MSVJUPBNEUQHOBJ2UJBDWVHRJ4SOA5NAC
const base64abc = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-", "_"
];
const base64codes = [
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 62, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 63,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
];
function getBase64Code(charCode: number): number {
if (charCode >= base64codes.length) {
throw new Error("Unable to parse base64 string.");
}
const code = base64codes[charCode];
if (code === 255) {
throw new Error("Unable to parse base64 string.");
}
return code;
}
export function bytesToBase64(bytes: Uint8Array): string {
let result = '', i, l = bytes.length;
for (i = 2; i < l; i += 3) {
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
result += base64abc[((bytes[i - 1] & 0x0F) << 2) | (bytes[i] >> 6)];
result += base64abc[bytes[i] & 0x3F];
}
if (i === l + 1) { // 1 octet yet to write
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[(bytes[i - 2] & 0x03) << 4];
result += "==";
}
if (i === l) { // 2 octets yet to write
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
result += base64abc[(bytes[i - 1] & 0x0F) << 2];
result += "=";
}
return result;
}
export function base64ToBytes(str: string): Uint8Array {
if (str.length % 4 !== 0) {
throw new Error("Unable to parse base64 string.");
}
const index = str.indexOf("=");
if (index !== -1 && index < str.length - 2) {
throw new Error("Unable to parse base64 string.");
}
let missingOctets = str.endsWith("==") ? 2 : str.endsWith("=") ? 1 : 0,
n = str.length,
result = new Uint8Array(3 * (n / 4)),
buffer;
for (let i = 0, j = 0; i < n; i += 4, j += 3) {
buffer =
getBase64Code(str.charCodeAt(i)) << 18 |
getBase64Code(str.charCodeAt(i + 1)) << 12 |
getBase64Code(str.charCodeAt(i + 2)) << 6 |
getBase64Code(str.charCodeAt(i + 3));
result[j] = buffer >> 16;
result[j + 1] = (buffer >> 8) & 0xFF;
result[j + 2] = buffer & 0xFF;
}
return result.subarray(0, result.length - missingOctets);
}
export function base64encode(str: Uint8Array): string {
return bytesToBase64(str);
}
export function base64decode(str: string, decoder = new TextDecoder()): Uint8Array {
return base64ToBytes(str)
}
let resp = await fetch(
`https://${srv}/api/grain`,
{ method: "POST",
body: f
})
const srv = import.meta.env.MODE == 'production' ? 'https://x.coturnix.fr' : 'http://localhost:5173'
const resp = await fetch(
`${srv}/api/grain/prms`, {
method: "POST",
body: JSON.stringify(prms)
}
)
}
let t = min_t;
for(let semaine of r) {
let b = new DataView(base64decode(semaine).buffer)
let length = b.getUint32(0, true)
for(let i = 8; i < 8 + 8 * length; i+= 8) {
const ti = b.getUint32(i, true)
const vi = b.getUint32(i+4, true)
for(let tt = t + 1800; tt < ti; tt += 1800) {
d.t.push(tt)
d.y.push(0)
}
t = ti
d.t.push(ti)
d.y.push(vi / 1000)
}
<table class="table">
<thead>
<tr><th></th><th>Profil</th><th>Consommation annuelle</th></tr>
</thead>
<tbody>
{#each pr as pr, i}
<tr>
<td class="px-3">Profil</td>
<td>
<select class="form-select" bind:value={pr.type} on:change={(_ev) => updatePlot(puissance)}>
{#each Object.entries(profils) as [p, _]}
<option value={p}>{p}</option>
{/each}
</select>
</td>
<td>
<input type="number" class="form-control" placeholder="Consommation Annuelle (kWh)" min="0" step="500" bind:value={pr.conso} on:change={(_ev) => updatePlot(puissance)}/>
</td>
<td style="width:1%;white-space:nowrap;text-align:center">
<button class="btn btn-link" on:click={(_ev) => delProfil(i)}><i class="bi bi-x-circle text-primary"/></button>
</td>
</tr>
{/each}
</table>
<table class="table">
<thead><tr><th></th><th>Profil</th><th>Consommation annuelle</th><th></th></tr></thead>
<tbody>
{#each pr as pr, i}
<tr>
<td class="px-3 align-middle">Profil</td>
<td>
<select class="form-select" bind:value={pr.type} on:change={(_ev) => updatePlot(puissance)}>
{#each Object.entries(profils) as [p, _]}
<option value={p}>{p}</option>
{/each}
</select>
</td>
<td>
<input type="number" class="form-control" placeholder="Consommation Annuelle (kWh)" min="0" step="500" bind:value={pr.conso} on:change={(_ev) => updatePlot(puissance)}/>
</td>
<td style="width:1%;white-space:nowrap;text-align:center">
<button class="btn btn-link" on:click={(_ev) => delProfil(i)}><i class="bi bi-x-circle text-primary"/></button>
</td>
</tr>
{/each}
</tbody>
</table>
<div class="row">
<div class="col px-3" style="width: 25%">Liste de PRM, séparés par des virgules
<button type="button" class="btn btn-link align-baseline p-0 ms-2" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-content="Un PRM, aussi appelé PDL, est l'identifiant d'un compteur électrique. On peut le trouver sur les factures d'électricité, ou en appuyant sur le bouton + du compteur Linky."><i class="bi bi-question-circle"></i></button>
</div>
<div class="col-auto my-3">
<input class="form-control" form="pdl" id="prms" name="prms" bind:value={prms}/>
<h2 class="my-5">Relevés de compteurs</h2>
{#if prms.length}
<table class="table">
<thead><tr><th>PRM<button type="button" class="btn btn-link align-baseline p-0 ms-2" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-content="Un PRM, aussi appelé PDL, est l'identifiant d'un compteur électrique. On peut le trouver sur les factures d'électricité, ou en appuyant sur le bouton + du compteur Linky."><i class="bi bi-question-circle"></i></button></th><th>Prénom</th><th>Nom</th><th></th></tr></thead>
<tbody>
{#each prms as prm, i}
<tr>
<td><input class="form-control" bind:value={prm.prm} type="number" /></td>
<td><input class="form-control" bind:value={prm.nom}/></td>
<td><input class="form-control" bind:value={prm.prenom}/></td>
<td style="width:1%;white-space:nowrap;text-align:center">
<button class="btn btn-link" on:click={(ev) => delPrm(ev, i)}><i class="bi bi-x-circle text-primary"/></button>
</td>
</tr>
{/each}
</tbody>
</table>
<div class="my-3 form-check">
<input class="form-check-input" type="checkbox" id="consentement" bind:checked={consentement}>
<label class="form-check-label" for="consentement">Ces abonnés consentent à ce que j'accède à leurs données</label>
<div class="col d-flex align-items-center">
<button class="btn btn-primary" form="pdl">Ok</button>
{#if prm_loading}
<div class="spinner-border text-primary ms-3"></div>
{/if}
<div class="d-flex justify-content-center">
<button class="mx-2 btn btn-primary" on:click={addPrm}><i class="bi bi-plus-circle"/> Ajouter un compteur</button>
{#if prms.length}
<button disabled={!consentement} class="mx-2 btn btn-primary" form="pdl">Mettre à jour</button>
<div class="mx-2 spinner-border text-primary" class:d-none={!prm_loading}></div>
<div class="my-5 text-center">
<button class="btn btn-primary" on:click={addProfil}><i class="bi bi-plus-circle"/> Ajouter un profil</button>
</div>
<h2 class="mt-5">Simulation</h2>
import type { Grain } from '../../../helpers'
import type { Modal, Offcanvas } from 'bootstrap'
import { email } from '../../../stores'
import type { Grain } from '../../../helpers'
import * as H from '../../../helpers'
import type { Modal, Offcanvas } from 'bootstrap'
import { email } from '../../../stores'
export let data: {
name: string,
porteur: string,
adresse: string,
tel: string,
id: string,
img?: string,
simulation: Grain.SimulationCommunaute,
guest?: boolean,
prms: string,
email?: string,
}
type Prm = { prm: number | null, nom: string, prenom: string }
export let data: {
nom: string,
porteur: string,
adresse: string,
tel: string,
id: string,
img?: string,
simulation: Grain.SimulationCommunaute,
guest?: boolean,
prms: Prm[],
email?: string,
}
$: primeInvestissement =
(p.puissance <= 36) ? (0.21 * 1000 * p.puissance) :
(p.puissance <= 100) ? (0.11 * 1000 * p.puissance) :
0
$: primeInvestissement =
(p.puissance <= 36) ? (0.21 * 1000 * p.puissance) :
(p.puissance <= 100) ? (0.11 * 1000 * p.puissance) :
0
$: tarifLocal = (i: number) => p.prix * Math.pow(1+p.inflation/100, i)
$: tarifAllo = (i: number) => p.coutElec * Math.pow(1+p.inflationElec/100, i)
$: tarifLocal = (i: number) => H.tarifLocal(p, i)
$: tarifAllo = (i: number) => p.coutElec * Math.pow(1+p.inflationElec/100, i)
<Simulation prmsFeature={prmsFeature} active={simuActive} bind:prms={data.prms} puissance={p.puissance} bind:prodWeekly={prodWeekly} bind:maxProd={maxProd} bind:pvgis_loading={pvgis_loading} />
<Simulation prmsFeature={prmsFeature} bind:prms={data.prms} puissance={p.puissance} bind:prodWeekly={prodWeekly} bind:maxProd={maxProd} bind:pvgis_loading={pvgis_loading} />
import { error, redirect } from '@sveltejs/kit';
import type { PageServerLoad, Actions, RouteParams } from './$types';
import { userForm, SimulationCommunaute, Grain, save, del } from '../../../helpers';
import { base } from '$app/paths'
import { base64encode } from '../../../base64';
import { error } from '@sveltejs/kit';
import type { PageServerLoad, Actions } from './$types';
import { userForm, Grain, save, del } from '../../../helpers';
<div class="pb-3 mb-3 border-bottom border-4 border-primary fs-1 fw-bold bg-cc fs-1 fw-bold">+ {donnees.economies} k€</div>
<div class="pb-3 mb-3 border-bottom border-4 border-primary fs-1 fw-bold bg-cc fs-1 fw-bold">+{Math.round(donnees.economies / 1000)}k€</div>
await userForm(platform.env, id_, {
DelProjet: {
id: params['id'],
}
})
throw redirect(302, `${base}`)
if(params.id) {
await userForm(platform.env, id_, {
DelProjet: {
id: params['id'],
}
})
throw redirect(302, `${base}`)
}
}
export function coutElec(p: Grain.SimulationCommunaute, N: number) {
let cout = 0
for (let i = 0; i < N; i++) {
cout += p.coutElec * Math.pow(1 + p.inflationElec / 100, i) * consoMoyenneFoyer(p, i)
}
return cout
}
export function coutElecAuto(p: Grain.SimulationCommunaute, N: number) {
let cout = 0
for (let i = 0; i < N; i++) {
cout += tarifLocal(p, i) * consoMoyenneFoyer(p, i) * p.autoprod / 100
+ p.coutElec * Math.pow(1 + p.inflationElec / 100, i) * consoMoyenneFoyer(p, i) * (100 - p.autoprod) / 100
}
return cout
}
export function tarifLocal(p: Grain.SimulationCommunaute, i: number) {
return p.prix * Math.pow(1+p.inflation/100, i)
}
export function productionAnnuelle(p: Grain.SimulationCommunaute, i: number){
return p.productible * p.puissance * Math.pow(1-(p.degradation)/100, i)
export function consoMoyenneFoyer(p: Grain.SimulationCommunaute, i: number) {
return Math.min(p.consoMoyenneFoyer, productionAnnuelle(p, i)/p.autoprod)
}