Skip to content

Commit

Permalink
update route API types and fix timeline (commaai#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
incognitojam authored Jun 13, 2024
1 parent 4d9723a commit 034438c
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 87 deletions.
9 changes: 3 additions & 6 deletions src/api/derived.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Route } from '~/types'
import { getRouteDuration } from '~/utils/date'

export interface GPSPathPoint {
t: number
Expand Down Expand Up @@ -106,9 +107,7 @@ const generateTimelineEvents = (
route: Route,
events: DriveEvent[],
): TimelineEvent[] => {
const routeDuration =
route.segment_end_times[route.segment_end_times.length - 1] -
route.segment_start_times[0]
const routeDuration = getRouteDuration(route)?.asMilliseconds() ?? 0

// sort events by timestamp
events.sort((a, b) => {
Expand Down Expand Up @@ -218,9 +217,7 @@ const generateTimelineStatistics = (
return {
engagedDuration,
userFlags,
duration:
route.segment_end_times[route.segment_end_times.length - 1] -
route.segment_start_times[0],
duration: getRouteDuration(route)?.asMilliseconds() ?? 0,
}
}

Expand Down
27 changes: 6 additions & 21 deletions src/api/route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { fetcher } from '.'
import { BASE_URL } from './config'
import type { Device, Route } from '~/types'
import type { Device, Route, RouteShareSignature } from '~/types'

export class RouteName {
// dongle ID date str
// 0123456789abcdef|2023-02-15--15-25-00

static readonly regex =
/^([0-9a-f]{16})\|(\d{4}-\d{2}-\d{2}--\d{2}-\d{2}-\d{2})$/
static readonly regex = /^([0-9a-f]{16})\|(\d{4}-\d{2}-\d{2}--\d{2}-\d{2}-\d{2})$/
static readonly regexGroup = {
dongleId: 1,
dateStr: 2,
Expand All @@ -18,10 +17,7 @@ export class RouteName {
if (!match) {
return null
}
return new RouteName(
match[RouteName.regexGroup.dongleId],
match[RouteName.regexGroup.dateStr],
)
return new RouteName(match[RouteName.regexGroup.dongleId], match[RouteName.regexGroup.dateStr])
}

readonly dongleId: Device['dongle_id']
Expand All @@ -40,27 +36,16 @@ export class RouteName {
export const getRoute = (routeName: Route['fullname']): Promise<Route> =>
fetcher<Route>(`/v1/route/${routeName}/`)

interface RouteShareSignature extends Record<string, string> {
exp: NonNullable<Route['share_exp']>
sig: NonNullable<Route['share_sig']>
}

export const getRouteShareSignature = (
routeName: string,
): Promise<RouteShareSignature> =>
export const getRouteShareSignature = (routeName: string): Promise<RouteShareSignature> =>
fetcher(`/v1/route/${routeName}/share_signature`)

export const createQCameraStreamUrl = (
routeName: Route['fullname'],
signature: RouteShareSignature,
): string =>
`${BASE_URL}/v1/route/${routeName}/qcamera.m3u8?${new URLSearchParams(
signature,
).toString()}`
`${BASE_URL}/v1/route/${routeName}/qcamera.m3u8?${new URLSearchParams(signature).toString()}`

export const getQCameraStreamUrl = (
routeName: Route['fullname'],
): Promise<string> =>
export const getQCameraStreamUrl = (routeName: Route['fullname']): Promise<string> =>
getRouteShareSignature(routeName).then((signature) =>
createQCameraStreamUrl(routeName, signature),
)
14 changes: 5 additions & 9 deletions src/components/RouteCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,14 @@ import Icon from '~/components/material/Icon'
import RouteStaticMap from '~/components/RouteStaticMap'
import RouteStatistics from '~/components/RouteStatistics'

import type { Route } from '~/types'
import type { RouteSegments } from '~/types'

const RouteHeader = (props: { route: Route }) => {
const RouteHeader = (props: { route: RouteSegments }) => {
const startTime = () => dayjs(props.route.segment_start_times[0])
const endTime = () =>
dayjs(
props.route.segment_end_times[props.route.segment_end_times.length - 1],
)
const endTime = () => dayjs(props.route.segment_end_times.at(-1))

const headline = () => startTime().format('ddd, MMM D, YYYY')
const subhead = () =>
`${startTime().format('h:mm A')} to ${endTime().format('h:mm A')}`
const subhead = () => `${startTime().format('h:mm A')} to ${endTime().format('h:mm A')}`

return (
<CardHeader
Expand All @@ -34,7 +30,7 @@ const RouteHeader = (props: { route: Route }) => {
}

interface RouteCardProps {
route: Route
route: RouteSegments
}

const RouteCard: VoidComponent<RouteCardProps> = (props) => {
Expand Down
14 changes: 4 additions & 10 deletions src/components/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ import clsx from 'clsx'
import { TimelineEvent, getTimelineEvents } from '~/api/derived'
import { getRoute } from '~/api/route'
import type { Route } from '~/types'
import { getRouteDuration } from '~/utils/date'

function renderTimelineEvents(
route: Route | undefined,
events: TimelineEvent[],
) {
if (!route) return null

const duration =
route.segment_end_times[route.segment_end_times.length - 1] -
route.segment_start_times[0]

const duration = getRouteDuration(route)?.asMilliseconds() ?? 0
return (
<For each={events}>
{(event) => {
Expand Down Expand Up @@ -99,12 +97,8 @@ function renderMarker(route: Route | undefined, seekTime: number | undefined) {
if (!route) return null
if (seekTime === undefined) return null

const duration =
route.segment_end_times[route.segment_end_times.length - 1] -
route.segment_start_times[0]

const offsetPct = (seekTime / (duration / 1000)) * 100

const duration = getRouteDuration(route)?.asSeconds() ?? 0
const offsetPct = (seekTime / duration) * 100
return (
<div
class="absolute top-0 z-10 h-full"
Expand Down
17 changes: 7 additions & 10 deletions src/pages/dashboard/components/RouteList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import type { VoidComponent } from 'solid-js'
import clsx from 'clsx'

import type { Route } from '~/types'
import type { RouteSegments } from '~/types'

import RouteCard from '~/components/RouteCard'
import { fetcher } from '~/api'
Expand All @@ -22,26 +22,23 @@ type RouteListProps = {
dongleId: string
}

const pages: Promise<Route[]>[] = []
const pages: Promise<RouteSegments[]>[] = []

const RouteList: VoidComponent<RouteListProps> = (props) => {
const endpoint = () =>
`/v1/devices/${props.dongleId}/routes_segments?limit=${PAGE_SIZE}`
const getKey = (previousPageData?: Route[]): string | undefined => {
const endpoint = () => `/v1/devices/${props.dongleId}/routes_segments?limit=${PAGE_SIZE}`
const getKey = (previousPageData?: RouteSegments[]): string | undefined => {
if (!previousPageData) return endpoint()
if (previousPageData.length === 0) return undefined
const lastRoute = previousPageData[previousPageData.length - 1]
const lastSegmentEndTime =
lastRoute.segment_start_times[lastRoute.segment_start_times.length - 1]
const lastSegmentEndTime = previousPageData.at(-1)!.segment_start_times.at(-1)!
return `${endpoint()}&end=${lastSegmentEndTime - 1}`
}
const getPage = (page: number): Promise<Route[]> => {
const getPage = (page: number): Promise<RouteSegments[]> => {
if (!pages[page]) {
// eslint-disable-next-line no-async-promise-executor
pages[page] = new Promise(async (resolve) => {
const previousPageData = page > 0 ? await getPage(page - 1) : undefined
const key = getKey(previousPageData)
resolve(key ? fetcher<Route[]>(key) : [])
resolve(key ? fetcher<RouteSegments[]>(key) : [])
})
}
return pages[page]
Expand Down
65 changes: 43 additions & 22 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,35 +46,56 @@ export enum SegmentDataSource {
}

export interface Route {
fullname: string
can?: boolean
create_time: number
devicetype: number
dongle_id: string
user_id: string
end_lat?: number
end_lng?: number
end_time?: string
fullname: string
git_branch?: string
git_commit?: string
git_dirty?: boolean
git_remote?: string
hpgps?: boolean
init_logmonotime?: number
is_public: boolean
url: string
create_time: number
segment_numbers: number[]
segment_start_times: number[]
segment_end_times: number[]
length?: number
can?: boolean
hpgps?: boolean
radar?: boolean
devicetype: number
maxcamera: number
maxdcamera: number
maxecamera: number
maxlog: number
maxqcamera: number
maxqlog: number
procqlog: number
start_time: number
end_time?: number
passive?: boolean
version?: string
git_commit?: string
git_branch?: string
git_remote?: string
git_dirty?: boolean
platform?: string
proccamera: number
proclog: number
procqcamera: number
procqlog: number
radar?: boolean
start_time: string
url: string
user_id: string | null
version?: string
vin?: string
init_logmonotime?: number
share_exp?: string
share_sig?: string
}

export interface RouteShareSignature extends Record<string, string> {
exp: string
sig: string
}

export interface RouteSegments extends Route {
end_time_utc_millis: number
is_preserved: boolean
segment_end_times: number[]
segment_numbers: number[]
segment_start_times: number[]
share_exp: RouteShareSignature['exp']
share_sig: RouteShareSignature['sig']
start_time_utc_millis: number
}

export interface Clip {
Expand Down
19 changes: 10 additions & 9 deletions src/utils/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,17 @@ export const formatDuration = (minutes: number | undefined): string => {
return _formatDuration(duration)
}

export const getRouteDuration = (route: Route): Duration | undefined => {
if (!route || !route.end_time) return undefined
const startTime = dayjs(route.start_time)
const endTime = dayjs(route.end_time)
return dayjs.duration(endTime.diff(startTime))
}

export const formatRouteDuration = (route: Route | undefined): string => {
if (!route || !route.segment_start_times || !route.segment_end_times)
return ''

const startTime = dayjs(route.segment_start_times[0])
const endTime = dayjs(
route.segment_end_times[route.segment_end_times.length - 1],
)
const duration = dayjs.duration(endTime.diff(startTime))
return _formatDuration(duration)
if (!route) return ''
const duration = getRouteDuration(route)
return duration ? _formatDuration(duration) : ''
}

export const parseDateStr = (dateStr: string): Dayjs => {
Expand Down

0 comments on commit 034438c

Please sign in to comment.