Path: blob/master/gate-controller/components/AddRelayDialog.tsx
1072 views
'use client';12import { useState, useEffect } from "react";3import { invoke } from "@tauri-apps/api/core";4import { Button } from "@/components/ui/button";5import {6Dialog,7DialogContent,8DialogDescription,9DialogHeader,10DialogTitle,11} from "@/components/ui/dialog";12import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";13import {14Select,15SelectContent,16SelectItem,17SelectTrigger,18SelectValue,19} from "@/components/ui/select";20import { Input } from "@/components/ui/input";21import { toast } from "sonner";2223interface UsbRelayInfo {24serial_number: string;25relay_type: string;26}2728interface AddRelayDialogProps {29open: boolean;30onOpenChange: (open: boolean) => void;31onRelayAdded: () => void;32}3334const getChannelCount = (relayType: string | undefined): number => {35if (!relayType) return 0;36const digitMatch = relayType.match(/(\d+)Channel/);37if (digitMatch) return parseInt(digitMatch[1], 10);38const wordMap: { [key: string]: number } = {39One: 1,40Two: 2,41Four: 4,42Eight: 8,43};44for (const word in wordMap) {45if (relayType.startsWith(word)) return wordMap[word];46}47return 0;48};4950export function AddRelayDialog({51open,52onOpenChange,53onRelayAdded,54}: AddRelayDialogProps) {55const [availablePorts, setAvailablePorts] = useState<string[]>([]);56const [selectedPort, setSelectedPort] = useState<string>("");5758const [availableUsbRelays, setAvailableUsbRelays] = useState<UsbRelayInfo[]>(59[],60);61const [selectedUsbRelay, setSelectedUsbRelay] = useState<UsbRelayInfo | null>(62null,63);6465const [ch340Channels, setCh340Channels] = useState(1);66const [cp210xChannels, setCp210xChannels] = useState(1);6768const refreshSerialPorts = async () => {69try {70const ports = await invoke<string[]>("list_serial_ports");71setAvailablePorts(ports);72if (ports.length > 0) setSelectedPort(ports[0]);73} catch (error) {74toast.error("Failed to list serial ports", {75description: String(error),76});77}78};7980const refreshUsbRelays = async () => {81try {82const relays = await invoke<UsbRelayInfo[]>("list_hw348_relays");83setAvailableUsbRelays(relays);84if (relays.length > 0) setSelectedUsbRelay(relays[0]);85} catch (error) {86toast.error("Failed to list HW-348 relays", { description: String(error) });87}88};8990useEffect(() => {91if (open) {92refreshSerialPorts();93refreshUsbRelays();94}95}, [open]);9697const handleAddCh340 = async () => {98if (!selectedPort) {99toast.warning("Please select a serial port.");100return;101}102if (ch340Channels <= 0) {103toast.warning("Number of channels must be greater than 0.");104return;105}106try {107await invoke("add_ch340_relay", { port: selectedPort, channels: ch340Channels });108toast.success("CH340 Relay Added", {109description: `Port ${selectedPort} has been configured with ${ch340Channels} channels.`,110});111onRelayAdded();112onOpenChange(false);113} catch (error) {114toast.error("Failed to add CH340 relay", { description: String(error) });115}116};117118const handleAddHw348 = async () => {119if (!selectedUsbRelay) {120toast.warning("Please select a USB relay.");121return;122}123try {124const channels = getChannelCount(selectedUsbRelay.relay_type);125await invoke("add_hw348_relay", {126serialNumber: selectedUsbRelay.serial_number,127channels,128});129toast.success("HW-348 Relay Added", {130description: `Relay ${selectedUsbRelay.serial_number} has been configured.`,131});132onRelayAdded();133onOpenChange(false);134} catch (error) {135toast.error("Failed to add HW-348 relay", { description: String(error) });136}137};138139const handleAddCp210x = async () => {140if (!selectedPort) {141toast.warning("Please select a serial port.");142return;143}144if (cp210xChannels <= 0) {145toast.warning("Number of channels must be greater than 0.");146return;147}148try {149await invoke("add_cp210x_relay", { port: selectedPort, channels: cp210xChannels });150toast.success("CP210x Relay Added", {151description: `Port ${selectedPort} has been configured with ${cp210xChannels} channels.`,152});153onRelayAdded();154onOpenChange(false);155} catch (error) {156toast.error("Failed to add CP210x relay", { description: String(error) });157}158};159160return (161<Dialog open={open} onOpenChange={onOpenChange}>162<DialogContent className="sm:max-w-[425px]">163<DialogHeader>164<DialogTitle>Add a New Relay</DialogTitle>165<DialogDescription>166Choose the chip of your USB relay you want to configure.167</DialogDescription>168</DialogHeader>169<Tabs defaultValue="ch340" className="w-full">170<TabsList className="grid w-full grid-cols-3">171<TabsTrigger value="ch340">CH340</TabsTrigger>172<TabsTrigger value="hw348">HW-348</TabsTrigger>173<TabsTrigger value="cp210x">CP210x</TabsTrigger>174</TabsList>175<TabsContent value="ch340">176<div className="space-y-4 py-4">177<div className="flex items-center gap-4">178<Select value={selectedPort} onValueChange={setSelectedPort}>179<SelectTrigger className="w-full">180<SelectValue placeholder="Select a serial port..." />181</SelectTrigger>182<SelectContent>183{availablePorts.map((port) => (184<SelectItem key={port} value={port}>185{port}186</SelectItem>187))}188</SelectContent>189</Select>190<Button variant="outline" onClick={refreshSerialPorts}>191Refresh192</Button>193</div>194<div className="flex items-center gap-4">195<label htmlFor="channels-ch340" className="text-sm font-medium">Channels</label>196<Input197id="channels-ch340"198type="number"199value={ch340Channels}200onChange={(e) => setCh340Channels(parseInt(e.target.value, 10) || 1)}201className="w-full"202min={1}203/>204</div>205<Button206className="w-full"207onClick={handleAddCh340}208disabled={!selectedPort}209>210Add Relay211</Button>212</div>213</TabsContent>214<TabsContent value="hw348">215<div className="space-y-4 py-4">216<div className="flex items-center gap-4">217<Select218value={selectedUsbRelay?.serial_number || ""}219onValueChange={(sn) =>220setSelectedUsbRelay(221availableUsbRelays.find((r) => r.serial_number === sn) ||222null,223)224}225>226<SelectTrigger className="w-full">227<SelectValue placeholder="Select a USB relay..." />228</SelectTrigger>229<SelectContent>230{availableUsbRelays.map((r) => (231<SelectItem key={r.serial_number} value={r.serial_number}>232{r.serial_number} ({r.relay_type})233</SelectItem>234))}235</SelectContent>236</Select>237<Button variant="outline" onClick={refreshUsbRelays}>238Refresh239</Button>240</div>241<Button242className="w-full"243onClick={handleAddHw348}244disabled={!selectedUsbRelay}245>246Add Relay247</Button>248</div>249</TabsContent>250<TabsContent value="cp210x">251<div className="space-y-4 py-4">252<div className="flex items-center gap-4">253<Select value={selectedPort} onValueChange={setSelectedPort}>254<SelectTrigger className="w-full">255<SelectValue placeholder="Select a serial port..." />256</SelectTrigger>257<SelectContent>258{availablePorts.map((port) => (259<SelectItem key={port} value={port}>260{port}261</SelectItem>262))}263</SelectContent>264</Select>265<Button variant="outline" onClick={refreshSerialPorts}>266Refresh267</Button>268</div>269<div className="flex items-center gap-4">270<label htmlFor="channels" className="text-sm font-medium">Channels</label>271<Input272id="channels"273type="number"274value={cp210xChannels}275onChange={(e) => setCp210xChannels(parseInt(e.target.value, 10) || 1)}276className="w-full"277min={1}278/>279</div>280<Button281className="w-full"282onClick={handleAddCp210x}283disabled={!selectedPort}284>285Add Relay286</Button>287</div>288</TabsContent>289</Tabs>290</DialogContent>291</Dialog>292);293}294295296