package daemon import ( "sync" "src.dualinventive.com/go/devsim" "src.dualinventive.com/go/devsim/repository" "src.dualinventive.com/go/devsim/simulator" "src.dualinventive.com/go/devsim/transport" "src.dualinventive.com/go/dinet" "src.dualinventive.com/go/lib/dilog" ) type simulatorService struct { simulatorsMx sync.Mutex templates map[string]*TemplateTracker simulators map[string]simulator.Simulator repService repository.Service smpEndpoint string storage Storage loggerDir string } // List return the current managed simulators func (s *simulatorService) List() []simulator.Simulator { s.simulatorsMx.Lock() defer s.simulatorsMx.Unlock() sims := make([]simulator.Simulator, 0, len(s.simulators)) for _, sim := range s.simulators { sims = append(sims, sim) } return sims } // Add adds a new simulator in the list of managed simulators. The simulator is prepared to start, but is // not started yet. func (s *simulatorService) Add(uri string, version string) (simulator.Info, error) { r, err := s.repService.Find(uri) if err != nil { return nil, err } key, err := r.Key(version) if err != nil { return nil, err } tracker, err := s.createTemplateIfNotExist(r, key) if err != nil { return nil, err } deviceUID, err := s.storage.GetNextDeviceUID() if err != nil { return nil, err } // Indicate that we are using this template. This creates the template if nessesary tracker.Increment() tpl := tracker.Info() templateInfo := &template{Info: tpl, deviceUID: deviceUID} // Get the driver driver := s.getDriver(templateInfo) if driver == nil { tracker.Decrement() return nil, ErrInvalidTemplate } // Get the model model, err := s.getModel(deviceUID) if err != nil { tracker.Decrement() return nil, err } // Create the simulator sim, err := simulator.NewSimulator(driver.Name(), model, templateInfo) if err != nil { tracker.Decrement() return nil, err } s.simulatorsMx.Lock() defer s.simulatorsMx.Unlock() if _, ok := s.simulators[deviceUID]; ok { tracker.Decrement() // This should not happen because we requested a deviceUID return nil, devsim.ErrAlreadyExists } s.simulators[deviceUID] = sim return templateInfo, nil } // Get returns the simulator with the given deviceUID or an ErrNotFound it returned when the simulator is // not found. func (s *simulatorService) Get(deviceUID string) (simulator.Simulator, error) { return s.getByDeviceUID(deviceUID) } // Remove removes the simulator of the list of managed simulators. This simulator must be stopped in order // to succeeds. An ErrNotFound is returned when the simulator is not found. An ErrNotStopped is returned when the // simulator is still running. func (s *simulatorService) Remove(deviceUID string) error { sim, err := s.getByDeviceUID(deviceUID) if err != nil { return err } key := sim.Key() if tTracker, ok := s.templates[key]; ok { tTracker.Decrement() } // TODO check if simulator is running delete(s.simulators, deviceUID) return nil } func (s *simulatorService) getByDeviceUID(deviceUID string) (simulator.Simulator, error) { s.simulatorsMx.Lock() defer s.simulatorsMx.Unlock() sim, ok := s.simulators[deviceUID] if !ok { return nil, devsim.ErrNotFound } return sim, nil } // Start starts the simulator with the given deviceUID. An ErrNotFound is returned when the simulator is // not found. An ErrAlreadyRunning is returned when the simulator is already running. func (s *simulatorService) Start(deviceUID string) error { sim, err := s.getByDeviceUID(deviceUID) if err != nil { return err } // TODO check if simulator is running return sim.Start() } // Stop stops the simulator with the given deviceUID. An ErrNotFound is returned when the simulator is // not found. An ErrAlreadyStopped is returned when the simulator is already stopped. func (s *simulatorService) Stop(deviceUID string) error { sim, err := s.getByDeviceUID(deviceUID) if err != nil { return err } // TODO check if simulator is running return sim.Stop() } func (s *simulatorService) createTemplateIfNotExist(rep repository.Repository, key string) (*TemplateTracker, error) { tTracker, ok := s.templates[key] if !ok { t, err := rep.Template(key) if err != nil { return nil, err } tTracker = &TemplateTracker{ usage: 0, template: t, } s.templates[key] = tTracker } return tTracker, nil } // getDriver returns the first valid driver that can execute the given template info func (s *simulatorService) getDriver(t simulator.Info) simulator.Driver { // Search for a valid driver that can execute our template drivers := simulator.Drivers() for _, driver := range drivers { if driver.Valid(t) { return driver } } return nil } // getModel returns the model that can be inserted in the simulator func (s *simulatorService) getModel(deviceUID string) (simulator.Model, error) { dStorage := s.storage.GetDeviceStorage(deviceUID) if dStorage == nil { return nil, ErrCannotCreateStorage } dTransport, err := transport.NewDinet(dinet.TransportLowlevel, deviceUID, s.smpEndpoint) if err != nil { return nil, err } // TODO get deviceUID logger discard := dilog.NewNilLogger() return simulator.NewModel(discard, dStorage, dTransport), nil }