UNPKG

spot-sdk-js

Version:

Develop applications and payloads for Spot using the unofficial Boston Dynamics Spot Node.js SDK.

224 lines (182 loc) 6.89 kB
const time_sync_pb = require('../bosdyn/api/time_sync_pb'); const time_sync_service_grpc_pb = require('../bosdyn/api/time_sync_service_grpc_pb'); const time_range_pb = require('../bosdyn/api/time_range_pb'); const { RobotTimeConverter, now_nsec, parse_timespan, nsec_to_timestamp, set_timestamp_from_nsec, timestamp_to_nsec } = require('../bosdyn-core/util'); const {BaseClient, common_header_errors} = require('./common'); class TimeSyncError extends Error { constructor(msg){ super(msg); this.name = 'TimeSyncError'; } }; class NotEstablishedError extends TimeSyncError { constructor(msg){ super(msg); this.name = 'NotEstablishedError'; } }; class TimedOutError extends TimeSyncError { constructor(msg){ super(msg); this.name = 'TimedOutError'; } }; class InactiveThreadError extends TimeSyncError { constructor(msg){ super(msg); this.name = 'InactiveThreadError'; } }; class TimeSyncClient extends BaseClient { static default_service_name = 'time-sync'; static service_type = 'bosdyn.api.TimeSyncService'; constructor(){ super(time_sync_service_grpc_pb.TimeSyncServiceClient); } async get_time_sync_update(previous_round_trip, clock_identifier, args){ const req = TimeSyncClient._get_time_sync_update_request(previous_round_trip, clock_identifier); return await this.call(this._stub.timeSyncUpdate, req, null, common_header_errors, args); } get_time_sync_update_async(previous_round_trip, clock_identifier, args){ var req = TimeSyncClient._get_time_sync_update_request(previous_round_trip, clock_identifier); return this.call_async(this._stub.timeSyncUpdate, req, null, common_header_errors, args); } static _get_time_sync_update_request(previous_round_trip, clock_identifier){ const req = new time_sync_pb.TimeSyncUpdateRequest() .setPreviousRoundTrip(previous_round_trip) .setClockIdentifier(clock_identifier); return req; } }; /*function _get_time_sync_status_value(response){ return response.getTimeSyncStatusMap(); }*/ function robot_time_range_from_nanoseconds(start_nsec, end_nsec, time_sync_endpoint = null){ const time_range = new time_range_pb.TimeRange(); const converter = time_sync_endpoint ? time_sync_endpoint.get_robot_time_converter() : null; function _convert_nsec(nsec){ let timestamp_proto = nsec_to_timestamp(parseInt(nsec)); if(!time_sync_endpoint) return timestamp_proto; return converter.robot_timestamp_from_local(timestamp_proto); } if(start_nsec) time_range.setStart(_convert_nsec(start_nsec)); if(end_nsec) time_range.setEnd(_convert_nsec(end_nsec)); return time_range; } function robot_time_range_from_datetimes(start_datetime, end_datetime, time_sync_endpoint = null){ function _datetime_to_nsec(date_time){ if(date_time) return new Date(date_time).getTime() * 1e6; return null; } return robot_time_range_from_nanoseconds(_datetime_to_nsec(start_datetime), _datetime_to_nsec(end_datetime), time_sync_endpoint); } function timespec_to_robot_timespan(timespan_spec, time_sync_endpoint = null){ let [start_datetime, end_datetime] = parse_timespan(timespan_spec) return robot_time_range_from_datetimes(start_datetime, end_datetime, time_sync_endpoint); } class TimeSyncEndpoint { constructor(time_sync_client){ this._client = time_sync_client; // this._lock = Lock(); this._locked_previous_round_trip = null; this._locked_previous_response = null; this._locked_clock_identifier = ""; } get response(){ return this._locked_previous_response; } get has_established_time_sync(){ const response = this.response; return response && response.getState().getStatus() == time_sync_pb.TimeSyncState.Status.STATUS_OK; } get round_trip_time(){ const response = this.response; if(!response) return null; return response.getState().getBestEstimate().getRoundTripTime(); } get clock_identifier(){ return this._locked_clock_identifier; } get clock_skew(){ const response = this.response; if(!response || response.getState().getStatus() != time_sync_pb.TimeSyncState.Status.STATUS_OK) throw new NotEstablishedError(); return response.getState().getBestEstimate().getClockSkew(); } establish_timesync(max_samples = 25, break_on_success = false){ let counter = 0; while(counter < max_samples){ if(break_on_success && this.has_established_time_sync) return true; this.get_new_estimate(); counter += 1; } return this.has_established_time_sync; } async _get_update(){ let round_trip = null; let clock_identifier = null; if(this._locked_clock_identifier){ round_trip = this._locked_previous_round_trip; clock_identifier = this._locked_clock_identifier; } return await this._client.get_time_sync_update(round_trip, clock_identifier); } get_new_estimate(){ const response = this._get_update(); const header = reponse.getHeader(); const rx_time = now_nsec(); const round_trip = new time_sync_pb.TimeSyncRoundTrip() .setClientTx(header.getRequestHeader().getRequestTimestamp()) .setServerRx(header.getRequestReceivedTimestamp()) .setServerTx(header.getResponseTimestamp()); set_timestamp_from_nsec(round_trip.getClientRx(), rx_time); this._locked_previous_round_trip = round_trip; this._locked_previous_response = response; this._locked_clock_identifier = response.getClockIdentifier(); return this.has_established_time_sync; } get_robot_time_converter(){ return new RobotTimeConverter(timestamp_to_nsec(this.clock_skew)); } robot_timestamp_from_local_secs(local_time_secs){ if(!local_time_secs) return null; const converter = this.get_robot_time_converter(); return converter.robot_timestamp_from_local_secs(local_time_secs); } }; // Besoin de trouver un module pour faire du multi-thread pour continuer cette partie. /*class TimeSyncThread { DEFAULT_TIME_SYNC_INTERVAL_SEC = 60; TIME_SYNC_SERVICE_NOT_READY_INTERVAL_SEC = 5; constructor(time_sync_client){ this._time_sync_endpoint = new TimeSyncEndpoint(time_sync_client); this._lock = Lock(); this._locked_time_sync_interval_sec = this.DEFAULT_TIME_SYNC_INTERVAL_SEC; this._locked_should_exit = false; this._locked_thread_exception = null; this._event = Event(); this._thread = null; } start(){ if(this._thread && this._thread.is_alive()) return; this._locked_should_exit = false; this._locked_thread_exception = null; // this._event.clear() // this._thread = Thread(target=self._timesync_thread) // this._thread.daemon = true; } };*/ module.exports = { TimeSyncError, NotEstablishedError, TimedOutError, InactiveThreadError, TimeSyncClient, TimeSyncEndpoint }