From 2eaf8f719b19abfa65238127722be4341f379654 Mon Sep 17 00:00:00 2001 From: Sebastian Seedorf Date: Fri, 9 Sep 2022 21:30:34 +0200 Subject: [PATCH] Add button to show service statistics --- components/contexts/FactoryProvider/index.tsx | 6 + .../visualize/NodeOverview/NodeOverview.tsx | 17 +- components/visualize/PageDetails.tsx | 92 ++++++++- components/visualize/PageOverview.tsx | 4 +- public/modules/energy-saving.csv | 193 ++++++++++++++++++ public/modules/fast.csv | 193 ++++++++++++++++++ public/modules/few-beacons.csv | 193 ++++++++++++++++++ public/modules/mix.csv | 193 ++++++++++++++++++ public/modules/no-modules.csv | 193 ++++++++++++++++++ public/modules/optimized.csv | 193 ++++++++++++++++++ public/modules/prefer-productivity.csv | 193 ++++++++++++++++++ res/i18n/de.json | 1 + res/i18n/en.json | 1 + res/i18n/nl.json | 1 + scripts/fetch/index.ts | 2 +- src/database/groups.ts | 5 + src/hooks/useAsyncEffect.ts | 35 ++++ 17 files changed, 1496 insertions(+), 19 deletions(-) create mode 100644 public/modules/energy-saving.csv create mode 100644 public/modules/fast.csv create mode 100644 public/modules/few-beacons.csv create mode 100644 public/modules/mix.csv create mode 100644 public/modules/no-modules.csv create mode 100644 public/modules/optimized.csv create mode 100644 public/modules/prefer-productivity.csv create mode 100644 src/hooks/useAsyncEffect.ts diff --git a/components/contexts/FactoryProvider/index.tsx b/components/contexts/FactoryProvider/index.tsx index 5637237..987072b 100644 --- a/components/contexts/FactoryProvider/index.tsx +++ b/components/contexts/FactoryProvider/index.tsx @@ -42,6 +42,12 @@ export const FactoryProvider: FC = ({ children }) => { return factories }, [locale]) + //const zip = 'bY3BCsNACET/xlP30KY9NODHGFeKsNld1JR+fjeQHgrBQZiReWYKwmsa8wSjEHTgjjdYteIEi5RA+XQT9xRG1XuzSHsMGrI6Un5TZcmJ1XjTmG2+P0BftZmgWqupl4G9cOtd7DBjeyinhQzWlrciJyCfZjn0azMtRf4OUoRjfBmwk94X' + //const zip = 'bU/RasMwDPwbP8UPXTboMvwxiqwVgWMbSR7t39cbTkm3YWHEnU53imAQTr6/dydgFNRhDS9u4xxmt1KyQNcqpOpNIGstYv4bdmy0aaBEaFIyo0cWbGyLLK9vE8QvyEjxGa1SsK/ifPEt7+AKZiS3oUNsW0tgRX4Ax5dchAKWWkl87QxN3A1H23+1br6CTNrSZxO3ldgSaTiu0nmhUX+jHck9zI5NwxhhTfQ0+c/lR/r3qXV+1EefO4+0XQ3I8SG9Aw==' + //const zip = 'bVHBcgMhCP0bT+WQpD10Z/wYmqXG6SoO4mw+v+qmU206coAHPB+woqI9QX3vRlDJZnNN9myCj/ZiPmhTS/cklDOoYMyJRaHBxiuFbJNPBMrghEtcF1lOLw3qTlYWdDWN8asDVxTHsKPj2OPPrfh1ijGQ3oR3koOqhPTT+VQBWkRIO07R+UhQom/x65vxLrKQ9cIR0lYHq2qItsM3gdeyUbaNNl8WetgkcMRH+hGfBNO5G/0rcu76HXvEu1hHKLDfqtbnXBd//HNseaz4c4kp1dY4AsMuHnTTrYbSbw==' + //const unzip = inflateRaw(Buffer.from(zip, 'base64'), {to: 'string'}) + //console.log(unzip) + const findFactory = useMemo(() => { const detailsMap = Object.fromEntries( internationalizedFactories.map((detail: EnrichedEntity) => [detail.href, detail]) diff --git a/components/visualize/NodeOverview/NodeOverview.tsx b/components/visualize/NodeOverview/NodeOverview.tsx index 429b569..b963cc6 100644 --- a/components/visualize/NodeOverview/NodeOverview.tsx +++ b/components/visualize/NodeOverview/NodeOverview.tsx @@ -2,13 +2,10 @@ import { FC, HTMLProps } from 'react' import { EnrichedEntity } from '../../../src/types' import cx from 'classnames' import styles from './NodeOverview.module.css' -import Link from 'next/link' import { EntityIcon } from '../../home/EntityIcon/EntityIcon' import { GraphNode } from '../../../src/graph-untangle/types' -import { fixedEncodeURIComponent } from '../../../src/utils' -import { useRouter } from 'next/router' -import { GraphIcon } from '../../icons/GraphIcon' import { I18n } from '../../shared/I18n/I18n' +import { ButtonVisualize } from '../../shared/ButtonVisualize/ButtonVisualize' export type OverviewGraphNode = GraphNode<{ icons: (EnrichedEntity | string)[] @@ -19,20 +16,10 @@ interface Props extends HTMLProps { } export const NodeOverview: FC = ({ node, className, ...props }) => { - const { query } = useRouter() return (

