diff --git a/packages/page-broker/src/Overview/CoreTable.tsx b/packages/page-broker/src/Overview/CoreTable.tsx index 2882ec6256..1fb9bba2ea 100644 --- a/packages/page-broker/src/Overview/CoreTable.tsx +++ b/packages/page-broker/src/Overview/CoreTable.tsx @@ -16,10 +16,9 @@ interface Props { core: number; workload?: CoreWorkloadType[], workplan?: CoreWorkplanType[], - timeslice: number, } -function CoreTable ({ api, core, timeslice, workload, workplan }: Props): React.ReactElement { +function CoreTable ({ api, core, workload, workplan }: Props): React.ReactElement { const { t } = useTranslation(); const headerRef = useRef<([React.ReactNode?, string?] | false)[]>([[t('core')]]); const header: [React.ReactNode?, string?, number?, (() => void)?][] = [ @@ -38,15 +37,14 @@ function CoreTable ({ api, core, timeslice, workload, workplan }: Props): React. isSplit={false} key={core} > - {workload?.map((v) => ( - - ))} + + ); } diff --git a/packages/page-broker/src/Overview/CoresTables.tsx b/packages/page-broker/src/Overview/CoresTables.tsx index 4290b567dd..41517a43fe 100644 --- a/packages/page-broker/src/Overview/CoresTables.tsx +++ b/packages/page-broker/src/Overview/CoresTables.tsx @@ -2,44 +2,31 @@ // SPDX-License-Identifier: Apache-2.0 import type { ApiPromise } from '@polkadot/api'; -import type { CoreInfo, CoreWorkloadType, CoreWorkplanType } from '../types.js'; +import type { CoreInfo } from '../types.js'; -import React, { useMemo } from 'react'; +import React from 'react'; import CoreTable from './CoreTable.js'; interface Props { api: ApiPromise; - workloadInfos?: CoreWorkloadType[]; - workplanInfos?: CoreWorkplanType[]; - timeslice: number; + data: CoreInfo[]; } -function CoresTable ({ api, timeslice, workloadInfos, workplanInfos }: Props): React.ReactElement { - const coreArr: number[] = useMemo(() => workloadInfos ? [...workloadInfos.map((plan) => plan.core)] : [], [workloadInfos]); - const filteredList: CoreInfo[] = useMemo(() => coreArr.map((c) => ({ - core: c, - workload: workloadInfos?.filter((v) => v.core === c), - workplan: workplanInfos?.filter((v) => v.core === c) - })), [workloadInfos, workplanInfos, coreArr]); - +function CoresTable ({ api, data }: Props): React.ReactElement { return ( <> - { - filteredList.map((c) => { - return ( - - ); - } - ) - } + {data?.map((coreData) => { + return ( + + ); + })} ); } diff --git a/packages/page-broker/src/Overview/Filters.tsx b/packages/page-broker/src/Overview/Filters.tsx index c0c1dbb8fe..c088030952 100644 --- a/packages/page-broker/src/Overview/Filters.tsx +++ b/packages/page-broker/src/Overview/Filters.tsx @@ -1,13 +1,14 @@ // Copyright 2017-2024 @polkadot/app-broker authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { CoreInfo } from '../types.js'; + import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Dropdown, Input, styled } from '@polkadot/react-components'; import { useDebounce } from '@polkadot/react-hooks'; import { useTranslation } from '../translate.js'; -import { type CoreWorkloadType, type CoreWorkplanType } from '../types.js'; const StyledDiv = styled.div` @media (max-width: 768px) { @@ -16,31 +17,31 @@ const StyledDiv = styled.div` `; interface Props { - workLoad?: CoreWorkloadType[]; - onFilter: (data: CoreWorkloadType[]) => void + data: CoreInfo[]; + onFilter: (data: CoreInfo[]) => void } -const filterLoad = (parachainId: string, load: CoreWorkloadType[] | CoreWorkplanType[], workloadCoreSelected: number): CoreWorkloadType[] | CoreWorkplanType[] => { +const filterLoad = (parachainId: string, data: CoreInfo[], workloadCoreSelected: number): CoreInfo[] => { if (parachainId) { - return load?.filter(({ info }) => info.task === parachainId); + return data.filter(({ workload }) => !!workload?.filter(({ info }) => info.task === parachainId).length); } if (workloadCoreSelected === -1) { - return load; + return data; } - return load?.filter(({ core }) => core === workloadCoreSelected); + return data.filter((one) => one.core === workloadCoreSelected); }; -function Filters ({ onFilter, workLoad }: Props): React.ReactElement { +function Filters ({ data, onFilter }: Props): React.ReactElement { const [workloadCoreSelected, setWorkloadCoreSelected] = useState(-1); const [_parachainId, setParachainId] = useState(''); const coreArr: number[] = useMemo(() => - workLoad?.length - ? Array.from({ length: workLoad?.length || 0 }, (_, index) => index) + data?.length + ? Array.from({ length: data.length || 0 }, (_, index) => index) : [] - , [workLoad]); + , [data]); const { t } = useTranslation(); const parachainId = useDebounce(_parachainId); @@ -60,14 +61,14 @@ function Filters ({ onFilter, workLoad }: Props): React.ReactElement { ); useEffect(() => { - if (!workLoad) { + if (!data) { return; } - const filtered = filterLoad(parachainId, workLoad, workloadCoreSelected); + const filtered = filterLoad(parachainId, data, workloadCoreSelected); onFilter(filtered); - }, [workLoad, workloadCoreSelected, parachainId, onFilter]); + }, [data, workloadCoreSelected, parachainId, onFilter]); const onDropDownChange = useCallback((v: number) => { setWorkloadCoreSelected(v); diff --git a/packages/page-broker/src/Overview/Summary.tsx b/packages/page-broker/src/Overview/Summary.tsx index 2b89985192..b3067c0e0a 100644 --- a/packages/page-broker/src/Overview/Summary.tsx +++ b/packages/page-broker/src/Overview/Summary.tsx @@ -2,19 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 import type { LinkOption } from '@polkadot/apps-config/endpoints/types'; -import type { CoreWorkloadType, statsType } from '../types.js'; +import type { statsType } from '../types.js'; import React from 'react'; import { CardSummary, styled, SummaryBox, UsageBar } from '@polkadot/react-components'; import { defaultHighlight } from '@polkadot/react-components/styles'; -import { useApi, useBrokerStatus } from '@polkadot/react-hooks'; +import { useApi, useBrokerSalesInfo, useBrokerStatus } from '@polkadot/react-hooks'; +import { type CoreWorkload } from '@polkadot/react-hooks/types'; +import { formatBalance } from '@polkadot/util'; import { useTranslation } from '../translate.js'; import { getStats } from '../utils.js'; -import Cores from './Summary/Cores.js'; import RegionLength from './Summary/RegionLength.js'; -import RenewalPrice from './Summary/RenewalPrice.js'; import Timeslice from './Summary/Timeslice.js'; import TimeslicePeriod from './Summary/TimeslicePeriod.js'; @@ -34,37 +34,47 @@ const StyledSection = styled.section` `; interface Props { + coreCount?: string apiEndpoint?: LinkOption | null; - workloadInfos?: CoreWorkloadType[] | CoreWorkloadType + workloadInfos?: CoreWorkload[] } -function Summary ({ workloadInfos }: Props): React.ReactElement { +function Summary ({ coreCount, workloadInfos }: Props): React.ReactElement { const { t } = useTranslation(); const { api, apiEndpoint } = useApi(); const totalCores = useBrokerStatus('coreCount'); const uiHighlight = apiEndpoint?.ui.color || defaultHighlight; const { idles, pools, tasks }: statsType = React.useMemo(() => getStats(totalCores, workloadInfos), [totalCores, workloadInfos]); + const salesInfo = useBrokerSalesInfo(); + return ( {api.query.broker && ( <> - + - - - - + - + - +
+ {formatBalance(salesInfo?.endPrice) || '-'} +
+
+ + {coreCount} + + +
+ {salesInfo?.coresSold} / {salesInfo?.coresOffered} +
@@ -73,8 +83,8 @@ function Summary ({ workloadInfos }: Props): React.ReactElement {
diff --git a/packages/page-broker/src/Overview/WorkInfoRow.tsx b/packages/page-broker/src/Overview/WorkInfoRow.tsx index 19a806520e..09f55ba882 100644 --- a/packages/page-broker/src/Overview/WorkInfoRow.tsx +++ b/packages/page-broker/src/Overview/WorkInfoRow.tsx @@ -39,8 +39,8 @@ function WorkInfoRow ({ data }: { data: InfoRow }): React.ReactElement { if (!data.task) { return ( <> - no task assigned - + no task + ); } @@ -60,7 +60,7 @@ function WorkInfoRow ({ data }: { data: InfoRow }): React.ReactElement { header='type' value={'Reservation'} /> - + ); } @@ -94,46 +94,53 @@ function WorkInfoRow ({ data }: { data: InfoRow }): React.ReactElement { header='type' value={'Legacy Lease'} /> + ); } default: { - return <> - - - - - - -
{'Owner'}
- {data.owner - ? ( - - ) + return ( + <> + + + + + + + +
{'Owner'}
+ {data.owner + ? ( + + ) - :

 

} -
; + :

 

} +
+ ); } } } diff --git a/packages/page-broker/src/Overview/Workload.tsx b/packages/page-broker/src/Overview/Workload.tsx index 889992cd46..028c84ee29 100644 --- a/packages/page-broker/src/Overview/Workload.tsx +++ b/packages/page-broker/src/Overview/Workload.tsx @@ -3,12 +3,14 @@ import type { ApiPromise } from '@polkadot/api'; import type { RegionInfo } from '@polkadot/react-hooks/types'; +import type { Option } from '@polkadot/types'; +import type { PalletBrokerStatusRecord } from '@polkadot/types/lookup'; import type { CoreWorkloadType, CoreWorkplanType, InfoRow } from '../types.js'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { ExpandButton } from '@polkadot/react-components'; -import { useRegions, useToggle } from '@polkadot/react-hooks'; +import { useApi, useBrokerSalesInfo, useCall, useRegions, useToggle } from '@polkadot/react-hooks'; import { formatRowInfo } from '../utils.js'; import WorkInfoRow from './WorkInfoRow.js'; @@ -16,63 +18,90 @@ import Workplan from './Workplan.js'; interface Props { api: ApiPromise; - value: CoreWorkloadType - timeslice: number; - workplan?: CoreWorkplanType[] | null + core: number; + workload: CoreWorkloadType[] | undefined + workplan?: CoreWorkplanType[] | undefined } -function Workload ({ api, timeslice, value: { core, info, lastBlock, type }, workplan }: Props): React.ReactElement { +function Workload ({ api, core, workload, workplan }: Props): React.ReactElement { + const { isApiReady } = useApi(); + const salesInfo = useBrokerSalesInfo(); + + const status = useCall>(isApiReady && api.query.broker?.status); const [isExpanded, toggleIsExpanded] = useToggle(false); - const [tableData, setTableData] = useState(); - const [currentRegion, setCurrentRegion] = useState(); + const [workloadData, setWorkloadData] = useState(); + const [workplanData, setWorkplanData] = useState(); + + const currentTimeSlice = useMemo(() => { + if (status?.isSome) { + return status.unwrap().lastCommittedTimeslice.toNumber(); + } + + return 0; + }, [status]); + const regionInfo = useRegions(api); + const region: RegionInfo | undefined = useMemo(() => regionInfo?.find((v) => v.core === core && v.start <= currentTimeSlice && v.end > currentTimeSlice), [regionInfo, core, currentTimeSlice]); useEffect(() => { - if (info) { - const region: RegionInfo | undefined = regionInfo?.find((v) => v.core === core && v.start <= timeslice && v.end > timeslice); - - setTableData(formatRowInfo(info, core, region, timeslice, type, lastBlock)); - setCurrentRegion(region); + if (!!workload?.length && !!salesInfo) { + setWorkloadData(formatRowInfo(workload, core, region, currentTimeSlice, salesInfo)); + } else { + return setWorkloadData([{ core }]); } - }, [info, regionInfo, core, timeslice, lastBlock, type]); + }, [workload, region, currentTimeSlice, core, salesInfo]); - const hasWorkplan = !!workplan?.length; + useEffect(() => { + if (!!workplan?.length && !!salesInfo) { + setWorkplanData(formatRowInfo(workplan, core, region, currentTimeSlice, salesInfo)); + } + } + , [workplan, region, currentTimeSlice, core, salesInfo]); + + const hasWorkplan = workplan?.length; return ( <> - {tableData && + {!!workloadData && - - -
Workplan ({workplan?.length})
- {hasWorkplan && - ( - - ) - } - {!hasWorkplan && 'none'} - + {workloadData.map((one) => { + return ( + + + +
Workplan {workplan?.length ? `(${workplan?.length})` : ''}
+ {!!hasWorkplan && + ( + + ) + } + {!hasWorkplan && 'none'} + +
+ ); + })} + } {isExpanded && <> workplans - + - {workplan?.map((workplanInfo) => ( + {workplanData?.map((workplanInfo) => ( ))} diff --git a/packages/page-broker/src/Overview/Workplan.tsx b/packages/page-broker/src/Overview/Workplan.tsx index e5a19f35e2..406be38fd0 100644 --- a/packages/page-broker/src/Overview/Workplan.tsx +++ b/packages/page-broker/src/Overview/Workplan.tsx @@ -2,29 +2,24 @@ // SPDX-License-Identifier: Apache-2.0 import type { RegionInfo } from '@polkadot/react-hooks/types'; -import type { CoreWorkplanType, InfoRow } from '../types.js'; +import type { InfoRow } from '../types.js'; -import React, { useMemo } from 'react'; +import React from 'react'; import { Spinner } from '@polkadot/react-components'; -import { formatRowInfo } from '../utils.js'; import WorkInfoRow from './WorkInfoRow.js'; interface Props { className?: string; - value: CoreWorkplanType; + workplanData: InfoRow; currentTimeSlice: number isExpanded: boolean region: RegionInfo | undefined } -function Workplan ({ currentTimeSlice, isExpanded, region, value: { core, info, lastBlock, type } }: Props): React.ReactElement { - const tableData: InfoRow = useMemo(() => { - return formatRowInfo(info, core, region, currentTimeSlice, type, lastBlock); - }, [info, core, region, currentTimeSlice, type, lastBlock]); - - if (!tableData) { +function Workplan ({ isExpanded, workplanData }: Props): React.ReactElement { + if (!workplanData) { return ( - {tableData && ( + {workplanData && ( - + )} diff --git a/packages/page-broker/src/Overview/index.tsx b/packages/page-broker/src/Overview/index.tsx index 97edf881a2..9506e2080e 100644 --- a/packages/page-broker/src/Overview/index.tsx +++ b/packages/page-broker/src/Overview/index.tsx @@ -1,16 +1,13 @@ // Copyright 2017-2024 @polkadot/app-broker authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { ApiPromise } from '@polkadot/api'; -import type { LinkOption } from '@polkadot/apps-config/endpoints/types'; import type { CoreWorkload, CoreWorkplan, LegacyLease, Reservation } from '@polkadot/react-hooks/types'; -import type { PalletBrokerStatusRecord } from '@polkadot/types/lookup'; +import type { CoreInfo } from '../types.js'; import React, { useEffect, useMemo, useState } from 'react'; -import { useApi, useBrokerLeases, useBrokerReservations, useCall, useWorkloadInfos, useWorkplanInfos } from '@polkadot/react-hooks'; +import { useApi, useBrokerLeases, useBrokerReservations, useBrokerStatus, useWorkloadInfos, useWorkplanInfos } from '@polkadot/react-hooks'; -import { type CoreWorkloadType, type CoreWorkplanType } from '../types.js'; import { createTaskMap, getOccupancyType } from '../utils.js'; import CoresTable from './CoresTables.js'; import Filters from './Filters.js'; @@ -18,85 +15,84 @@ import Summary from './Summary.js'; interface Props { className?: string; - apiEndpoint?: LinkOption | null; - api: ApiPromise; - isReady: boolean; } -const formatLoad = (data: CoreWorkplan[] | CoreWorkload[] | undefined, leaseMap: Record, reservationMap: Record): CoreWorkloadType[] | CoreWorkplanType[] => { - if (!data) { - return []; - } +type LeaseMapType = Record +type ReservationMapType = Record - return data.map((w: CoreWorkload | CoreWorkplan) => { - const result = { - ...w, - lastBlock: leaseMap[w.info.task as number]?.until || 0, - type: getOccupancyType(leaseMap[w.info.task as number], reservationMap[w.info.task as number]) +const formatDataObject = (one: CoreWorkplan | CoreWorkload, leaseMap: LeaseMapType, reservationMap: ReservationMapType) => ({ + ...one, + lastBlock: leaseMap[one?.info.task as number]?.until || 0, + maskBits: one.info.maskBits, + task: one.info.task, + type: getOccupancyType(leaseMap[one.info.task as number], reservationMap[one.info.task as number]) +}); + +const formatData = (coreCount: number, workplan: CoreWorkplan[], workload: CoreWorkload[], leaseMap: LeaseMapType, reservationMap: ReservationMapType): CoreInfo[] => { + return Array.from({ length: coreCount }, (_, coreNumber) => { + const processWorkload = (data: CoreWorkload[]) => data + .filter((load) => load.core === coreNumber) + .map((one) => (formatDataObject(one, leaseMap, reservationMap))); + + const processWorkplan = (data: CoreWorkplan[]) => data + .filter((load) => load.core === coreNumber) + .map((one) => ({ + ...formatDataObject(one, leaseMap, reservationMap), + timeslice: one.timeslice + })); + + const coreData = { + core: coreNumber, + workload: workload?.length ? processWorkload(workload) : [], + workplan: workplan?.length ? processWorkplan(workplan) : [] }; - if ('timeslice' in w) { - return result as CoreWorkplanType; - } else { - return result as CoreWorkloadType; - } + return coreData; }); }; -function Overview ({ api, apiEndpoint, className, isReady }: Props): React.ReactElement { - const [workLoad, setWorkLoad] = useState(); - const [workPlan, setWorkPlan] = useState(); - const [filtered, setFiltered] = useState(); - const { isApiReady } = useApi(); +function Overview ({ className }: Props): React.ReactElement { + const { api, apiEndpoint, isApiReady } = useApi(); + const [data, setData] = useState([]); + + const [filtered, setFiltered] = useState(); + const coreCount = useBrokerStatus('coreCount') || '-'; - const status = useCall(isReady && api.query.broker?.status); const workloadInfos: CoreWorkload[] | undefined = useWorkloadInfos(api, isApiReady); const workplanInfos: CoreWorkplan[] | undefined = useWorkplanInfos(api, isApiReady); const reservations: Reservation[] | undefined = useBrokerReservations(api, isApiReady); const leases: LegacyLease[] | undefined = useBrokerLeases(api, isApiReady); - const leaseMap = useMemo(() => leases ? createTaskMap(leases) : [], [leases]); - const reservationMap = useMemo(() => reservations ? createTaskMap(reservations) : [], [reservations]); - - const timesliceAsString = useMemo(() => { - if (!!status && !!status?.toHuman()) { - const timeslice = status?.toHuman().lastCommittedTimeslice?.toString(); - - return timeslice === undefined ? '' : timeslice.toString().split(',').join(''); - } - - return '0'; - }, [status]); - - useEffect(() => - setWorkPlan(formatLoad(workplanInfos, leaseMap, reservationMap)) - , [workplanInfos, leaseMap, reservationMap]); + const leaseMap: LeaseMapType = useMemo(() => leases ? createTaskMap(leases) : [], [leases]); + const reservationMap: ReservationMapType = useMemo(() => reservations ? createTaskMap(reservations) : [], [reservations]); useEffect(() => { - setWorkLoad(formatLoad(workloadInfos, leaseMap, reservationMap)); - }, [workloadInfos, leaseMap, reservationMap]); + !!workplanInfos && !!workloadInfos && !!coreCount && + setData(formatData(Number(coreCount), workplanInfos, workloadInfos, leaseMap, reservationMap)); + }, [workplanInfos, workloadInfos, leaseMap, reservationMap, coreCount]); return (
- {!!workPlan?.length && + {!!data?.length && (<> - + {!!filtered && ( + + )} ) } - {!workPlan?.length &&

No data currently available

} + {!data?.length &&

No data currently available

}
); } diff --git a/packages/page-broker/src/index.tsx b/packages/page-broker/src/index.tsx index 7b0c5fbdf1..477aa10715 100644 --- a/packages/page-broker/src/index.tsx +++ b/packages/page-broker/src/index.tsx @@ -6,7 +6,6 @@ import type { TabItem } from '@polkadot/react-components/types'; import React, { useRef } from 'react'; import { Tabs } from '@polkadot/react-components'; -import { useApi } from '@polkadot/react-hooks'; import Overview from './Overview/index.js'; import { useTranslation } from './translate.js'; @@ -29,7 +28,6 @@ function createItemsRef (t: (key: string, options?: { replace: Record { const { t } = useTranslation(); const itemsRef = useRef(createItemsRef(t)); - const { api, apiEndpoint, isApiReady } = useApi(); return (
@@ -37,11 +35,7 @@ function BrokerApp ({ basePath, className }: Props): React.ReactElement { basePath={basePath} items={itemsRef.current} /> - +
); } diff --git a/packages/page-broker/src/types.ts b/packages/page-broker/src/types.ts index 2c9c88eac1..69d25e21c6 100644 --- a/packages/page-broker/src/types.ts +++ b/packages/page-broker/src/types.ts @@ -1,11 +1,11 @@ // Copyright 2017-2024 @polkadot/app-broker authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { CoreWorkplan } from '@polkadot/react-hooks/types'; +import type { CoreWorkload, CoreWorkplan } from '@polkadot/react-hooks/types'; export interface InfoRow { - task: string | number, - maskBits: number, + task?: string | number, + maskBits?: number, core: number mask?: string start?: string | null, @@ -31,7 +31,7 @@ export interface statsType { export enum Occupancy { 'Reservation', 'Lease', - 'Rent' + 'Bulk Coretime' } export interface CoreWorkplanType extends CoreWorkplan { @@ -39,7 +39,7 @@ export interface CoreWorkplanType extends CoreWorkplan { type: Occupancy } -export interface CoreWorkloadType extends CoreWorkplan { +export interface CoreWorkloadType extends CoreWorkload { lastBlock: number, type: Occupancy } diff --git a/packages/page-broker/src/utils.ts b/packages/page-broker/src/utils.ts index 31be9243e9..90104c4afa 100644 --- a/packages/page-broker/src/utils.ts +++ b/packages/page-broker/src/utils.ts @@ -1,14 +1,14 @@ // Copyright 2017-2024 @polkadot/app-broker authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { CoreWorkloadInfo, LegacyLease, RegionInfo, Reservation } from '@polkadot/react-hooks/types'; -import type { CoreWorkloadType, InfoRow } from './types.js'; +import type { CoreWorkload, LegacyLease, RegionInfo, Reservation } from '@polkadot/react-hooks/types'; +import type { CoreWorkloadType, CoreWorkplanType, InfoRow } from './types.js'; import { BN } from '@polkadot/util'; import { Occupancy } from './types.js'; -const CoreTimeConsts = { +export const CoreTimeConsts = { BlockTime: 6000, BlocksPerTimeslice: 80, DefaultRegion: 5040 @@ -22,24 +22,36 @@ function formatDate (date: Date) { return `${day} ${month} ${year}`; } -export const estimateTime = (targetBlock: string, latestBlock: number, timestamp: number): string | null => { - if (!timestamp || !latestBlock || !targetBlock) { +/** + * blockTime = 6000 ms + * BlocksPerTimeslice = 80 + * Default Regoin = 5040 timeslices + * TargetBlock = TargetTimeslice * BlocksPerTimeslice + * Block Time Difference = |TargetBlock - latest Block| * blockTime + * + * Estimate timestamp = + * if targetBlock is before the latestBlock + * now minus block time difference + * else + * now plus block time difference + */ +export const estimateTime = (targetTimeslice: string | number, latestBlock: number): string | null => { + if (!latestBlock || !targetTimeslice) { console.error('Invalid input: one or more inputs are missing'); return null; } + const now = new Date().getTime(); + try { const blockTime = new BN(CoreTimeConsts.BlockTime); // Average block time in milliseconds (6 seconds) const timeSlice = new BN(CoreTimeConsts.BlocksPerTimeslice); - const targetBlockBN = new BN(targetBlock).mul(timeSlice); const latestBlockBN = new BN(latestBlock); - const timestampBN = new BN(timestamp); - const blockDifference = targetBlockBN.sub((latestBlockBN)).abs().mul(blockTime); - - const estTimestamp = targetBlockBN.lt(latestBlockBN) - ? timestampBN.sub(blockDifference) - : timestampBN.add(blockDifference); + const timestampBN = new BN(now); + const targetBlockBN = new BN(targetTimeslice).mul(timeSlice); + const blockTimeDifference = targetBlockBN.sub(latestBlockBN).mul(blockTime); + const estTimestamp = timestampBN.add(blockTimeDifference); return formatDate(new Date(estTimestamp.toNumber())); } catch (error) { @@ -49,43 +61,42 @@ export const estimateTime = (targetBlock: string, latestBlock: number, timestamp } }; -export function formatRowInfo (info: CoreWorkloadInfo, core: number, currentRegion: RegionInfo | undefined, timeslice: number, type: Occupancy, lastBlock: number, regionLength = CoreTimeConsts.DefaultRegion): InfoRow { - const item: InfoRow = { core, maskBits: info.maskBits, task: info.task }; - - if (currentRegion) { - const start = currentRegion?.start?.toString() ?? 0; - const end = currentRegion?.end?.toString() ?? 0; +export function formatRowInfo (data: CoreWorkloadType[] | CoreWorkplanType[], core: number, currentRegion: RegionInfo | undefined, timeslice: number, { regionBegin, regionEnd }: { regionBegin: number, regionEnd: number }, regionLength = CoreTimeConsts.DefaultRegion): InfoRow[] { + return data.map((one: CoreWorkloadType | CoreWorkplanType) => { + const item: InfoRow = { core, maskBits: one?.info?.maskBits, task: one?.info?.task, type: one?.type }; const blockNumber = timeslice * 80; - item.start = estimateTime(start, blockNumber, new Date().getTime()); - item.end = estimateTime(end, blockNumber, new Date().getTime()); - item.endBlock = Number(end) * 80; + item.type = one.type; + let end; let start = null; + + if (one.type === Occupancy.Lease) { + const period = Math.floor(one.lastBlock / regionLength); + + end = period * regionLength; + } else { + start = currentRegion?.start?.toString() ?? regionBegin; + end = currentRegion?.end?.toString() ?? regionEnd; + } + item.owner = currentRegion?.owner.toString(); - } + item.start = start ? estimateTime(start, blockNumber) : null; + item.end = end ? estimateTime(end, blockNumber) : null; + item.endBlock = end ? Number(end) * 80 : 0; - item.type = type; + if ('timeslice' in one && !start) { + start = estimateTime(one.timeslice, blockNumber) ?? null; + } - if (lastBlock) { - const blockNumber = timeslice * 80; - const period = Math.floor(lastBlock / regionLength); - const end = period * regionLength; - - item.start = ' - '; - item.end = estimateTime(end.toString(), blockNumber, new Date().getTime()); - item.endBlock = Number(end) * 80; - } - - return item; + return item; + }); } -export function getStats (totalCores: string | undefined, workloadInfos: CoreWorkloadType[] | CoreWorkloadType | undefined) { +export function getStats (totalCores: string | undefined, workloadInfos: CoreWorkload[] | undefined) { if (!totalCores || !workloadInfos) { return { idles: 0, pools: 0, tasks: 0 }; } - const sanitized: CoreWorkloadType[] = Array.isArray(workloadInfos) ? workloadInfos : [workloadInfos]; - - const { pools, tasks } = sanitized.reduce( + const { pools, tasks } = workloadInfos.reduce( (acc, { info }) => { if (info.isTask) { acc.tasks += 1; @@ -111,5 +122,5 @@ export const createTaskMap = (items: T[]): Record { - return reservation ? Occupancy.Reservation : lease ? Occupancy.Lease : Occupancy.Rent; + return reservation ? Occupancy.Reservation : lease ? Occupancy.Lease : Occupancy['Bulk Coretime']; }; diff --git a/packages/react-hooks/src/types.ts b/packages/react-hooks/src/types.ts index 54101c444b..ad387f2fa9 100644 --- a/packages/react-hooks/src/types.ts +++ b/packages/react-hooks/src/types.ts @@ -264,7 +264,8 @@ export interface RegionInfo { export interface Reservation { task: string - mask: number, + mask: string[], + maskBits: number } export interface LegacyLease { diff --git a/packages/react-hooks/src/useBrokerReservations.ts b/packages/react-hooks/src/useBrokerReservations.ts index 4c9afe6646..cd5e37e085 100644 --- a/packages/react-hooks/src/useBrokerReservations.ts +++ b/packages/react-hooks/src/useBrokerReservations.ts @@ -24,7 +24,8 @@ function useBrokerReservationsImpl (api: ApiPromise, ready: boolean): Reservatio setState( reservations.map((info: PalletBrokerScheduleItem[]) => { return { - mask: processHexMask(info[0]?.mask)?.length ?? 0, + mask: processHexMask(info[0]?.mask), + maskBits: processHexMask(info[0]?.mask)?.length ?? 0, task: info[0]?.assignment?.isTask ? info[0]?.assignment?.asTask.toString() : info[0]?.assignment?.isPool ? 'Pool' : '' }; }