From e2d8f74823c7139ce1ccd0831876e361fcd6c419 Mon Sep 17 00:00:00 2001
From: Paul Oliver <contact@pauloliver.dev>
Date: Sat, 3 Jan 2026 16:05:41 +0000
Subject: Adds motor control to frontend

---
 hsm-web/Client/src/MotorCtl.vue | 98 +++++++++++++++++++++++++++++++++--------
 1 file changed, 79 insertions(+), 19 deletions(-)

(limited to 'hsm-web/Client/src/MotorCtl.vue')

diff --git a/hsm-web/Client/src/MotorCtl.vue b/hsm-web/Client/src/MotorCtl.vue
index b1c53b2..d7d026e 100644
--- a/hsm-web/Client/src/MotorCtl.vue
+++ b/hsm-web/Client/src/MotorCtl.vue
@@ -4,27 +4,37 @@
     <tbody>
       <tr>
         <td>
-          <button :id='ccw' class='bmot'>{{ dirIcons.ccw }}</button>
+          <button :class='bdirClass("ccw")' @click='nextDir = "ccw"' :disabled='armed'>
+            {{ dirIcons.ccw }}
+          </button>
         </td>
         <td>
           <table id='tmot'>
             <tbody>
               <tr v-for='ds in [["nw", "n", "ne"], ["w", "", "e"], ["sw", "s", "se"]]' :key='ds.id'>
                 <td v-for='d in ds' :key='d.id'>
-                  <button v-if='d' :id='d' class='bmot'>{{ dirIcons[d] }}</button>
+                  <button v-if='d' :class='bdirClass(d)' @click='nextDir = d' :disabled='armed'>
+                    {{ dirIcons[d] }}
+                  </button>
                 </td>
               </tr>
             </tbody>
           </table>
         </td>
         <td>
-          <button :id='cw' class='bmot'>{{ dirIcons.cw }}</button>
+          <button :class='bdirClass("cw")' @click='nextDir = "cw"' :disabled='armed'>
+            {{ dirIcons.cw }}
+          </button>
         </td>
         <td>
           <table id='tspeed'>
             <tbody>
-              <tr v-for='s in ["Top", "Fast", "Slow", "Slow2", "Slow4"]' :key='s.id'>
-                <td><button id='s{{ s.toLowerCase() }}'>{{ s }}</button></td>
+              <tr v-for='s in ["top", "fast", "slow", "slow2", "slow4"]' :key='s.id'>
+                <td>
+                  <button :class='bspeedClass(s)' @click='nextSpeed = s' :disabled='armed'>
+                    {{ s }}
+                  </button>
+                </td>
               </tr>
             </tbody>
           </table>
@@ -32,8 +42,12 @@
         <td>
           <table id='ttime'>
             <tbody>
-              <tr v-for='t in ["4s", "2s", "1s", "hs", "qs"]' :key='t.id'>
-                <td><button id='t{{ t }}'>{{ t }}</button></td>
+              <tr v-for='t in ["4s", "2s", "1s", "0.5s", "0.25s"]' :key='t.id'>
+                <td>
+                  <button :class='btimeClass(t)' @click='nextTime = t' :disabled='armed'>
+                    {{ t }}
+                  </button>
+                </td>
               </tr>
             </tbody>
           </table>
@@ -41,28 +55,27 @@
       </tr>
     </tbody>
   </table>
-  <table id='thist'>
+  <table id='tcmd'>
     <tbody>
       <tr>
-        <td><input id='ihist' placeholder='command' disabled /></td>
-        <td v-for='h in ["hup", "hdown"]' :key='h.id'>
-          <button id='{{ h }}'>{{ h }}</button>
-        </td>
+        <td><input id='ihist' :value='renderCommand()' disabled /></td>
       </tr>
     </tbody>
   </table>
   <table id='tarm'>
     <tbody>
       <tr>
-        <td v-for='a in ["arm", "dispatch"]' :key='a.id'>
-          <button id='{{ a }}'>{{ a }}</button>
-        </td>
+        <td><button id='arm' @click='armed = !armed' :disabled='running'>{{ armed ? 'disarm' : 'arm' }}</button></td>
+        <td><button id='dispatch' @click='dispatch()' :disabled='!armed || running'>dispatch</button></td>
       </tr>
     </tbody>
   </table>
 </template>
 
 <script>
+import axios  from 'axios'
+import config from './config'
+
 export default {
   data() {
     return {
@@ -77,12 +90,42 @@ export default {
         nw:  '↖',
         ccw: '↺',
         cw:  '↻'
-      }
+      },
+      nextDir:   'n',
+      nextSpeed: 'slow',
+      nextTime:  '1s',
+      armed:     false,
+      running:   false
     }
   },
-  mounted() {
-  },
   methods: {
+    bdirClass(d) {
+      return 'bmot' + (d == this.nextDir ? ' bhigh' : '')
+    },
+    bspeedClass(d) {
+      return d == this.nextSpeed ? 'bhigh' : ''
+    },
+    btimeClass(t) {
+      return t == this.nextTime ? 'bhigh' : ''
+    },
+    renderCommand() {
+      const cmd   =  ['ccw', 'cw'].includes(this.nextDir) ? 'Tilt' : 'Move'
+      const dir   = this.nextDir.toUpperCase()
+      const speed = this.nextSpeed.charAt(0).toUpperCase() + this.nextSpeed.slice(1)
+      const time  = this.nextTime.slice(0, -1)
+
+      return `${cmd} ${dir} ${speed} ${time}`
+    },
+    async dispatch() {
+      this.running = true
+
+      const res = await axios.get(config.api + '/command?cmd=' + this.renderCommand())
+
+      if (res.status == 200) {
+        this.armed   = false
+        this.running = false
+      }
+    }
   }
 }
 </script>
@@ -92,11 +135,28 @@ export default {
   height: 33%;
   width:  33%;
 }
-#tmain td, #thist td {
+#tmain td, #tcmd td {
   background-color: #2aa198;
 }
 .bmot {
   font-size:   16px;
   font-weight: bold;
 }
+.bhigh {
+  background-color: #b58900;
+  color:            #073642;
+}
+#tarm td:nth-child(1) {
+  width: 30%;
+}
+#arm {
+  background-color: #dc322f;
+}
+#dispatch {
+  background-color: #859900;
+}
+#arm, #dispatch {
+  color:       #073642;
+  font-weight: bold;
+}
 </style>
-- 
cgit v1.2.3-70-g09d2

