Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust drive definition from unix time range to openpilot route #486

Merged
merged 19 commits into from
Jun 18, 2024
Merged
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,4 @@ The current playback is tracked not by storing the current offset, but instead s
(Date.now() - state.startTime) * state.playSpeed + state.offset
```

With this central authority on current offset time, it becomes much easier to have each data source keep themselves in sync instead of trying to manage synchronizing all of them.


With this central authority on current offset time, it becomes much easier to have each data source keep themselves in sync instead of trying to manage synchronizing all of them.
4 changes: 2 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { CircularProgress, Grid } from '@material-ui/core';
import MyCommaAuth, { config as AuthConfig, storage as AuthStorage } from '@commaai/my-comma-auth';
import { athena as Athena, auth as Auth, billing as Billing, request as Request } from '@commaai/api';

import { getZoom } from './url';
import { getZoom, getSegmentRange } from './url';
import store, { history } from './store';

import ErrorFallback from './components/ErrorFallback';
Expand Down Expand Up @@ -122,7 +122,7 @@ class App extends Component {
return this.renderLoading();
}

const showLogin = !MyCommaAuth.isAuthenticated() && !getZoom(window.location.pathname);
const showLogin = !MyCommaAuth.isAuthenticated() && !getZoom(window.location.pathname) && !getSegmentRange(window.location.pathname);
let content = (
<Suspense fallback={this.renderLoading()}>
{ showLogin ? this.anonymousRoutes() : this.authRoutes() }
Expand Down
4 changes: 2 additions & 2 deletions src/__puppeteer__/drive.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import { configureViewport, goto } from './utils';

const DEMO_DEVICE_URL = '/1d3dc3e03047b0c7';
const DEMO_ROUTE_URL = '/1d3dc3e03047b0c7/1716484475499/1716485004466';
const ZOOMED_DEMO_URL = '/1d3dc3e03047b0c7/1716484476499/1716485003466';
const DEMO_ROUTE_URL = '/1d3dc3e03047b0c7/000000dd--455f14369d';
const ZOOMED_DEMO_URL = '/1d3dc3e03047b0c7/000000dd--455f14369d/109/423';

jest.setTimeout(60000);

Expand Down
2 changes: 1 addition & 1 deletion src/__puppeteer__/routing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('routing', () => {
});

it('load route from URL', async () => {
await goto('/1d3dc3e03047b0c7/1716484475499/1716485004466', { timeout: 50000 });
await goto('/1d3dc3e03047b0c7/000000dd--455f14369d', { timeout: 50000 });

// Wait for video src to be set
await page.waitForFunction(
Expand Down
30 changes: 25 additions & 5 deletions src/actions/history.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { LOCATION_CHANGE } from 'connected-react-router';
import { getDongleID, getZoom, getPrimeNav } from '../url';
import { primeNav, selectDevice, pushTimelineRange } from './index';
import { getDongleID, getZoom, getSegmentRange, getPrimeNav } from '../url';
import { primeNav, selectDevice, pushTimelineRange, updateSegmentRange } from './index';
import { drives as Drives } from '@commaai/api';

export const onHistoryMiddleware = ({ dispatch, getState }) => (next) => (action) => {
export const onHistoryMiddleware = ({ dispatch, getState }) => (next) => async (action) => {
if (!action) {
return;
}
Expand All @@ -18,8 +19,27 @@ export const onHistoryMiddleware = ({ dispatch, getState }) => (next) => (action
}

const pathZoom = getZoom(action.payload.location.pathname);
if (pathZoom !== state.zoom) {
dispatch(pushTimelineRange(pathZoom?.start, pathZoom?.end, false));
Comment on lines -21 to -22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0x7B5 this used to reset the timeline zoom points when you pressed back in the browser, but below I don't think ever executes anymore, or at least not in this case.

const pathSegmentRange = getSegmentRange(action.payload.location.pathname);

if ((pathZoom !== state.zoom) && pathZoom && !pathSegmentRange) {
const [start, end] = [pathZoom.start, pathZoom.end];

Drives.getRoutesSegments(pathDongleId, start, end).then((routesData) => {
if (routesData && routesData.length > 0) {
const log_id = routesData[0].fullname.split('|')[1];
const duration = routesData[0].end_time_utc_millis - routesData[0].start_time_utc_millis;

dispatch(pushTimelineRange(log_id, null, null, true));
dispatch(updateSegmentRange(log_id, 0, duration));
}
}).catch((err) => {
console.error('Error fetching routes data for log ID conversion', err);
});
}


if (pathSegmentRange !== state.segmentRange) {
dispatch(pushTimelineRange(pathSegmentRange?.log_id, pathSegmentRange?.start, pathSegmentRange?.end, false));
}

const pathPrimeNav = getPrimeNav(action.payload.location.pathname);
Expand Down
10 changes: 5 additions & 5 deletions src/actions/history.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('history middleware', () => {
};
invoke(action);
expect(next).toHaveBeenCalledWith(action);
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledTimes(2);
expect(store.dispatch).toHaveBeenCalledWith(fakeInner);
expect(actionsIndex.selectDevice).toHaveBeenCalledWith('0000aaaa0000aaaa', false);
});
Expand All @@ -96,14 +96,14 @@ describe('history middleware', () => {
type: LOCATION_CHANGE,
payload: {
action: 'POP',
location: { pathname: '0000aaaa0000aaaa/1230/1234' },
location: { pathname: '0000aaaa0000aaaa/00000000--000f00000d/1230/1234' },
},
};
invoke(action);
expect(next).toHaveBeenCalledWith(action);
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(fakeInner);
expect(actionsIndex.pushTimelineRange).toHaveBeenCalledWith(1230, 1234, false);
expect(actionsIndex.pushTimelineRange).toHaveBeenCalledWith("00000000--000f00000d", 1230000, 1234000, false);
});

it('should call prime nav with history', async () => {
Expand All @@ -130,7 +130,7 @@ describe('history middleware', () => {
expect(store.dispatch).toHaveBeenCalledTimes(2);
expect(store.dispatch).toHaveBeenCalledWith(fakeInner);
expect(store.dispatch).toHaveBeenCalledWith(fakeInner2);
expect(actionsIndex.pushTimelineRange).toHaveBeenCalledWith(undefined, undefined, false);
expect(actionsIndex.pushTimelineRange).toHaveBeenCalledWith(undefined, undefined, undefined, false);
expect(actionsIndex.primeNav).toHaveBeenCalledWith(true);
});
});
});
58 changes: 39 additions & 19 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import MyCommaAuth from '@commaai/my-comma-auth';

import * as Types from './types';
import { resetPlayback, selectLoop } from '../timeline/playback';
import { getSegmentFetchRange, hasRoutesData } from '../timeline/segments';
import {hasRoutesData } from '../timeline/segments';
import { getDeviceFromState, deviceVersionAtLeast } from '../utils';

let routesRequest = null;
Expand All @@ -26,7 +26,7 @@ export function checkRoutesData() {
}
console.debug('We need to update the segment metadata...');
const { dongleId } = state;
const fetchRange = getSegmentFetchRange(state);
const fetchRange = state.filter;

routesRequest = {
req: Drives.getRoutesSegments(dongleId, fetchRange.start, fetchRange.end),
Expand All @@ -35,7 +35,7 @@ export function checkRoutesData() {

routesRequest.req.then((routesData) => {
state = getState();
const currentRange = getSegmentFetchRange(state);
const currentRange = state.filter;
if (currentRange.start !== fetchRange.start
|| currentRange.end !== fetchRange.end
|| state.dongleId !== dongleId) {
Expand Down Expand Up @@ -66,12 +66,15 @@ export function checkRoutesData() {
return {
...r,
url: r.url.replace('chffrprivate.blob.core.windows.net', 'chffrprivate.azureedge.net'),
offset: Math.round(startTime) - state.filter.start,
log_id: r.fullname.split('|')[1],
duration: endTime - startTime,
start_time_utc_millis: startTime,
end_time_utc_millis: endTime,
segment_offsets: r.segment_start_times.map((x) => x - state.filter.start),
// TODO: get this from the API, this isn't correct for segments with a time jump
segment_durations: r.segment_start_times.map((x, i) => r.segment_end_times[i] - x),
};
}).sort((a, b) => {
return b.create_time - a.create_time;
});

dispatch({
Expand All @@ -91,20 +94,23 @@ export function checkRoutesData() {
};
}

export function urlForState(dongleId, start, end, prime) {
export function urlForState(dongleId, log_id, start, end, prime) {
const path = [dongleId];

if (start && end) {
path.push(start);
path.push(end);
if (log_id) {
path.push(log_id);
if (start && end && start > 0) {
path.push(start);
path.push(end);
}
} else if (prime) {
path.push('prime');
}

return `/${path.join('/')}`;
}

function updateTimeline(state, dispatch, start, end, allowPathChange) {
function updateTimeline(state, dispatch, log_id, start, end, allowPathChange) {
dispatch(checkRoutesData());

if (!state.loop || !state.loop.startTime || !state.loop.duration || state.loop.startTime < start
Expand All @@ -114,14 +120,14 @@ function updateTimeline(state, dispatch, start, end, allowPathChange) {
}

if (allowPathChange) {
const desiredPath = urlForState(state.dongleId, start, end, false);
const desiredPath = urlForState(state.dongleId, log_id, Math.floor(start/1000), Math.floor(end/1000), false);
if (window.location.pathname !== desiredPath) {
dispatch(push(desiredPath));
}
}
}

export function popTimelineRange(allowPathChange = true) {
export function popTimelineRange(log_id, allowPathChange = true) {
return (dispatch, getState) => {
const state = getState();
if (state.zoom.previous) {
Expand All @@ -130,26 +136,30 @@ export function popTimelineRange(allowPathChange = true) {
});

const { start, end } = state.zoom.previous;
updateTimeline(state, dispatch, start, end, allowPathChange);
updateTimeline(state, dispatch, log_id, start, end, allowPathChange);
}
};
}

export function pushTimelineRange(start, end, allowPathChange = true) {
export function pushTimelineRange(log_id, start, end, allowPathChange = true) {
return (dispatch, getState) => {
const state = getState();
if (state.zoom?.start !== start || state.zoom?.end !== end) {

if (state.zoom?.start !== start || state.zoom?.end !== end || state.segmentRange?.log_id !== log_id) {
dispatch({
type: Types.TIMELINE_PUSH_SELECTION,
log_id,
start,
end,
});
}

updateTimeline(state, dispatch, start, end, allowPathChange);
updateTimeline(state, dispatch, log_id, start, end, allowPathChange);
};

}


export function primeGetSubscription(dongleId, subscription) {
return {
type: Types.ACTION_PRIME_SUBSCRIPTION,
Expand Down Expand Up @@ -206,6 +216,15 @@ export function fetchDeviceOnline(dongleId) {
};
}

export function updateSegmentRange(log_id, start, end) {
return {
type: Types.ACTION_UPDATE_SEGMENT_RANGE,
log_id,
start,
end,
};
}

export function selectDevice(dongleId, allowPathChange = true) {
return (dispatch, getState) => {
const state = getState();
Expand All @@ -222,7 +241,8 @@ export function selectDevice(dongleId, allowPathChange = true) {
dongleId,
});

dispatch(pushTimelineRange(null, null, false));
dispatch(pushTimelineRange(null, null, null, false));
dispatch(updateSegmentRange(null, null, null));
if ((device && !device.shared) || state.profile?.superuser) {
dispatch(primeFetchSubscription(dongleId, device));
dispatch(fetchDeviceOnline(dongleId));
Expand All @@ -231,7 +251,7 @@ export function selectDevice(dongleId, allowPathChange = true) {
dispatch(checkRoutesData());

if (allowPathChange) {
const desiredPath = urlForState(dongleId, null, null, null);
const desiredPath = urlForState(dongleId, null, null, null, null);
if (window.location.pathname !== desiredPath) {
dispatch(push(desiredPath));
}
Expand All @@ -255,7 +275,7 @@ export function primeNav(nav, allowPathChange = true) {

if (allowPathChange) {
const curPath = document.location.pathname;
const desiredPath = urlForState(state.dongleId, null, null, nav);
const desiredPath = urlForState(state.dongleId, null, null, null, nav);
if (curPath !== desiredPath) {
dispatch(push(desiredPath));
}
Expand Down
6 changes: 3 additions & 3 deletions src/actions/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ describe('timeline actions', () => {
it('should push history state when editing zoom', () => {
const dispatch = jest.fn();
const getState = jest.fn();
const actionThunk = pushTimelineRange(123, 1234);
const actionThunk = pushTimelineRange("log_id", 123, 1234);

getState.mockImplementationOnce(() => ({
dongleId: 'statedongle',
loop: {},
zoom: {},
}));
actionThunk(dispatch, getState);
expect(push).toBeCalledWith('/statedongle/123/1234');
expect(push).toBeCalledWith('/statedongle/log_id');
});
});
});
2 changes: 1 addition & 1 deletion src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const ACTION_BUFFER_VIDEO = 'action_buffer_video';
export const ACTION_RESET = 'action_reset';

// segments
export const ACTION_UPDATE_SEGMENTS = 'update_segments';
export const ACTION_UPDATE_SEGMENT_RANGE = 'update_segment_range';
export const ACTION_ROUTES_METADATA = 'routes_metadata';

// files
Expand Down
4 changes: 2 additions & 2 deletions src/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ export function attachRelTime(obj, key, ms = true, cluster = null) {
}

function getVideoPercent(state, offset) {
const { zoom, filter } = state;
const { zoom } = state;
if (!offset) {
offset = state.offset;
}
return (offset - (zoom.start - filter.start)) / (zoom.end - zoom.start);
return (offset - (zoom.start)) / (zoom.end - zoom.start);
}

function logAction(action, prevState, state) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/AppHeader/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const styles = () => ({
});

const AppHeader = ({
profile, classes, dispatch, drawerIsOpen, annotating, showDrawerButton,
profile, classes, dispatch, drawerIsOpen, viewingRoute, showDrawerButton,
forwardRef, handleDrawerStateChanged, primeNav, dongleId,
}) => {
const [anchorEl, setAnchorEl] = useState(null);
Expand Down Expand Up @@ -112,7 +112,7 @@ const AppHeader = ({
</a>
</div>
<div className="flex order-4 w-full justify-center sm:order-none sm:w-auto">
{Boolean(!primeNav && !annotating && dongleId) && <TimeFilter />}
{Boolean(!primeNav && !viewingRoute && dongleId) && <TimeFilter />}
</div>
<div className="flex flex-row gap-2">
<Suspense><PWAIcon /></Suspense>
Expand Down
6 changes: 3 additions & 3 deletions src/components/Dashboard/DriveListItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const DriveListItem = (props) => {
}, [drive, dispatch, isVisible, el]);

const onClick = filterRegularClick(
() => dispatch(pushTimelineRange(drive.start_time_utc_millis, drive.end_time_utc_millis)),
() => dispatch(pushTimelineRange(drive.log_id, 0, drive.duration, true)),
);

const small = windowWidth < 580;
Expand Down Expand Up @@ -117,7 +117,7 @@ const DriveListItem = (props) => {
key={drive.fullname}
className={`${classes.drive} DriveEntry`}
ref={el}
href={`/${drive.dongle_id}/${drive.start_time_utc_millis}/${drive.end_time_utc_millis}`}
href={`/${drive.dongle_id}/${drive.log_id}`}
onClick={onClick}
>
<div className={classes.driveHeader} style={!small ? { padding: '18px 32px' } : { padding: 18 }}>
Expand Down Expand Up @@ -148,7 +148,7 @@ const DriveListItem = (props) => {
<Timeline
route={drive}
thumbnailsVisible={isVisible}
zoomOverride={{ start: drive.start_time_utc_millis, end: drive.end_time_utc_millis }}
zoomOverride={{ start: 0, end: drive.duration }}
/>
</a>
);
Expand Down
Loading