You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
6.5 KiB
TypeScript

import dayjs from 'dayjs'
import type {
DeviceTimelineRow,
HourlyStatusItem,
OverviewOption,
RunOverviewData,
RunOverviewQueryParams,
RunOverviewMetric,
RunStatus
} from './components/types'
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
export const GROUP_OPTIONS: OverviewOption[] = [
{ label: 'SMT一组', value: 'group-1' },
{ label: '成型二组', value: 'group-2' },
{ label: '总装三组', value: 'group-3' }
]
export const DEVICE_OPTIONS: OverviewOption[] = [
{ label: '模拟干燥设备04', value: 'device-01' },
{ label: '模拟干燥设备03', value: 'device-02' },
{ label: '模拟干燥设备02', value: 'device-03' },
{ label: '成型模拟设备05', value: 'device-04' },
{ label: '成型模拟设备04', value: 'device-05' },
{ label: '模拟干燥设备', value: 'device-06' },
{ label: '成型模拟设备02', value: 'device-07' },
{ label: '成型模拟设备01', value: 'device-08' },
{ label: '物流输送设备01', value: 'device-09' },
{ label: '包装工作站02', value: 'device-10' },
{ label: '包装工作站03', value: 'device-11' },
{ label: '拧紧设备01', value: 'device-12' },
{ label: '视觉检测设备01', value: 'device-13' }
]
const shiftTimeline = (segments: Array<{ status: RunStatus; startHour: number; endHour: number }>, offset: number) =>
segments.map((segment, index) => {
let nextStart = segment.startHour + offset
let nextEnd = segment.endHour + offset
if (index === 0 && nextStart < 0) nextStart = 0
if (index === segments.length - 1 && nextEnd > 24) nextEnd = 24
return {
...segment,
startHour: Math.max(0, Math.min(24, Number(nextStart.toFixed(2)))),
endHour: Math.max(0, Math.min(24, Number(nextEnd.toFixed(2))))
}
})
const BASE_SEGMENTS = [
{ status: 'running' as const, startHour: 0, endHour: 2.7 },
{ status: 'standby' as const, startHour: 2.7, endHour: 3.1 },
{ status: 'running' as const, startHour: 3.1, endHour: 6.6 },
{ status: 'standby' as const, startHour: 6.6, endHour: 7.15 },
{ status: 'running' as const, startHour: 7.15, endHour: 13.9 },
{ status: 'standby' as const, startHour: 13.9, endHour: 14.25 },
{ status: 'offline' as const, startHour: 14.25, endHour: 14.95 },
{ status: 'running' as const, startHour: 14.95, endHour: 17.45 },
{ status: 'fault' as const, startHour: 17.45, endHour: 18.15 },
{ status: 'standby' as const, startHour: 18.15, endHour: 18.8 },
{ status: 'running' as const, startHour: 18.8, endHour: 20.55 },
{ status: 'standby' as const, startHour: 20.55, endHour: 20.95 },
{ status: 'running' as const, startHour: 20.95, endHour: 22.25 },
{ status: 'offline' as const, startHour: 22.25, endHour: 23.05 },
{ status: 'running' as const, startHour: 23.05, endHour: 23.7 },
{ status: 'offline' as const, startHour: 23.7, endHour: 24 }
]
const TIMELINE_OFFSETS = [0, -0.3, 0.8, -0.5, 0.45, 1.05, -1.1, 0.25, -0.75, 0.6, -0.2, 1.2, -0.45]
export const buildDefaultQueryParams = (): RunOverviewQueryParams => {
const start = dayjs().startOf('day')
const end = dayjs().endOf('day')
return {
groupId: GROUP_OPTIONS[0].value,
deviceId: '',
quickRange: 'today',
timeRange: [start.format(DATE_TIME_FORMAT), end.format(DATE_TIME_FORMAT)]
}
}
const toTimelineRows = (): DeviceTimelineRow[] =>
DEVICE_OPTIONS.map((device, index) => ({
id: device.value,
name: device.label,
utilizationRate: [82.35, 76.12, 68.54, 75.63, 72.18, 91.24, 65.32, 78.44, 71.05, 83.67, 69.18, 87.42, 74.56][index],
segments: shiftTimeline(BASE_SEGMENTS, TIMELINE_OFFSETS[index] || 0)
}))
const toHourlyStatus = (summaryFactor: number): HourlyStatusItem[] => {
return Array.from({ length: 24 }, (_, hour) => {
const runningBase = 74 + Math.sin((hour / 24) * Math.PI * 3) * 14 + summaryFactor * 2
const standbyBase = 10 + Math.cos((hour / 24) * Math.PI * 4) * 7
const faultBase = 2 + Math.max(0, Math.sin((hour - 5) / 2.2)) * 5
const offlineBase = 100 - runningBase - standbyBase - faultBase
const running = Math.max(48, Math.min(88, Number(runningBase.toFixed(2))))
const standby = Math.max(4, Math.min(26, Number(standbyBase.toFixed(2))))
const fault = Math.max(1, Math.min(8, Number(faultBase.toFixed(2))))
const offline = Number(Math.max(4, 100 - running - standby - fault).toFixed(2))
return {
hour: `${String(hour).padStart(2, '0')}:00`,
running,
standby,
fault,
offline
}
})
}
const createMetrics = (summaryFactor: number): RunOverviewMetric[] => [
{
key: 'utilizationRate',
icon: 'ep:pie-chart',
value: Number((75.42 + summaryFactor * 1.2).toFixed(2)),
unit: '%',
change: 4.32
},
{
key: 'powerOnRate',
icon: 'ep:video-play',
value: Number((90.12 + summaryFactor * 0.7).toFixed(2)),
unit: '%',
change: 2.15
},
{
key: 'faultRate',
icon: 'ep:warning',
value: Number(Math.max(1.5, 3.21 - summaryFactor * 0.35).toFixed(2)),
unit: '%',
change: -1.03
},
{
key: 'standbyRate',
icon: 'ep:timer',
value: Number(Math.max(4, 6.67 - summaryFactor * 0.25).toFixed(2)),
unit: '%',
change: -1.14
}
]
const createSummary = (summaryFactor: number) => {
const running = Number((75.42 + summaryFactor * 1.2).toFixed(2))
const standby = Number(Math.max(4, 6.67 - summaryFactor * 0.25).toFixed(2))
const fault = Number(Math.max(1.5, 3.21 - summaryFactor * 0.35).toFixed(2))
const offline = Number((100 - running - standby - fault).toFixed(2))
return [
{ status: 'running' as const, percent: running, hours: Number(((24 * running) / 100).toFixed(2)) },
{ status: 'standby' as const, percent: standby, hours: Number(((24 * standby) / 100).toFixed(2)) },
{ status: 'fault' as const, percent: fault, hours: Number(((24 * fault) / 100).toFixed(2)) },
{ status: 'offline' as const, percent: offline, hours: Number(((24 * offline) / 100).toFixed(2)) }
]
}
export const buildRunOverviewData = (query: RunOverviewQueryParams): RunOverviewData => {
const summaryFactor =
query.quickRange === 'today'
? 0
: query.quickRange === 'yesterday'
? -0.8
: query.quickRange === 'last7Days'
? 1.1
: query.quickRange === 'last30Days'
? 0.45
: 0.15
const rows = toTimelineRows()
const filteredRows = query.deviceId ? rows.filter((row) => row.id === query.deviceId) : rows
return {
metrics: createMetrics(summaryFactor),
hourlyStatus: toHourlyStatus(summaryFactor),
summary: createSummary(summaryFactor),
timelineRows: filteredRows,
totalDevices: filteredRows.length
}
}