Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
be shown if any pair of takeoff positions or any pair of landing positions are
too close to each other.

- Make the "Flash lights" button latchable by holding Shift while activating it.

### Changed

- When setting the start time based on a time offset from the current time,
Expand Down
1 change: 1 addition & 0 deletions config/baseline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const baseline: Config = {
'app-settings-button',
'alert-button',
'help-button',
'check-clock-sync',
'full-screen-button',
'session-expiry-box',
],
Expand Down
137 changes: 137 additions & 0 deletions src/components/header/CheckClockSync.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import config from 'config';
import React from 'react';
import { Translation } from 'react-i18next';

import MoreTime from '@material-ui/icons/MoreTime';
import GenericHeaderButton from '@skybrush/mui-components/lib/GenericHeaderButton';

import store from '~/store';
import {showNotification } from '~/features/snackbar/actions';
import { MessageSemantics } from '~/features/snackbar/types';




async function CheckClock(){

const MsThreShold=2; //MilliSeconds ThreShold
const ScThreShold=1; //Seconds ThreShold
const MmThreShold=1; //Minute ThreShold
const HhThreShold=1; //Hour ThreShold
const DdThreShold=1; //Days ThreShold


//Get Actual Timezone
try{

const timezone=Intl.DateTimeFormat().resolvedOptions().timeZone;
const TimeZone_Array = timezone.split("/");

var continent = TimeZone_Array[0];
var city = TimeZone_Array[1];

}
catch{
store.dispatch(showNotification( { message: 'TimeAPI: Not able to retrieve local timezone.',semantics: MessageSemantics.WARNING}));
return
}

try{


store.dispatch(showNotification( "TimeAPI: Getting Time from API based on "+continent+"/"+city));

const response = await fetch('https://timeapi.io/api/time/current/zone?timeZone='+continent+'%2F'+city,{ signal: AbortSignal.timeout(5000) });
const data = await response.json();
const api_date_time_complete=JSON.stringify(data.dateTime)
const regex = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d+)/;
const match = api_date_time_complete.match(regex);

if (match) {
const year = match[1];
const month = match[2];
const day = match[3];
const hour = match[4];
const minute = match[5];
const second = match[6];
const millisecond = match[7].substring(0, 3); //Take only 3 digit after point
const final_match= year+"-"+month+"-"+day+"T"+hour+":"+minute+":"+second+"."+millisecond
var api_data_object=new Date(final_match);

} else {
store.dispatch(showNotification( { message: 'TimeAPI: Time format not valid',semantics: MessageSemantics.WARNING}));
return
}



var currentDate = new Date(); // Get Actual Date Object


//Calculate difference beetween local date and api date
const differenceInMilliseconds = Math.round(Math.abs(api_data_object - currentDate));
const differenceInSeconds = Math.round(Math.abs(differenceInMilliseconds / 1000));
const differenceInMinutes = Math.round(Math.abs(differenceInSeconds / 60));
const differenceInHours = Math.round(Math.abs(differenceInMinutes / 60));
const differenceInDays = Math.round(Math.abs(differenceInHours / 24));

var delayed=false;

if(differenceInMilliseconds>MsThreShold || differenceInSeconds>ScThreShold || differenceInMinutes>MmThreShold || differenceInHours>HhThreShold || differenceInDays>DdThreShold )
{
delayed=true
// Dynamic Message
var delayParts = [];

if (differenceInDays > 0) {
delayParts.push(<div>Days: {differenceInDays}</div>);
}
if (differenceInHours > 0) {
delayParts.push(<div>Hours: {differenceInHours}</div>);
}
if (differenceInMinutes > 0) {
delayParts.push(<div>Minutes: {differenceInMinutes}</div>);
}
if (differenceInSeconds > 0) {
delayParts.push(<div>Seconds: {differenceInSeconds}</div>);
}
if (differenceInMilliseconds > 0) {
delayParts.push(<div>Milliseconds: {differenceInMilliseconds}</div>);
}
}


if(delayed==false){store.dispatch(showNotification({message:"TimeAPI: Gcs clock is Sync",semantics:MessageSemantics.SUCCESS}))}
else
{
store.dispatch(showNotification({message:
<span>
TimeAPI : Delay of : <br />
{delayParts}
</span>,
semantics:MessageSemantics.WARNING}))
}
return

}catch{
store.dispatch(showNotification( { message: 'TimeAPI: No internet Connection , not able to retrieve data',semantics: MessageSemantics.WARNING}));
return
}
};

const CheckClockSyncButton = () => (
<Translation>
{(t) => (
<GenericHeaderButton
id='tour-CheckClockSync-button'
tooltip={t('CheckClockSync')}
onClick={CheckClock}
>
<MoreTime />
</GenericHeaderButton>
)}
</Translation>

);

