Add back better ping indicator
This commit is contained in:
@ -975,6 +975,14 @@ export class CameraControls {
|
|||||||
view: this.oldCameraState,
|
view: this.oldCameraState,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await this.engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveRemoteCameraState() {
|
async saveRemoteCameraState() {
|
||||||
|
@ -10,22 +10,8 @@ import {
|
|||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
export const ModelStateIndicator = () => {
|
export const ModelStateIndicator = () => {
|
||||||
const [commands] = useEngineCommands()
|
|
||||||
const [isDone, setIsDone] = useState<boolean>(false)
|
|
||||||
|
|
||||||
const engineStreamState = useSelector(engineStreamActor, (state) => state)
|
const engineStreamState = useSelector(engineStreamActor, (state) => state)
|
||||||
|
|
||||||
const lastCommandType = commands[commands.length - 1]?.type
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (lastCommandType === CommandLogType.SetDefaultSystemProperties) {
|
|
||||||
setIsDone(false)
|
|
||||||
}
|
|
||||||
if (lastCommandType === CommandLogType.ExecutionDone) {
|
|
||||||
setIsDone(true)
|
|
||||||
}
|
|
||||||
}, [lastCommandType])
|
|
||||||
|
|
||||||
let className = 'w-6 h-6 '
|
let className = 'w-6 h-6 '
|
||||||
let icon = <div className={className}></div>
|
let icon = <div className={className}></div>
|
||||||
let dataTestId = 'model-state-indicator'
|
let dataTestId = 'model-state-indicator'
|
||||||
@ -38,15 +24,7 @@ export const ModelStateIndicator = () => {
|
|||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
/>
|
/>
|
||||||
} else if (engineStreamState.value === EngineStreamState.Resuming) {
|
} else if (engineStreamState.value === EngineStreamState.Playing) {
|
||||||
className += 'text-secondary'
|
|
||||||
icon = <FontAwesomeIcon
|
|
||||||
data-testid={dataTestId + '-resuming'}
|
|
||||||
icon={faSpinner}
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
/>
|
|
||||||
} else if (isDone) {
|
|
||||||
className += 'text-secondary'
|
className += 'text-secondary'
|
||||||
icon = (
|
icon = (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
@ -56,6 +34,14 @@ export const ModelStateIndicator = () => {
|
|||||||
height="20"
|
height="20"
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
className += 'text-secondary'
|
||||||
|
icon = <FontAwesomeIcon
|
||||||
|
data-testid={dataTestId + '-resuming'}
|
||||||
|
icon={faSpinner}
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -86,6 +86,7 @@ export const NetworkHealthIndicator = () => {
|
|||||||
error,
|
error,
|
||||||
setHasCopied,
|
setHasCopied,
|
||||||
hasCopied,
|
hasCopied,
|
||||||
|
ping
|
||||||
} = useNetworkContext()
|
} = useNetworkContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -129,6 +130,17 @@ export const NetworkHealthIndicator = () => {
|
|||||||
{NETWORK_HEALTH_TEXT[overallState]}
|
{NETWORK_HEALTH_TEXT[overallState]}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className={`flex items-center justify-between p-2 rounded-t-sm`}
|
||||||
|
>
|
||||||
|
<h2 className={`text-xs font-sans font-normal ${overallConnectionStateColor[overallState].icon}`}>Ping</h2>
|
||||||
|
<p
|
||||||
|
data-testid="network"
|
||||||
|
className={`font-bold text-xs uppercase px-2 py-1 rounded-sm ${overallConnectionStateColor[overallState].icon}`}
|
||||||
|
>
|
||||||
|
{ping ?? 'N/A'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
||||||
{Object.keys(steps).map((name) => (
|
{Object.keys(steps).map((name) => (
|
||||||
<li
|
<li
|
||||||
|
@ -25,7 +25,7 @@ export const NetworkContext = createContext<NetworkStatus>({
|
|||||||
error: undefined,
|
error: undefined,
|
||||||
setHasCopied: (b: boolean) => {},
|
setHasCopied: (b: boolean) => {},
|
||||||
hasCopied: false,
|
hasCopied: false,
|
||||||
pingPongHealth: undefined,
|
ping: undefined,
|
||||||
} as NetworkStatus)
|
} as NetworkStatus)
|
||||||
export const useNetworkContext = () => {
|
export const useNetworkContext = () => {
|
||||||
return useContext(NetworkContext)
|
return useContext(NetworkContext)
|
||||||
|
@ -32,7 +32,7 @@ export interface NetworkStatus {
|
|||||||
error: ErrorType | undefined
|
error: ErrorType | undefined
|
||||||
setHasCopied: (b: boolean) => void
|
setHasCopied: (b: boolean) => void
|
||||||
hasCopied: boolean
|
hasCopied: boolean
|
||||||
pingPongHealth: undefined | 'OK' | 'TIMEOUT'
|
ping: undefined | number
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called from one place in the application.
|
// Must be called from one place in the application.
|
||||||
@ -48,9 +48,7 @@ export function useNetworkStatus() {
|
|||||||
const [overallState, setOverallState] = useState<NetworkHealthState>(
|
const [overallState, setOverallState] = useState<NetworkHealthState>(
|
||||||
NetworkHealthState.Disconnected
|
NetworkHealthState.Disconnected
|
||||||
)
|
)
|
||||||
const [pingPongHealth, setPingPongHealth] = useState<
|
const [ping, setPing] = useState<undefined | number>(undefined)
|
||||||
undefined | 'OK' | 'TIMEOUT'
|
|
||||||
>(undefined)
|
|
||||||
const [hasCopied, setHasCopied] = useState<boolean>(false)
|
const [hasCopied, setHasCopied] = useState<boolean>(false)
|
||||||
|
|
||||||
const [error, setError] = useState<ErrorType | undefined>(undefined)
|
const [error, setError] = useState<ErrorType | undefined>(undefined)
|
||||||
@ -72,12 +70,12 @@ export function useNetworkStatus() {
|
|||||||
!internetConnected
|
!internetConnected
|
||||||
? NetworkHealthState.Disconnected
|
? NetworkHealthState.Disconnected
|
||||||
: hasIssues || hasIssues === undefined
|
: hasIssues || hasIssues === undefined
|
||||||
? NetworkHealthState.Issue
|
? NetworkHealthState.Issue
|
||||||
: pingPongHealth === 'TIMEOUT'
|
: ping > (16.6*3) // we consider ping longer than 3 frames as weak
|
||||||
? NetworkHealthState.Weak
|
? NetworkHealthState.Weak
|
||||||
: NetworkHealthState.Ok
|
: NetworkHealthState.Ok
|
||||||
)
|
)
|
||||||
}, [hasIssues, internetConnected, pingPongHealth])
|
}, [hasIssues, internetConnected, ping])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onlineCallback = () => {
|
const onlineCallback = () => {
|
||||||
@ -128,7 +126,8 @@ export function useNetworkStatus() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onPingPongChange = ({ detail: state }: CustomEvent) => {
|
const onPingPongChange = ({ detail: state }: CustomEvent) => {
|
||||||
setPingPongHealth(state)
|
console.log(state)
|
||||||
|
setPing(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onConnectionStateChange = ({
|
const onConnectionStateChange = ({
|
||||||
@ -233,6 +232,6 @@ export function useNetworkStatus() {
|
|||||||
error,
|
error,
|
||||||
setHasCopied,
|
setHasCopied,
|
||||||
hasCopied,
|
hasCopied,
|
||||||
pingPongHealth,
|
ping,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,7 @@ import {
|
|||||||
import { reportRejection } from '@src/lib/trap'
|
import { reportRejection } from '@src/lib/trap'
|
||||||
import { binaryToUuid, uuidv4 } from '@src/lib/utils'
|
import { binaryToUuid, uuidv4 } from '@src/lib/utils'
|
||||||
|
|
||||||
// TODO(paultag): This ought to be tweakable.
|
const pingIntervalMs = 1_000
|
||||||
const pingIntervalMs = 5_000
|
|
||||||
|
|
||||||
function isHighlightSetEntity_type(
|
function isHighlightSetEntity_type(
|
||||||
data: any
|
data: any
|
||||||
@ -191,8 +190,6 @@ export type EngineConnectionState =
|
|||||||
| State<EngineConnectionStateType.Disconnecting, DisconnectingValue>
|
| State<EngineConnectionStateType.Disconnecting, DisconnectingValue>
|
||||||
| State<EngineConnectionStateType.Disconnected, void>
|
| State<EngineConnectionStateType.Disconnected, void>
|
||||||
|
|
||||||
export type PingPongState = 'OK' | 'TIMEOUT'
|
|
||||||
|
|
||||||
export enum EngineConnectionEvents {
|
export enum EngineConnectionEvents {
|
||||||
// Fires for each ping-pong success or failure.
|
// Fires for each ping-pong success or failure.
|
||||||
PingPongChanged = 'ping-pong-changed', // (state: PingPongState) => void
|
PingPongChanged = 'ping-pong-changed', // (state: PingPongState) => void
|
||||||
@ -333,70 +330,17 @@ class EngineConnection extends EventTarget {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Without an interval ping, our connection will timeout.
|
|
||||||
// If this.idleMode is true we skip this logic so only reconnect
|
|
||||||
// happens on mouse move
|
|
||||||
this.pingIntervalId = setInterval(() => {
|
this.pingIntervalId = setInterval(() => {
|
||||||
if (this.idleMode) return
|
// Only start a new ping when the other is fulfilled.
|
||||||
|
if (this.pingPongSpan.ping) { return }
|
||||||
|
|
||||||
switch (this.state.type as EngineConnectionStateType) {
|
// Don't start pinging until we're connected.
|
||||||
case EngineConnectionStateType.ConnectionEstablished:
|
if (this.state.type !== EngineConnectionStateType.ConnectionEstablished) { return }
|
||||||
// If there was no reply to the last ping, report a timeout and
|
|
||||||
// teardown the connection.
|
|
||||||
if (this.pingPongSpan.ping && !this.pingPongSpan.pong) {
|
|
||||||
this.dispatchEvent(
|
|
||||||
new CustomEvent(EngineConnectionEvents.PingPongChanged, {
|
|
||||||
detail: 'TIMEOUT',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
this.state = {
|
|
||||||
type: EngineConnectionStateType.Disconnecting,
|
|
||||||
value: {
|
|
||||||
type: DisconnectingType.Timeout,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
this.disconnectAll()
|
|
||||||
|
|
||||||
// Otherwise check the time between was >= pingIntervalMs,
|
this.send({ type: 'ping' })
|
||||||
// and if it was, then it's bad network health.
|
this.pingPongSpan = {
|
||||||
} else if (this.pingPongSpan.ping && this.pingPongSpan.pong) {
|
ping: new Date(),
|
||||||
if (
|
pong: undefined
|
||||||
Math.abs(
|
|
||||||
this.pingPongSpan.pong.valueOf() -
|
|
||||||
this.pingPongSpan.ping.valueOf()
|
|
||||||
) >= pingIntervalMs
|
|
||||||
) {
|
|
||||||
this.dispatchEvent(
|
|
||||||
new CustomEvent(EngineConnectionEvents.PingPongChanged, {
|
|
||||||
detail: 'TIMEOUT',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
this.dispatchEvent(
|
|
||||||
new CustomEvent(EngineConnectionEvents.PingPongChanged, {
|
|
||||||
detail: 'OK',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send({ type: 'ping' })
|
|
||||||
this.pingPongSpan.ping = new Date()
|
|
||||||
this.pingPongSpan.pong = undefined
|
|
||||||
break
|
|
||||||
case EngineConnectionStateType.Disconnecting:
|
|
||||||
case EngineConnectionStateType.Disconnected:
|
|
||||||
// We will do reconnection elsewhere, because we basically need
|
|
||||||
// to destroy this EngineConnection, and this setInterval loop
|
|
||||||
// lives inside it. (lee) I might change this in the future so it's
|
|
||||||
// outside this class.
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
if (this.isConnecting()) break
|
|
||||||
// Means we never could do an initial connection. Reconnect everything.
|
|
||||||
if (!this.pingPongSpan.ping)
|
|
||||||
this.connect({ reconnect: false }).catch(reportRejection)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}, pingIntervalMs)
|
}, pingIntervalMs)
|
||||||
|
|
||||||
@ -1054,6 +998,14 @@ class EngineConnection extends EventTarget {
|
|||||||
switch (resp.type) {
|
switch (resp.type) {
|
||||||
case 'pong':
|
case 'pong':
|
||||||
this.pingPongSpan.pong = new Date()
|
this.pingPongSpan.pong = new Date()
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent(EngineConnectionEvents.PingPongChanged, {
|
||||||
|
detail: Math.min(999, Math.floor(this.pingPongSpan.pong - this.pingPongSpan.ping)),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
// Clear the initial ping so our interval ping loop can fire again
|
||||||
|
// But only after using it above!
|
||||||
|
this.pingPongSpan.ping = undefined
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'modeling_session_data':
|
case 'modeling_session_data':
|
||||||
|
Reference in New Issue
Block a user