jim800121chen c54f16fca0 Initial commit: visionA monorepo with local-tool subproject
local-tool/: visionA-local desktop app
- M1: Wails shell + Go server + Next.js UI + Mock mode (macOS dmg ready)
- M2: i18n (zh-TW/en) + Settings 4-tab refactor
- M3: Embedded Python 3.12 runtime (python-build-standalone) + KneronPLUS wheels
- M4: Windows Inno Setup script (build on Windows runner)
- M5: Linux AppImage script + udev rule (build on Linux runner)
- M6: ffmpeg (GPL, pending legal review) + yt-dlp bundled
- Lifecycle: watchServer health check, fatal native dialog,
            Wails IPC raise endpoint, stale process cleanup

.autoflow/: full PRD / Design Spec / Architecture / Testing docs
            (4 rounds tri-party discussion + cross review)
.github/workflows/: macOS / Windows / Linux build CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:10:38 +08:00

184 lines
4.2 KiB
Go

package mock
import (
"fmt"
"math/rand"
"sync"
"time"
"visiona-local/server/internal/driver"
)
var mockLabels = []string{"person", "car", "bicycle", "dog", "cat", "chair", "bottle", "phone"}
type MockDriver struct {
info driver.DeviceInfo
connected bool
inferring bool
modelLoaded string
mu sync.Mutex
}
func NewMockDriver(info driver.DeviceInfo) *MockDriver {
return &MockDriver{info: info}
}
func Factory(id string, index int) driver.DeviceDriver {
info := driver.DeviceInfo{
ID: id,
Name: fmt.Sprintf("Kneron KL720 (Mock #%d)", index+1),
Type: "kneron_kl720",
Port: fmt.Sprintf("/dev/ttyMOCK%d", index),
Status: driver.StatusDetected,
FirmwareVer: "2.2.0-mock",
}
return NewMockDriver(info)
}
func (d *MockDriver) Info() driver.DeviceInfo {
d.mu.Lock()
defer d.mu.Unlock()
return d.info
}
func (d *MockDriver) Connect() error {
d.mu.Lock()
defer d.mu.Unlock()
time.Sleep(200 * time.Millisecond)
d.connected = true
d.info.Status = driver.StatusConnected
return nil
}
func (d *MockDriver) Disconnect() error {
d.mu.Lock()
defer d.mu.Unlock()
d.connected = false
d.inferring = false
d.info.Status = driver.StatusDisconnected
return nil
}
func (d *MockDriver) IsConnected() bool {
d.mu.Lock()
defer d.mu.Unlock()
return d.connected
}
func (d *MockDriver) Flash(modelPath string, progressCh chan<- driver.FlashProgress) error {
d.mu.Lock()
d.info.Status = driver.StatusFlashing
d.mu.Unlock()
type stage struct {
name string
duration time.Duration
startPct int
endPct int
}
stages := []stage{
{"preparing", 1 * time.Second, 0, 10},
{"transferring", 6 * time.Second, 10, 80},
{"verifying", 2 * time.Second, 80, 95},
{"rebooting", 1 * time.Second, 95, 99},
}
for _, s := range stages {
steps := (s.endPct - s.startPct) / 5
if steps < 1 {
steps = 1
}
interval := s.duration / time.Duration(steps)
for i := 0; i <= steps; i++ {
pct := s.startPct + (s.endPct-s.startPct)*i/steps
progressCh <- driver.FlashProgress{
Percent: pct,
Stage: s.name,
Message: fmt.Sprintf("%s... %d%%", s.name, pct),
}
time.Sleep(interval)
}
}
d.mu.Lock()
d.modelLoaded = modelPath
d.info.FlashedModel = modelPath
d.info.Status = driver.StatusConnected
d.mu.Unlock()
progressCh <- driver.FlashProgress{Percent: 100, Stage: "done", Message: "Flash complete"}
return nil
}
func (d *MockDriver) StartInference() error {
d.mu.Lock()
defer d.mu.Unlock()
d.inferring = true
d.info.Status = driver.StatusInferencing
return nil
}
func (d *MockDriver) StopInference() error {
d.mu.Lock()
defer d.mu.Unlock()
d.inferring = false
d.info.Status = driver.StatusConnected
return nil
}
func (d *MockDriver) ReadInference() (*driver.InferenceResult, error) {
return d.RunInference(nil)
}
func (d *MockDriver) RunInference(imageData []byte) (*driver.InferenceResult, error) {
time.Sleep(30 * time.Millisecond)
numDetections := rand.Intn(3) + 1
detections := make([]driver.DetectionResult, numDetections)
for i := 0; i < numDetections; i++ {
w := 0.1 + rand.Float64()*0.3
h := 0.1 + rand.Float64()*0.3
detections[i] = driver.DetectionResult{
Label: mockLabels[rand.Intn(len(mockLabels))],
Confidence: 0.3 + rand.Float64()*0.7,
BBox: driver.BBox{
X: rand.Float64() * (1 - w),
Y: rand.Float64() * (1 - h),
Width: w,
Height: h,
},
}
}
classifications := []driver.ClassResult{
{Label: "person", Confidence: 0.5 + rand.Float64()*0.5},
{Label: "car", Confidence: rand.Float64() * 0.5},
{Label: "dog", Confidence: rand.Float64() * 0.3},
{Label: "cat", Confidence: rand.Float64() * 0.2},
{Label: "bicycle", Confidence: rand.Float64() * 0.15},
}
return &driver.InferenceResult{
TaskType: "detection",
Timestamp: time.Now().UnixMilli(),
LatencyMs: 20 + rand.Float64()*30,
Detections: detections,
Classifications: classifications,
}, nil
}
func (d *MockDriver) GetModelInfo() (*driver.ModelInfo, error) {
d.mu.Lock()
defer d.mu.Unlock()
if d.modelLoaded == "" {
return nil, fmt.Errorf("no model loaded")
}
return &driver.ModelInfo{
ID: d.modelLoaded,
Name: d.modelLoaded,
LoadedAt: time.Now(),
}, nil
}