194 lines
5.1 KiB
Go
194 lines
5.1 KiB
Go
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
|
|
}
|