Files
yovision/Sense/cmd/sense-lab/main.go
T
QiuSW 9567838045
Harness governance / validate (push) Has been cancelled
Harness governance / validate (pull_request) Has been cancelled
feat(sense): complete T-006 five-stream integration
2026-08-07 16:33:35 +08:00

144 lines
4.2 KiB
Go

package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"os"
"yovision/sense/internal/device"
"yovision/sense/internal/store"
)
type manifest struct {
Site manifestSite `json:"site"`
Devices []manifestDevice `json:"devices"`
}
type manifestSite struct {
TenantID string `json:"tenant_id"`
ID string `json:"id"`
Name string `json:"name"`
MaxVideoChannels int `json:"max_video_channels"`
}
type manifestDevice struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
SiteID string `json:"site_id"`
SerialNumber string `json:"serial_number"`
Name string `json:"name"`
Capabilities []string `json:"capabilities"`
EndpointRef string `json:"endpoint_ref"`
CredentialRef string `json:"credential_ref"`
PathName string `json:"path_name"`
}
func main() {
if err := run(os.Args[1:], os.Stdout); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run(args []string, output io.Writer) error {
if len(args) == 0 {
return errors.New("usage: sense-lab <seed|status>")
}
switch args[0] {
case "seed":
return seed(args[1:], output)
case "status":
return status(args[1:], output)
default:
return fmt.Errorf("unknown command %q", args[0])
}
}
func seed(args []string, output io.Writer) error {
flags := flag.NewFlagSet("seed", flag.ContinueOnError)
flags.SetOutput(io.Discard)
dsn := flags.String("db", "", "SQLite DSN")
manifestPath := flags.String("manifest", "", "manifest JSON path")
if err := flags.Parse(args); err != nil {
return err
}
if *dsn == "" || *manifestPath == "" {
return errors.New("seed requires -db and -manifest")
}
file, err := os.Open(*manifestPath)
if err != nil {
return fmt.Errorf("open manifest: %w", err)
}
defer file.Close()
var value manifest
decoder := json.NewDecoder(io.LimitReader(file, 1<<20))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&value); err != nil {
return fmt.Errorf("decode manifest: %w", err)
}
repository, err := store.OpenSQLite(context.Background(), *dsn)
if err != nil {
return err
}
defer repository.Close()
ctx := context.Background()
if err := repository.EnsureSite(ctx, device.Site{
TenantID: value.Site.TenantID, ID: value.Site.ID, Name: value.Site.Name,
MaxVideoChannels: value.Site.MaxVideoChannels,
}); err != nil {
return err
}
for _, input := range value.Devices {
capabilities := make([]device.Capability, 0, len(input.Capabilities))
for _, capability := range input.Capabilities {
capabilities = append(capabilities, device.Capability(capability))
}
if err := repository.CreateDevice(ctx, device.Device{
ID: input.ID, TenantID: input.TenantID, SiteID: input.SiteID,
SerialNumber: input.SerialNumber, Name: input.Name, Modality: device.ModalityVideo,
Capabilities: capabilities, DesiredState: device.DesiredEnabled, ActualState: device.ActualPending,
EndpointRef: input.EndpointRef, CredentialRef: input.CredentialRef, PathName: input.PathName,
}); err != nil {
return fmt.Errorf("create device %s: %w", input.ID, err)
}
}
return json.NewEncoder(output).Encode(map[string]int{"seeded": len(value.Devices)})
}
func status(args []string, output io.Writer) error {
flags := flag.NewFlagSet("status", flag.ContinueOnError)
flags.SetOutput(io.Discard)
dsn := flags.String("db", "", "SQLite DSN")
expect := flags.Int("expect", -1, "expected device count")
requireConverged := flags.Bool("require-converged", false, "fail when unconverged is non-zero")
if err := flags.Parse(args); err != nil {
return err
}
if *dsn == "" {
return errors.New("status requires -db")
}
repository, err := store.OpenSQLite(context.Background(), *dsn)
if err != nil {
return err
}
defer repository.Close()
snapshot, err := repository.ConvergenceSnapshot(context.Background())
if err != nil {
return err
}
if err := json.NewEncoder(output).Encode(snapshot); err != nil {
return err
}
if *expect >= 0 && snapshot.Total != *expect {
return fmt.Errorf("expected %d devices, got %d", *expect, snapshot.Total)
}
if *requireConverged && snapshot.Unconverged != 0 {
return fmt.Errorf("unconverged devices: %d", snapshot.Unconverged)
}
return nil
}