export default CheckClockSyncButton;
2 changes: 2 additions & 0 deletions src/components/header/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import AppSettingsButton from './AppSettingsButton';
import AuthenticationButton from './AuthenticationButton';
import BroadcastButton from './BroadcastButton';
import ConnectionStatusButton from './ConnectionStatusButton';
import CheckClockSyncButton from './CheckClockSync';
import FullScreenButton from './FullScreenButton';
import HelpButton from './HelpButton';
import SafetyButton from './SafetyButton';
Expand Down Expand Up @@ -54,6 +55,7 @@ const componentRegistry = {
<BroadcastButton timeoutLength={BROADCAST_MODE_TIMEOUT_LENGTH} />
),
'connection-status-button': ConnectionStatusButton,
'check-clock-sync': () => (<CheckClockSyncButton />),
'distance-summary-header-button': DistanceSummaryHeaderButton,
'full-screen-button': FullScreenButton,
'help-button': () => (config.urls.help ? <HelpButton /> : null),
Expand Down
32 changes: 27 additions & 5 deletions src/components/uavs/UAVOperationsButtonGroup.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import isEmpty from 'lodash-es/isEmpty';
import PropTypes from 'prop-types';
import React from 'react';
import React, { useCallback, useState } from 'react';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { useInterval } from 'react-use';
import { bindActionCreators } from '@reduxjs/toolkit';

import Button from '@material-ui/core/Button';
Expand All @@ -27,6 +28,7 @@ import Colors from '~/components/colors';
import ToolbarDivider from '~/components/ToolbarDivider';
import Bolt from '~/icons/Bolt';

