-
-
-
-
-
+
+
+ Connecting...
+
+
+
+
Current position:
diff --git a/toid_frontend/src/assets/scss/global.scss b/toid_frontend/src/assets/scss/global.scss
index 5b77908..d82263f 100644
--- a/toid_frontend/src/assets/scss/global.scss
+++ b/toid_frontend/src/assets/scss/global.scss
@@ -6,4 +6,5 @@ body,
min-height: 100vh;
max-width: 100vw;
background-color: #242130;
+ color: #ffffff;
}
diff --git a/toid_frontend/src/components/RobotControl.vue b/toid_frontend/src/components/RobotControl.vue
new file mode 100644
index 0000000..7114d52
--- /dev/null
+++ b/toid_frontend/src/components/RobotControl.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/toid_frontend/src/main.ts b/toid_frontend/src/main.ts
index 5c63039..2d4158c 100644
--- a/toid_frontend/src/main.ts
+++ b/toid_frontend/src/main.ts
@@ -3,34 +3,11 @@ import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
-import { ros } from './ros_client'
-
import '@/assets/scss/global.scss'
+import '@/ts/robot_bridge'
+import '@/ts/ros_client'
const app = createApp(App)
-
app.use(createPinia())
app.use(router)
-
app.mount('#app')
-
-ros.connect('http://localhost:3000')
-
-ros.on('error', (error) => {
- console.log(error)
- setTimeout(() => {
- ros.connect('http://localhost:3000').catch()
- }, 1000)
-})
-
-ros.on('close', () => {
- setTimeout(() => {
- if (!ros.isConnected) {
- ros.connect('http://localhost:3000').catch()
- }
- }, 1000)
-})
-
-ros.on('connection', () => {
- console.log('Connection made!')
-})
diff --git a/toid_frontend/src/ros_client.ts b/toid_frontend/src/ros_client.ts
deleted file mode 100644
index 18fc433..0000000
--- a/toid_frontend/src/ros_client.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import * as ROSLIB from 'roslib'
-
-interface BehaviorTreeList {
- tree_ids: Array
-}
-
-const ros = new ROSLIB.Ros()
-
-export { ros, type BehaviorTreeList }
diff --git a/toid_frontend/src/stores/connection-status.ts b/toid_frontend/src/stores/connection-status.ts
new file mode 100644
index 0000000..aba6bb5
--- /dev/null
+++ b/toid_frontend/src/stores/connection-status.ts
@@ -0,0 +1,8 @@
+import { ref } from 'vue'
+import { defineStore } from 'pinia'
+
+export const connectionStatus = defineStore('connstatus', () => {
+ const rosbridgeConnected = ref(false)
+ const backendConnected = ref(false)
+ return { rosbridgeConnected, backendConnected }
+})
diff --git a/toid_frontend/src/ts/robot_bridge.ts b/toid_frontend/src/ts/robot_bridge.ts
new file mode 100644
index 0000000..3e77d30
--- /dev/null
+++ b/toid_frontend/src/ts/robot_bridge.ts
@@ -0,0 +1,81 @@
+import { connectionStatus } from '@/stores/connection-status'
+
+class BackendBridge {
+ private static instance: BackendBridge
+ private readonly url: string = 'http://localhost:8000/action/ws'
+ private socket: WebSocket | null = null
+ private timeout: number | null = null
+
+ private reconnectAttempts = 0
+ private readonly maxAttempts = 10
+ private readonly baseDelay = 100
+
+ private constructor() {
+ this.connect()
+ }
+
+ public static getInstance(): BackendBridge {
+ if (!BackendBridge.instance) {
+ BackendBridge.instance = new BackendBridge()
+ }
+ return this.instance
+ }
+
+ public manualConnect() {
+ if (this.timeout) clearTimeout(this.timeout)
+ this.connect()
+ }
+
+ private connect(): void {
+ console.log(`Connecting to ${this.url}...`)
+ if (this.socket) {
+ connectionStatus().backendConnected = false
+ this.socket.onclose = null
+ this.socket.close()
+ }
+ this.socket = new WebSocket(this.url)
+
+ this.socket.onopen = () => {
+ connectionStatus().backendConnected = true
+ console.log('Connected successfully!')
+ this.reconnectAttempts = 0
+ }
+
+ this.socket.onclose = (event) => {
+ connectionStatus().backendConnected = false
+ console.warn('Socket closed:', event)
+ this.handleReconnect()
+ }
+
+ this.socket.onerror = (error) => {
+ console.error('Socket error:', error)
+ }
+ }
+
+ // Backoff algorithm
+ private handleReconnect(): void {
+ if (this.reconnectAttempts < this.maxAttempts) {
+ this.reconnectAttempts++
+
+ const delay = this.baseDelay * Math.pow(2, this.reconnectAttempts - 1)
+
+ console.log(
+ `Retrying in ${delay}ms... (Attempt ${this.reconnectAttempts}/${this.maxAttempts})`,
+ )
+
+ this.timeout = setTimeout(() => {
+ this.timeout = null
+ console.log(`Starting attempt [${this.reconnectAttempts}/${this.maxAttempts}]`)
+ this.connect()
+ }, delay)
+ } else {
+ console.error('Max reconnection attempts reached. Please check your connection.')
+ }
+ }
+}
+
+const rosBackend = BackendBridge.getInstance()
+
+export { rosBackend }
+
+export type { BackendBridge }
diff --git a/toid_frontend/src/ts/ros_client.ts b/toid_frontend/src/ts/ros_client.ts
new file mode 100644
index 0000000..e2ba80d
--- /dev/null
+++ b/toid_frontend/src/ts/ros_client.ts
@@ -0,0 +1,84 @@
+import * as ROSLIB from 'roslib'
+import { connectionStatus } from '@/stores/connection-status'
+
+interface BehaviorTreeList {
+ tree_ids: Array
+}
+
+class Ros {
+ private static instance: Ros
+ readonly ros: ROSLIB.Ros = new ROSLIB.Ros()
+ private readonly url: string = 'http://localhost:3000'
+ private timeout: number | null = null
+
+ private reconnectAttempts = 0
+ private readonly maxAttempts = 10
+ private readonly baseDelay = 100 // 1 second
+
+ private constructor() {
+ this.connect()
+ }
+
+ public static getInstance(): Ros {
+ if (!Ros.instance) {
+ Ros.instance = new Ros()
+ }
+ return Ros.instance
+ }
+
+ public reconnect() {
+ this.ros.connect(this.url)
+ }
+
+ public manualConnect() {
+ if (this.timeout) clearTimeout(this.timeout)
+ this.connect()
+ }
+
+ private connect(): void {
+ console.log(`Connecting to ${this.url}...`)
+
+ this.ros.on('connection', () => {
+ connectionStatus().rosbridgeConnected = true
+ console.log('Connected successfully!')
+ this.reconnectAttempts = 0
+ })
+
+ this.ros.on('close', (event: ROSLIB.TransportEvent) => {
+ connectionStatus().rosbridgeConnected = false
+ console.warn('Socket closed:', event)
+ this.handleReconnect()
+ })
+
+ this.ros.on('error', (error) => {
+ console.error('Socket error:', error)
+ })
+ this.ros.connect(this.url)
+ }
+
+ // Backoff algorithm
+ private handleReconnect(): void {
+ if (this.reconnectAttempts < this.maxAttempts) {
+ this.reconnectAttempts++
+
+ const delay = this.baseDelay * Math.pow(2, this.reconnectAttempts - 1)
+
+ console.log(
+ `Retrying in ${delay}ms... (Attempt ${this.reconnectAttempts}/${this.maxAttempts})`,
+ )
+
+ this.timeout = setTimeout(() => {
+ this.timeout = null
+ console.log(`Starting attempt [${this.reconnectAttempts}/${this.maxAttempts}]`)
+ this.ros.connect(this.url)
+ }, delay)
+ } else {
+ console.error('Max reconnection attempts reached. Please check your connection.')
+ }
+ }
+}
+
+// Usage:
+const ros = Ros.getInstance()
+
+export { ros, type BehaviorTreeList }