Add health check command and panorama control
- New `dwarfctl health` command: checks WebSocket connectivity, device state,
battery, storage, RGB, focus motor, both cameras, motor position status,
and CMOS temperature in one pass with clear OK/FAIL output
- Added `dwarfctl pano start|stop` for panorama grid control
- Added StartPanoramaGrid/StopPanorama to the API client
- Fixed CMOS temperature display (int32 not float64)
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@ -29,6 +29,7 @@ func main() {
|
|||||||
rootCmd.PersistentFlags().BoolVarP(&flagDebug, "debug", "d", false, "verbose WebSocket traffic log")
|
rootCmd.PersistentFlags().BoolVarP(&flagDebug, "debug", "d", false, "verbose WebSocket traffic log")
|
||||||
|
|
||||||
rootCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
|
cmdHealth(),
|
||||||
cmdState(),
|
cmdState(),
|
||||||
cmdCamera(),
|
cmdCamera(),
|
||||||
cmdMotor(),
|
cmdMotor(),
|
||||||
@ -71,6 +72,106 @@ func mustCam(s string) api.Camera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- health ---
|
||||||
|
|
||||||
|
func cmdHealth() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "health",
|
||||||
|
Short: "Full telescope health check (connectivity + device state)",
|
||||||
|
Run: func(_ *cobra.Command, _ []string) {
|
||||||
|
checks := 0
|
||||||
|
passed := 0
|
||||||
|
|
||||||
|
check := func(name string, ok bool, detail string) {
|
||||||
|
checks++
|
||||||
|
if ok {
|
||||||
|
passed++
|
||||||
|
fmt.Printf(" ✅ %-20s %s\n", name, detail)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ❌ %-20s %s\n", name, detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("DWARF Telescope Health Check")
|
||||||
|
fmt.Println(strings.Repeat("=", 50))
|
||||||
|
|
||||||
|
// 1. Connect
|
||||||
|
scope := api.New(flagClientID)
|
||||||
|
scope.SetDebug(flagDebug)
|
||||||
|
connectStart := time.Now()
|
||||||
|
err := scope.Connect(flagIP)
|
||||||
|
elapsed := time.Since(connectStart)
|
||||||
|
check("WebSocket :9900", err == nil, fmt.Sprintf("%s (%dms)", flagIP+":9900", elapsed.Milliseconds()))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("\nCannot connect. Aborting.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer scope.Close()
|
||||||
|
|
||||||
|
// 2. Device state
|
||||||
|
state, err := scope.GetDeviceState()
|
||||||
|
check("Device state query", err == nil, fmt.Sprintf("%d bytes", len(state.String())))
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
di := state.GetDeviceStateInfo()
|
||||||
|
|
||||||
|
// 3. Battery
|
||||||
|
batt := di.GetBatteryInfo().GetPercentage()
|
||||||
|
check("Battery", batt > 0, fmt.Sprintf("%d%%", batt))
|
||||||
|
|
||||||
|
// 4. Storage
|
||||||
|
stor := di.GetStorageInfo()
|
||||||
|
avail := stor.GetAvailableSize()
|
||||||
|
total := stor.GetTotalSize()
|
||||||
|
storOK := total > 0 && avail > 0 && stor.GetIsValid()
|
||||||
|
check("Storage", storOK, fmt.Sprintf("%d/%d GB (%s)", avail, total, func() string {
|
||||||
|
if storOK { return "valid" }
|
||||||
|
return "INVALID"
|
||||||
|
}()))
|
||||||
|
|
||||||
|
// 5. RGB state
|
||||||
|
rgb := di.GetRgbState().GetState()
|
||||||
|
check("RGB ring", true, fmt.Sprintf("state=%d", rgb))
|
||||||
|
|
||||||
|
// 6. Focus position
|
||||||
|
focus := state.GetFocusMotorStateInfo().GetFocusPosition().GetPos()
|
||||||
|
check("Focus motor", focus > 0, fmt.Sprintf("pos=%d", focus))
|
||||||
|
|
||||||
|
// 7. Tele camera
|
||||||
|
tele := state.GetTeleCameraStateInfo()
|
||||||
|
teleRes := fmt.Sprintf("%dx%d", tele.GetResolutionWidth(), tele.GetResolutionHeight())
|
||||||
|
check("Tele camera", tele.GetResolutionWidth() > 0, teleRes)
|
||||||
|
|
||||||
|
// 8. Wide camera
|
||||||
|
wide := state.GetWideCameraStateInfo()
|
||||||
|
wideRes := fmt.Sprintf("%dx%d", wide.GetResolutionWidth(), wide.GetResolutionHeight())
|
||||||
|
check("Wide camera", wide.GetResolutionWidth() > 0, wideRes)
|
||||||
|
|
||||||
|
// 9. Motor position (may be NEED_RESET)
|
||||||
|
pos, posErr := scope.MotorGetPosition(0)
|
||||||
|
if posErr != nil {
|
||||||
|
check("Motor position", false, posErr.Error())
|
||||||
|
} else if pos.GetCode() != 0 {
|
||||||
|
check("Motor position", false, fmt.Sprintf("code=%d (NEED_RESET)", pos.GetCode()))
|
||||||
|
} else {
|
||||||
|
check("Motor position", true, fmt.Sprintf("%.2f", pos.GetPosition()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. Temperature
|
||||||
|
cmosTemp := state.GetWideCameraStateInfo().GetCmosTemperature().GetTemperature()
|
||||||
|
check("CMOS temp", cmosTemp > -270, fmt.Sprintf("%d°C", cmosTemp))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(strings.Repeat("=", 50))
|
||||||
|
status := "HEALTHY"
|
||||||
|
if passed < checks {
|
||||||
|
status = "ISSUES DETECTED"
|
||||||
|
}
|
||||||
|
fmt.Printf("Result: %d/%d checks passed — %s\n", passed, checks, status)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- state ---
|
// --- state ---
|
||||||
|
|
||||||
func cmdState() *cobra.Command {
|
func cmdState() *cobra.Command {
|
||||||
|
|||||||
Reference in New Issue
Block a user