+
Status:
-
- WebSocket
+
+
-
+
Device Connection
-
+
tunneld
-
+
Location Simulation
diff --git a/src/components/models.ts b/src/components/models.ts
index 6945920..e2cf6bf 100644
--- a/src/components/models.ts
+++ b/src/components/models.ts
@@ -1,8 +1,201 @@
-export interface Todo {
- id: number;
- content: string;
+
+export type SimulationCommands = "start" | "pause" | "resume" | "clear" | "end" | "add";
+
+export type DeviceCommands= "start_tunnel" | "stop_tunnel" | "shutdown";
+
+export interface CtrlAttrs {
+ [key: string]: CtrlAttr;
+}
+
+export interface CtrlAttr {
+ name: string;
+ cmd: string;
+ cmdClass: string;
+ icon: string;
+ cnfrm: boolean;
+ delay: number;
+}
+
+export interface LocationQueue {
+ [key: string]: LocationMark
+}
+
+interface LocationMark {
+ loc_id: string;
+ latitude: number | undefined | null;
+ longitude: number | undefined | null;
+ delay?: number | undefined | null;
+ start_time: string | undefined | null;
+ end_time?: string | undefined | null ;
+}
+
+export interface SimulationControlResponse {
+ status: string;
+ command: SimulationCommands;
+ loc_id: string;
+ message?: string | undefined;
+ latitude?: number | undefined | null;
+ longitude?: number | undefined | null;
+ delay?: number | undefined | null;
+ start_time?: string | undefined | null;
+ end_time?: string | undefined | null;
+}
+
+interface DeviceControlResponse {
+ status: string;
+ command: DeviceCommands;
+ delay?: number;
+}
+
+interface StatusUpdate {
+ simulation_active: boolean;
+ set_location_enabled: boolean;
+ queue: number,
+ latitude: number | undefined | null;
+ longitude: number | undefined | null;
+ next_move?: number | undefined | null;
+ queue_list: LocationQueue[]
+ queue_state: string | undefined | null;
+ queue_status: boolean;
+ simulation_task: string | undefined | null;
+ test_mode: boolean
+ tunnel: string | undefined | null;
+ device_count?: number | undefined | null;
+ udid?: string | null;
+ device_name?: string | null | undefined;
+ product_version?: string | null | undefined;
+ phone_number?: string | null | undefined;
+ developer_mode_enabled?: boolean | undefined | null;
+ ddi_mounted?: boolean ;
+ rsd_address?: string | null | undefined;
+ rsd_port?: number | undefined | null;
+ lockdown_trusted_port?: number | undefined | null;
+ lockdown_untrusted_port?: number | undefined | null;
+ lockdown_trusted_reachable?: boolean | undefined | null;
+ lockdown_untrusted_reachable?: boolean | undefined | null;
+ dtservicehub_reachable?: boolean | undefined | null
+}
+
+export interface ServerToClientEvents {
+ noArg: () => void;
+ withAck: (a: string, callback: (b: number) => void) => void;
+ simulationStatus: (c: SimulationStatus) => void;
+ status: (d: StatusUpdate) => void;
+ device_status: (d: DeviceStatus) => void;
+ error: (data: ErrorFull) => void;
+ message: (e: string) => void;
+}
+
+export interface ClientToServerEvents {
+ message: (e: string, callback: (b: boolean, r: string) => void) => void;
+ simulation_control: (
+ args: {
+ command: SimulationCommands,
+ latitude?: number | null | undefined,
+ longitude?: number | null | undefined,
+ delay?: number | undefined
+ },
+ callback: (response: SimulationControlResponse) => void
+ ) => void;
+ device_control: (
+ args: {
+ command: DeviceCommands,
+ delay?: number
+ },
+ callback?: (response: DeviceControlResponse) => void
+ ) => void;
+}
+
+interface SimulationStatus {
+ status: boolean;
+ data: {
+ latitude: number;
+ longitude: number;
+ start: string;
+ end?: string;
+ next_move?: number;
+ };
}
export interface Meta {
totalCount: number;
}
+
+
+interface DeviceStatus {
+ device_connected: boolean;
+ device_count: number;
+ udid?: string | null;
+ device_name?: string | null;
+ product_version?: string | null;
+ phone_number?: string | null;
+ developer_mode_enabled?: boolean;
+ ddi_mounted?: boolean;
+ rsd_address?: string | null;
+ rsd_port?: number;
+ lockdown_trusted_port?: number;
+ lockdown_untrusted_port?: number;
+ lockdown_trusted_reachable?: boolean;
+ lockdown_untrusted_reachable?: boolean;
+ dtservicehub_reachable?: boolean;
+}
+
+export type Control = DeviceControl | SimulationControl;
+
+export interface DeviceControl {
+ id: number;
+ name: string;
+ cmd: DeviceCommands
+ cmdClass: "device_control"
+ icon: string;
+ confirm: boolean;
+}
+
+export interface SimulationControl {
+ id: number;
+ name: string;
+ cmd: SimulationCommands;
+ cmdClass: 'simulation_control';
+ icon: string;
+ confirm: boolean;
+}
+
+export interface coords {
+ lat: number;
+ lng: number;
+}
+
+import type { OpenStreetMapProvider } from 'leaflet-geosearch';
+export interface SearchControlProps {
+ provider: OpenStreetMapProvider;
+ showMarker: boolean;
+ autoClose: boolean;
+ updateMap: boolean;
+ showPopup: boolean;
+ style: 'button' | 'bar';
+ acceptAutoLoad: boolean;
+ autoComplete: boolean;
+ autoCompleteDelay: number;
+ retainZoomLevel: boolean;
+ animateZoom: boolean;
+ keepResult: boolean;
+}
+
+export interface CurrentLocation {
+ loc_id: string;
+ latitude: number;
+ longitude: number;
+ next_move?: number | null
+}
+
+export interface NextLocation {
+ loc_id: string;
+ latitude: number;
+ longitude: number;
+ time_at_location?: number | null;
+}
+
+export interface ErrorFull {
+ type: string;
+ error: string;
+}
diff --git a/src/constants/controls.ts b/src/constants/controls.ts
new file mode 100644
index 0000000..4a734c3
--- /dev/null
+++ b/src/constants/controls.ts
@@ -0,0 +1,62 @@
+import type { CtrlAttrs } from 'components/models';
+
+export const controls: CtrlAttrs = {
+ sim_start: {
+ name: 'Start Location Sim',
+ cmd: 'start',
+ cmdClass: 'simulation_control',
+ icon: 'play_arrow',
+ cnfrm: false,
+ delay: 0,
+ },
+ sim_pause: {
+ name: 'Pause Location Sim',
+ cmd: 'pause',
+ cmdClass: 'simulation_control',
+ icon: 'pause',
+ cnfrm: false,
+ delay: 0,
+ },
+ sim_resume: {
+ name: 'Resume Location Simulation',
+ cmd: 'resume',
+ cmdClass: 'simulation_control',
+ icon: 'play_arrow',
+ cnfrm: false,
+ delay: 0,
+ },
+ sim_clear: {
+ name: 'Clear Location Queue',
+ cmd: 'clear',
+ cmdClass: 'simulation_control',
+ icon: 'directions_off',
+ cnfrm: false,
+ delay: 0,
+ },
+ sim_end: {
+ name: 'End Location Sim',
+ cmd: 'end',
+ cmdClass: 'simulation_control',
+ icon: 'stop',
+ cnfrm: true,
+ delay: 0,
+ },
+ dev_shutdown: {
+ name: 'Shutdown',
+ cmd: 'shutdown',
+ cmdClass: 'device_control',
+ icon: 'power_settings_new',
+ cnfrm: true,
+ delay: 5,
+ },
+ dev_reboot: {
+ name: 'Reboot',
+ cmd: 'reboot',
+ cmdClass: 'device_control',
+ icon: 'restart_alt',
+ cnfrm: true,
+ delay: 5,
+ },
+};
+
+
diff --git a/src/constants/favorites.ts b/src/constants/favorites.ts
new file mode 100644
index 0000000..70f599f
--- /dev/null
+++ b/src/constants/favorites.ts
@@ -0,0 +1,79 @@
+export const favorites = [
+ {
+ name: 'Home',
+ icon: 'home',
+ coords: {
+ lat: 40.910773020811,
+ lng: -73.891069806448,
+ },
+ },
+ {
+ name: "Work Places",
+ icon: "work",
+ subitems: [
+ {
+ name: 'Jeong',
+ icon: 'language_korean_latin',
+ coords: {
+ lat: 40.76624975651346,
+ lng: -73.81444335286128,
+ },
+ address: '35-02 150th Pl, Flushing, NY 11354',
+ },
+ {
+ name: 'Santos',
+ icon: 'rice_bowl',
+ coords: {
+ lat: 40.74504671877868,
+ lng: -73.8880099638491,
+ },
+ address: '77-08 Broadway, Elmhurst, NY 11373'
+ },
+ {
+ name: 'Natalyaa (Qns)',
+ icon: 'currency_ruble',
+ coords: {
+ lat: 40.69644966409178,
+ lng: -73.837453217826,
+ },
+ address: '110-14 Jamaica Ave, Richmond Hill, NY 11418',
+ },
+ {
+ name: 'Natalyaa (Bronx)',
+ icon: 'currency_ruble',
+ coords: {
+ lat: 40.85384419116598,
+ lng: -73.86314767911834,
+ },
+ address: '2109 Matthews Ave, Bronx, NY 10462',
+ },
+ {
+ name: 'Linwood Plaza',
+ icon: 'dermatology',
+ coords: {
+ lat: 40.86141832913106,
+ lng: -73.96997583196286,
+ },
+ address: '158 Linwood Plaza, Fort Lee, NJ 07024',
+ },
+ ],
+ },
+ {
+ name: 'Man Mini Storage',
+ icon: 'box',
+ coords: {
+ lat: 40.75158955085288,
+ lng: -73.9328988710467,
+ },
+ address: '31-08 Northern Blvd, Long Island City, NY 11101',
+ },
+ {
+ name: 'Acmd',
+ icon: 'grocery',
+ coords: {
+ lat: 40.90930366920829,
+ lng: -73.87658695470259,
+ },
+ address: '31-08 Northern Blvd, Long Island City, NY 11101',
+ },
+];
diff --git a/src/functions/routingControl.ts b/src/functions/routingControl.ts
new file mode 100644
index 0000000..5d2e050
--- /dev/null
+++ b/src/functions/routingControl.ts
@@ -0,0 +1,89 @@
+import { Utilities } from "@vue-leaflet/vue-leaflet";
+import type * as L from "leaflet";
+import type { IRouter, IGeocoder, LineOptions } from "leaflet-routing-machine";
+
+// Props typing
+export interface RoutingControlProps {
+ waypoints: L.LatLng[];
+ router?: IRouter;
+ plan?: any; // L.Routing.Plan (can refine if you typed it)
+ fitSelectedRoutes?: string | boolean;
+ lineOptions?: LineOptions;
+ routeLine?: (...args: any[]) => any;
+ autoRoute?: boolean;
+ routeWhileDragging?: boolean;
+ routeDragInterval?: number;
+ waypointMode?: string;
+ useZoomParameter?: boolean;
+ showAlternatives?: boolean;
+ altLineOptions?: LineOptions;
+}
+
+// Vue-style prop definitions (still needed for runtime)
+export const routingControlProps = {
+ waypoints: {
+ type: Array as () => L.LatLng[],
+ default: () => [],
+ },
+ router: {
+ type: Object as () => IRouter,
+ default: undefined,
+ },
+ plan: {
+ type: Object as () => any,
+ default: undefined,
+ },
+ fitSelectedRoutes: {
+ type: [String, Boolean] as () => string | boolean,
+ default: "smart",
+ },
+ lineOptions: {
+ type: Object as () => LineOptions,
+ default: undefined,
+ },
+ routeLine: {
+ type: Function as () => (...args: any[]) => any,
+ default: undefined,
+ },
+ autoRoute: {
+ type: Boolean,
+ default: true,
+ },
+ routeWhileDragging: {
+ type: Boolean,
+ default: false,
+ },
+ routeDragInterval: {
+ type: Number,
+ default: 500,
+ },
+ waypointMode: {
+ type: String,
+ default: "connect",
+ },
+ useZoomParameter: {
+ type: Boolean,
+ default: false,
+ },
+ showAlternatives: {
+ type: Boolean,
+ default: false,
+ },
+ altLineOptions: {
+ type: Object as () => LineOptions,
+ default: undefined,
+ },
+};
+
+// Setup function
+export const setupRoutingControl = (props: RoutingControlProps) => {
+ const options = Utilities.propsToLeafletOptions(
+ props,
+ routingControlProps
+ );
+
+ return {
+ options,
+ methods: {} as Record,
+ };
+};
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index 8caff60..e263030 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -1,5 +1,5 @@
-
+
@@ -16,7 +16,12 @@
-
+
@@ -36,12 +41,16 @@
diff --git a/src/pages/DeviceInfo.vue b/src/pages/DeviceInfo.vue
new file mode 100644
index 0000000..4b37411
--- /dev/null
+++ b/src/pages/DeviceInfo.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
Device Info:
+
+ -
+ {{ key }}:
+
+
+ -
+ {{ subKey }}:
+
+
+ -
+ {{ subSubKey }}:
+
+
+ -
+ {{ subSubSubKey }}: {{ subSubSubValue }}
+
+
+
+ {{ subSubValue }}
+
+
+
+ {{ subValue }}
+
+
+
+ {{ value }}
+
+
+
+
+
+
+
diff --git a/src/router/routes.ts b/src/router/routes.ts
index 7d15658..ac9ef02 100644
--- a/src/router/routes.ts
+++ b/src/router/routes.ts
@@ -22,6 +22,11 @@ const routes: RouteRecordRaw[] = [
name: 'Test',
component: () => import('pages/TestPage.vue')
},
+ {
+ path: 'device-info',
+ name: 'DeviceInfo',
+ component: () => import('pages/DeviceInfo.vue'),
+ },
],
},
diff --git a/src/stores/socketio.ts b/src/stores/socketio.ts
new file mode 100644
index 0000000..ad51188
--- /dev/null
+++ b/src/stores/socketio.ts
@@ -0,0 +1,233 @@
+import { defineStore, acceptHMRUpdate } from 'pinia';
+import { socket } from 'boot/socketio';
+import type {
+ CurrentLocation,
+ NextLocation,
+ ErrorFull,
+ SimulationControl,
+ SimulationCommands,
+ LocationQueue,
+ SimulationControlResponse,
+ StatusUpdate
+} from 'components/models';
+
+
+
+
+export const useSocketioStore = defineStore('socketio', {
+ state: () => {
+ return {
+ sockConnected: false as boolean,
+ socketID: null as string | null | undefined,
+ deviceConnected: false as boolean,
+ tunnelConnected: false as boolean,
+ simulationRunning: false as boolean | string,
+ simulationState: null as string | null | undefined,
+ simulationQueneLength: 0 as number,
+ currentLocation: null as CurrentLocation | null,
+ nextLocation: null as NextLocation | null,
+ messageList: [''] as string[],
+ errorList: [] as ErrorFull[],
+ locationQueue: [] as LocationQueue[],
+ leafLetZoom: 10 as number,
+
+ };
+ },
+ getters: {
+ sockState: (state) => state.sockConnected,
+ deviceState: (state) => state.deviceConnected,
+ markerLatLng: (state) => {
+ return [state.currentLocation.latitude, state.currentLocation.longitude]
+ },
+ center(): [number, number] {
+ return this.leafletCurrentMarker
+ },
+ zoom: (state) => state.leafletZoom,
+ },
+ actions: {
+ setSockStatus() {
+ this.sockConnected = socket.connected;
+ this.socketID = socket.id;
+ },
+ bindEvents() {
+ this.setSockStatus();
+ socket.on('connect', () => {
+ this.setSockStatus();
+ socket.emit('message', 'Hello from client', (e: boolean) => {
+ console.log('Message delivered: ' + e);
+ });
+ console.log('Connected to server');
+ });
+
+ socket.on('disconnect', () => {
+ this.setSockStatus();
+ console.log('Disconnected from server');
+ });
+
+ socket.on('message', (e: string) => {
+ this.setSockStatus();
+ this.messageList.push(e);
+ console.log('Websock message received!');
+ });
+
+ socket.on('error', (data: ErrorFull) => {
+ this.setSockStatus();
+ const errorFull = { type: data.type, error: data.error };
+ this.errorList.push(errorFull);
+ });
+
+ socket.on('status', (data: StatusUpdate): void => {
+ console.log("StatusUpdate received: ", data);
+ this.simulationRunning = data.simulation_active;
+ this.simulationState = data.queue_state;
+ this.simulationQueneLength = data.quene_length;
+ this.tunnelConnected = !!data.tunnel;
+ this.currentLocation = { loc_id: data.loc_id, latitude: data.latitude, longitude: data.longitude, next_move: data.next_mode };
+ });
+
+ socket.onAny((eventName, ...args) => {
+ this.setSockStatus();
+ console.log(`Received event: ${eventName}`, args);
+ });
+ },
+ connect() {
+ console.log('Connecting to server...');
+ socket.connect();
+ this.setSockStatus();
+ },
+ disconnect() {
+ socket.disconnect();
+ this.setSockStatus();
+ },
+ toggleSock() {
+ this.setSockStatus();
+ if (this.sockConnected) {
+ socket.disconnect();
+ } else {
+ socket.connect();
+ }
+ this.setSockStatus();
+ },
+ setSimulationRunning(isRunning: boolean, states: string ): void {
+ this.setSockStatus();
+ this.simulationState = states;
+ this.simulationRunning = isRunning;
+ },
+ simulationControl(command: SimulationCommands, delay?: number, latitude?: number | null, longitude?: number | null): string | never {
+ this.setSockStatus();
+ switch (command) {
+ case 'start':
+ console.log("socketStore: got command: start");
+ if (this.simulationRunning || this.simulationState == "RUNNING" || this.simulationState == "PAUSED") {
+ throw new Error('Simulation is already running' + this.simulationState);
+ }
+ console.log("Emmitting simulation_control: start")
+ socket.emit('simulation_control', { command: 'start', delay: 0, latitude: null, longitude: null}, (response: SimulationControlResponse) => {
+ if (response.status == "error") {
+ throw new Error(response.message);
+ } else {
+ this.simulationState = response.status;
+ console.log(response.message);
+ return response.message;
+ }
+ });
+ break;
+ case 'pause':
+ if (this.simulationState !== "RUNNING" ) {
+ throw new Error('Simulation is not running');
+ }
+ socket.emit('simulation_control', { command: 'pause' }, (response: SimulationControlResponse) => {
+ if (response.status === "error") {
+ throw new Error(response.message);
+ } else {
+ this.simulationState = response.status;
+ console.log(response.message);
+ return response.message;
+ }
+ });
+ break;
+ case 'resume':
+ if (this.simulationState !== "PAUSED") {
+ throw new Error('Simulation is not paused');
+ }
+ socket.emit('simulation_control', { command: 'resume' }, (response) => {
+ if (response.status == "error") {
+ throw new Error(response.message)
+ } else {
+ this.simulationState = response.status;
+ console.log(response.message)
+ return response.message
+ }
+ });
+ break;
+ case 'clear':
+ if (this.simulationQueueLength== 0) {
+ throw new Error('Simulation queue is empty');
+ }
+ if (this.simulationState == "STOPPED " || !this.simulationRunning) {
+ throw new Error('Simulation is not running');
+ }
+ socket.emit('simulation_control', { command: 'clear' }, (response) => {
+ if (response.status == 'error') {
+ throw new Error(response.message);
+ } else {
+ this.simulationState = response.status;
+ console.log(response.message);
+ return response.message;
+ }
+ });
+ break;
+ case 'end':
+ if (this.simulationState == "ENDED" || !this.simulationRunning) {
+ throw new Error('Simulation is already ended');
+ }
+ socket.emit('simulation_control', { command: 'end' }, (response) => {
+ if (response.status == 'error') {
+ throw new Error(response.message);
+ } else {
+ this.simulationState = response.status;
+ console.log(response.message);
+ return response.message;
+ }
+ });
+ break;
+ case 'add':
+ if (this.simulationState == "ENDED" || !this.simulationRunning) {
+ throw new Error('Simulation is not running');
+ }
+ if (!latitude || !longitude) {
+ throw new Error ("latitude or longitude not set");
+ }
+ socket.emit('simulation_control',{ command: 'add', latitude: latitude, longitude: longitude, delay: delay }, (response) => {
+ if (response.status == "error") {
+ throw new Error(response.message)
+ } else {
+ this.simulationState = response.status;
+ console.log("response from simulate_control_add: ", response);
+ const locMrk = {
+ [response.loc_id]: {
+ loc_id: response.loc_id,
+ latitude: response.latitude,
+ longitude: response.longitude,
+ delay: response.delay,
+ start_time: response.start_time,
+ },
+ };
+ this.locationQueue.push(locMrk);
+ return response.message;
+ }
+ });
+ break;
+ default:
+ throw new Error('Invalid command');
+ }
+ },
+ setDeviceState(state: boolean) {
+ this.deviceConnected = state;
+ },
+ },
+});
+
+if (import.meta.hot) {
+ import.meta.hot.accept(acceptHMRUpdate(useSocketioStore, import.meta.hot));
+}
diff --git a/src/stores/status.ts b/src/stores/status.ts
deleted file mode 100644
index 76a3bbd..0000000
--- a/src/stores/status.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { acceptHMRUpdate, defineStore } from 'pinia';
-import { socket } from 'boot/socket';
-
-export const useStatusStore = defineStore('status', {
- state: () => ({
- device: {},
- statusList: {
- socketConnected: false,
- deviceConnected: false,
- tunnelConnected: false,
- simulationRunning: false,
- },
- }),
- actions: {
- bindEvents() {
- socket.on('connect', () => {
- this.statusList.socketConnected = true;
- });
- socket.on('disconnect', () => {
- this.statusList.socketConnected = false;
- });
- socket.on('status_update', (data) => {
- this.$patch((state) => {
- Object.assign(state.device, data);
- });
- });
- },
- socketConnect() {
- socket.connect();
- },
- socketDisconnect() {
- socket.disconnect();
- },
- },
-});
-
-if (import.meta.hot) {
- import.meta.hot.accept(acceptHMRUpdate(useStatusStore, import.meta.hot));
-}
diff --git a/src/types/leaflet-routing-machine.d.ts b/src/types/leaflet-routing-machine.d.ts
new file mode 100644
index 0000000..75c90a6
--- /dev/null
+++ b/src/types/leaflet-routing-machine.d.ts
@@ -0,0 +1,15 @@
+declare module "leaflet-routing-machine" {
+ import * as L from "leaflet";
+
+ export interface IRouter {}
+ export interface IGeocoder {}
+ export interface LineOptions extends L.PolylineOptions {}
+
+ export namespace Routing {
+ function control(options: any): any;
+ class Plan {}
+ }
+
+ const Routing: typeof Routing;
+ export default Routing;
+}