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 }