import { UAV_SIGNAL_DURATION } from '~/features/settings/constants';
import {
requestRemovalOfUAVsByIds,
requestRemovalOfUAVsMarkedAsGone,
Expand Down Expand Up @@ -89,16 +91,33 @@ const UAVOperationsButtonGroup = ({
dispatch
);

const [keepFlashing, setKeepFlashing] = useState(false);
const flashLightsButtonOnClick = useCallback(
(event) => {
if (keepFlashing) {
setKeepFlashing(false);
} else if (event.shiftKey) {
setKeepFlashing(true);
flashLight();
} else {
flashLight();
}
},
[flashLight, keepFlashing, setKeepFlashing]
);

useInterval(flashLight, keepFlashing ? UAV_SIGNAL_DURATION * 1000 : null);

const fontSize = size === 'small' ? 'small' : 'medium';
const iconSize = size;

const flashLightsButton =
size === 'small' ? (
<Button
startIcon={<WbSunny />}
startIcon={<WbSunny color={keepFlashing ? 'primary' : undefined} />}
disabled={isSelectionEmpty}
size={iconSize}
onClick={flashLight}
onClick={flashLightsButtonOnClick}
>
{t('UAVOpButtonGrp.flashLights')}
</Button>
Expand All @@ -107,9 +126,12 @@ const UAVOperationsButtonGroup = ({
<IconButton
disabled={isSelectionEmpty}
size={iconSize}
onClick={flashLight}
onClick={flashLightsButtonOnClick}
>
<WbSunny fontSize={fontSize} />
<WbSunny
fontSize={fontSize}
color={keepFlashing ? 'primary' : undefined}
/>
</IconButton>
</Tooltip>
);
Expand Down
7 changes: 6 additions & 1 deletion src/features/settings/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
/**
* Timeout length for broadcast mode.
* Timeout length for broadcast mode, in seconds.
*/
export const BROADCAST_MODE_TIMEOUT_LENGTH = 5;

/**
* Duration of the `UAV-SIGNAL` (flash light) command, in seconds.
*/
export const UAV_SIGNAL_DURATION = 5;
10 changes: 10 additions & 0 deletions src/features/uavs/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { convertRGB565ToCSSNotation } from '~/flockwave/parsing';
import { globalIdToUavId } from '~/model/identifiers';
import { UAVAge } from '~/model/uav';
import { selectionForSubset } from '~/selectors/selection';
import { selectOrdered } from '~/utils/collections';
import { euclideanDistance2D, getMeanAngle } from '~/utils/math';
import { EMPTY_ARRAY } from '~/utils/redux';
import { createDeepResultSelector } from '~/utils/selectors';
Expand Down Expand Up @@ -68,6 +69,15 @@ export const getUAVIdToStateMapping = (state) => state.uavs.byId;
*/
export const getUAVById = (state, uavId) => state.uavs.byId[uavId];

/**
* Selector that calculates and caches the list of all the UAVs in the
* state object.
*/
export const getUAVsInOrder = createSelector(
(state) => state.uavs,
selectOrdered
);

/**
* Returns the current position of the UAV with the given ID, given the current
* state.
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"APIKeysTab": {
"description": "Some map tile providers work without an API key; in this case, a default API key is used. This API key is shared between all users. We make no guarantees about the availability of map tiles if you use the default shared API key."
},
"CheckClockSync": "Check delay of GCS Clock based on WorldTime API",
"ConnectionStatusMiniList": {
"noConnections": "This server uses no connections.",
"useSecondaryChannel": "Use secondary channel"
Expand Down Expand Up @@ -85,6 +86,7 @@
"disable": "Disable broadcast",
"enable": "Enable broadcast for {{time}} seconds"
},
"ciao": "",
"coordinateAveragingDialogToolbar": {
"addNewDrone": "Add new drone",
"copyCentroid": "Copy centroid",
Expand Down
14 changes: 14 additions & 0 deletions src/model/layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Map from '@material-ui/icons/Map';
import MyLocation from '@material-ui/icons/MyLocation';
import Timeline from '@material-ui/icons/Timeline';
import TrackChanges from '@material-ui/icons/TrackChanges';
import Wifi from '@material-ui/icons/Wifi';

import type BaseLayer from 'ol/layer/Base';
import type OLLayer from 'ol/layer/Layer';
Expand Down Expand Up @@ -49,6 +50,9 @@ export enum LayerType {
UAV_TRACE = 'uavTrace',
UNAVAILABLE = 'unavailable',
UNTYPED = 'untyped',

RSSI = 'rssi',
RTK = "rtk",
}

export type Layer = {
Expand Down Expand Up @@ -76,6 +80,8 @@ export const LayerTypes = [
LayerType.OWN_LOCATION,
LayerType.GEOJSON,
LayerType.HEATMAP,
LayerType.RSSI,
LayerType.RTK,
] as const;

export const ProLayerTypes = [LayerType.IMAGE] as const;
Expand Down Expand Up @@ -251,6 +257,14 @@ const propertiesForLayerTypes: Record<
label: 'Untyped layer',
icon: HelpOutline,
},
[LayerType.RSSI]: {
label: 'RSSI',
icon: Wifi,
},
[LayerType.RTK]: {
label: 'RTK',
icon: Wifi,
},
} as const;

/**
Expand Down
4 changes: 4 additions & 0 deletions src/model/uav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export default class UAV {
// to the output later on, thus the object spread can be avoided.
_positionMemoizer: (position: GPSPosition) => GPSPosition;

// TODO: This should be unnecessary if we can ensure that no mutation happens
// to the output later on, thus the object spread can be avoided.
// _positionMemoizer: (position: GPSPosition) => GPSPosition;

/**
* Constructor.
*
Expand Down
3 changes: 2 additions & 1 deletion src/utils/messaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import mapValues from 'lodash-es/mapValues';
import values from 'lodash-es/values';

import { showConfirmationDialog } from '~/features/prompt/actions';
import { UAV_SIGNAL_DURATION } from '~/features/settings/constants';
import { shouldConfirmUAVOperation } from '~/features/settings/selectors';
import { showNotification } from '~/features/snackbar/actions';
import { MessageSemantics } from '~/features/snackbar/types';
Expand Down Expand Up @@ -148,7 +149,7 @@ export const flashLightOnUAVsAndHideFailures = performMassOperation({
name: 'Light signal command',
mapper: (options) => ({
signals: ['light'],
duration: 5000,
duration: UAV_SIGNAL_DURATION * 1000,
...options,
}),
reportSuccess: false,
Expand Down
5 changes: 5 additions & 0 deletions src/views/map/layers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ import { TileServerLayerSettings, TileServerLayer } from './tileserver';
import { UAVsLayerSettings, UAVsLayer } from './uavs';
import { UAVTraceLayerSettings, UAVTraceLayer } from './uavtrace';
import { UntypedLayerSettings, UntypedLayer } from './untyped';
import { RSSILayer } from './rssi';
import { RTKLayer } from './rtk_status';

import { LayerType } from '~/model/layers';
import { Layer } from 'ol/layer';

const UnavailableLayerSettings = () => (
<div key='_hint' style={{ position: 'relative', height: 48 }}>
Expand Down Expand Up @@ -76,6 +79,8 @@ export const Layers = {
[LayerType.UAVS]: UAVsLayer,
[LayerType.UAV_TRACE]: UAVTraceLayer,
[LayerType.UNTYPED]: UntypedLayer,
[LayerType.RSSI]: RSSILayer,
[LayerType.RTK]: RTKLayer,
};

export const stateObjectToLayer = (layer, props) => {
Expand Down
Loading