- - - - - + {node.name}

{node.icons?.length ? ( diff --git a/components/visualize/PageDetails.tsx b/components/visualize/PageDetails.tsx index 2cbb0cf..cf15b7f 100644 --- a/components/visualize/PageDetails.tsx +++ b/components/visualize/PageDetails.tsx @@ -1,6 +1,6 @@ import { useRouter } from 'next/router' import { useGroups } from '../contexts/GroupProvider' -import { FC, useMemo } from 'react' +import { FC, useMemo, useState } from 'react' import { calculateInputs } from '../../src/calculateInputs' import { DetailGraphNode, NodeDetails } from './NodeDetails/NodeDetails' import { groupBy, isNonNullable, uniquify } from '../../src/utils' @@ -11,6 +11,8 @@ import { ProducingGraph } from './ProducingGraph/ProducingGraph' import { useFactories } from '../contexts/FactoryProvider' import { i18n, I18n } from '../shared/I18n/I18n' import { useIntl } from 'react-intl' +import { Button } from '../shared/Button/Button' +import { useAsyncEffect } from '../../src/hooks/useAsyncEffect' export const PageDetails: FC = () => { const intl = useIntl() @@ -61,6 +63,91 @@ export const PageDetails: FC = () => { ) }, [findFactory, group, intermediateFactories]) + const [savingModuleText, setSavingModuleText] = useState() + // eslint-disable-next-line react-hooks/exhaustive-deps + useAsyncEffect(async () => { + const res = await fetch('/factorio/modules/energy-saving.csv') + if (res.ok) setSavingModuleText(await res.text()) + }, []) + + const [optimalModuleText, setOptimalModuleText] = useState() + // eslint-disable-next-line react-hooks/exhaustive-deps + useAsyncEffect(async () => { + const res = await fetch('/factorio/modules/few-beacons.csv') + if (res.ok) setOptimalModuleText(await res.text()) + }, []) + + const statisticsUrl = useMemo(() => { + const toDashedKey = (n: string) => n.slice(1).toLowerCase().replace(/_/g, '-') + const checkIfPresent = (dashedKey: string) => { + const b = + !!optimalModuleText?.match(new RegExp(`${dashedKey};`)) && dashedKey !== 'rail-signal' + return b + } + const getModuleString = (dashedKey: string, powerSaving?: boolean) => { + const match = (powerSaving ? savingModuleText : optimalModuleText)?.match( + new RegExp(`${dashedKey};.*;(\\d+);(\\d+);(\\d+);(\\d+)`) + ) + if (match) { + const map = 'spe' + const [beacons, ...modules] = match.slice(1).map(x => +x) + let beaconModules = beacons == 1 ? 8 : beacons == 5 ? 12 : 0 + const maxIdx = modules.reduce((m, c, i, arr) => (c > arr[m] ? i : m), 0) + modules[maxIdx] -= beaconModules + if (modules[maxIdx] < 0) { + const newMaxIdx = modules.reduce((m, c, i, arr) => (c > arr[m] ? i : m), 0) + modules[newMaxIdx] += modules[maxIdx] + beaconModules += modules[maxIdx] + modules[maxIdx] = 0 + } + let res = [ + dashedKey, + ...[0, 1, 2].flatMap(idx => Array(modules[idx]).fill(`${map[idx]}3`)) + ].join(':') + if (beacons + modules[0] + modules[1] === 0 && modules[2] === 2) + res = res.replace(/e3/g, 'e2') + else if (beacons + modules[0] + modules[1] === 0 && modules[2] >= 3) + res = res.replace(/e3/g, 'ee') + // console.log(beacons, modules, res, dashedKey) + return res + (beaconModules ? `;${map[maxIdx]}3:${beaconModules}` : '') + } + return dashedKey + } + const params = { + data: '1-1-19', + rate: 's', + cp: '2', + min: '3', + belt: 'express-transport-belt', + items: [ + group?.malls + .map(toDashedKey) + .filter(checkIfPresent) + .map(n => `${n}:r:1`) ?? [], + group?.exports + .map(toDashedKey) + .filter(checkIfPresent) + .map(n => `${n}:r:45`) ?? [] + ].join(','), + ignore: inputFactories.map(toDashedKey).filter(checkIfPresent).join(','), + modules: [ + ...([...(group?.exports ?? []), ...intermediateFactories, ...inputFactories] + .map(toDashedKey) + .filter(checkIfPresent) + .map(n => getModuleString(n)) ?? []), + ...((group?.malls ?? []) + .map(toDashedKey) + .filter(checkIfPresent) + .map(n => getModuleString(n, true)) ?? []) + ].join(',') + } + const paramString = Object.entries(params) + .map(keyValue => keyValue.join('=')) + .join('&') + // console.log('params', 'https://kirkmcdonald.github.io/calc.html#' + paramString) + return 'https://kirkmcdonald.github.io/calc.html#' + paramString + }, [group, inputFactories, savingModuleText, optimalModuleText, intermediateFactories]) + return ( <> @@ -74,6 +161,9 @@ export const PageDetails: FC = () => {

+ { <> - <I18n id={'page.visualize.details.title'} /> + <I18n id={'page.visualize.overview.title'} />

- +

{ name = name.replace(/[.$]/g, '') const data = await getData(uuid) diff --git a/src/hooks/useAsyncEffect.ts b/src/hooks/useAsyncEffect.ts new file mode 100644 index 0000000..47b159c --- /dev/null +++ b/src/hooks/useAsyncEffect.ts @@ -0,0 +1,35 @@ +import { DependencyList, useEffect } from 'react' + +export const useAsyncEffect = ( + effect: (isMounted: () => boolean, signal: AbortSignal | undefined) => Promise, + inputs?: DependencyList, + onDestroy?: (result: T | undefined) => void +) => { + const controller = + typeof AbortController !== 'undefined' + ? new AbortController() + : { + signal: undefined, + abort() { + return + } + } + + useEffect(() => { + let result: T | undefined = undefined + let mounted = true + const maybePromise = effect(() => mounted, controller.signal) + + Promise.resolve(maybePromise).then(value => { + result = value + }) + + return () => { + mounted = false + controller.abort() + + onDestroy?.(result) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, inputs) +}