github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/automation/task_manager.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package automation
    20  
    21  import (
    22  	"context"
    23  	"sync"
    24  
    25  	"github.com/e154/smart-home/adaptors"
    26  	"github.com/e154/smart-home/common/events"
    27  	"github.com/e154/smart-home/common/telemetry"
    28  	m "github.com/e154/smart-home/models"
    29  	"github.com/e154/smart-home/plugins/triggers"
    30  	"github.com/e154/smart-home/system/bus"
    31  	"github.com/e154/smart-home/system/scripts"
    32  	"github.com/e154/smart-home/system/supervisor"
    33  	"go.uber.org/atomic"
    34  )
    35  
    36  type taskManager struct {
    37  	eventBus      bus.Bus
    38  	scriptService scripts.ScriptService
    39  	supervisor    supervisor.Supervisor
    40  	adaptors      *adaptors.Adaptors
    41  	taskCount     *atomic.Uint64
    42  	isStarted     *atomic.Bool
    43  	rawPlugin     triggers.IGetTrigger
    44  	sync.Mutex
    45  	tasks map[int64]*Task
    46  }
    47  
    48  // NewTaskManager ...
    49  func NewTaskManager(
    50  	eventBus bus.Bus,
    51  	scriptService scripts.ScriptService,
    52  	sup supervisor.Supervisor,
    53  	adaptors *adaptors.Adaptors) (manager *taskManager) {
    54  
    55  	manager = &taskManager{
    56  		eventBus:      eventBus,
    57  		scriptService: scriptService,
    58  		tasks:         make(map[int64]*Task),
    59  		supervisor:    sup,
    60  		adaptors:      adaptors,
    61  		isStarted:     atomic.NewBool(false),
    62  		taskCount:     atomic.NewUint64(0),
    63  	}
    64  
    65  	return
    66  }
    67  
    68  // Start ...
    69  func (a *taskManager) Start() {
    70  	a.load()
    71  	_ = a.eventBus.Subscribe("system/models/tasks/+", a.eventHandler, false)
    72  	_ = a.eventBus.Subscribe("system/automation/tasks/+", a.eventHandler, false)
    73  
    74  	log.Info("Started")
    75  }
    76  
    77  // Shutdown ...
    78  func (a *taskManager) Shutdown() {
    79  	a.unload()
    80  	_ = a.eventBus.Unsubscribe("system/models/tasks/+", a.eventHandler)
    81  	_ = a.eventBus.Unsubscribe("system/automation/tasks/+", a.eventHandler)
    82  	log.Info("shutdown")
    83  }
    84  
    85  func (a *taskManager) load() {
    86  	if a.isStarted.Load() {
    87  		return
    88  	}
    89  
    90  	// load triggers plugin
    91  	plugin, err := a.supervisor.GetPlugin(triggers.Name)
    92  	if err != nil {
    93  		log.Error(err.Error())
    94  		return
    95  	}
    96  
    97  	if rawPlugin, ok := plugin.(triggers.IGetTrigger); ok {
    98  		a.rawPlugin = rawPlugin
    99  	} else {
   100  		log.Fatal("bad static cast triggers.IGetTrigger")
   101  	}
   102  
   103  	a.AddTasks()
   104  
   105  	a.isStarted.Store(true)
   106  
   107  	log.Info("Loaded ...")
   108  }
   109  
   110  func (a *taskManager) unload() {
   111  	if !a.isStarted.Load() {
   112  		return
   113  	}
   114  
   115  	for id := range a.tasks {
   116  		a.removeTask(id)
   117  	}
   118  	a.isStarted.Store(false)
   119  
   120  	log.Info("Unloaded ...")
   121  }
   122  
   123  // AddTasks ...
   124  func (a *taskManager) AddTasks() {
   125  
   126  	const perPage int64 = 500
   127  	var page int64 = 0
   128  LOOP:
   129  	tasks, _, err := a.adaptors.Task.List(context.Background(), perPage, page*perPage, "", "", true)
   130  	if err != nil {
   131  		log.Error(err.Error())
   132  		return
   133  	}
   134  	for _, task := range tasks {
   135  		a.addTask(task)
   136  	}
   137  	if len(tasks) != 0 {
   138  		page++
   139  		goto LOOP
   140  	}
   141  }
   142  
   143  func (a *taskManager) eventHandler(_ string, msg interface{}) {
   144  
   145  	switch v := msg.(type) {
   146  	case events.CommandEnableTask:
   147  		go a.updateTask(v.Id)
   148  	case events.CommandDisableTask:
   149  		go a.removeTask(v.Id)
   150  
   151  	case events.EventUpdatedTaskModel:
   152  		go a.updateTask(v.Id)
   153  	case events.EventCreatedTaskModel:
   154  		go a.updateTask(v.Id)
   155  	case events.EventRemovedTaskModel:
   156  		go a.removeTask(v.Id)
   157  	}
   158  }
   159  
   160  // addTask ...
   161  func (a *taskManager) addTask(model *m.Task) {
   162  	task := NewTask(a.eventBus, a.scriptService, model)
   163  	a.taskCount.Inc()
   164  	log.Infof("add task name(%s) id(%d)", task.Name(), task.Id())
   165  	a.Lock()
   166  	a.tasks[model.Id] = task
   167  	a.Unlock()
   168  	task.Start()
   169  }
   170  
   171  func (a *taskManager) removeTask(id int64) {
   172  	a.Lock()
   173  	defer a.Unlock()
   174  
   175  	task, ok := a.tasks[id]
   176  	if !ok {
   177  		return
   178  	}
   179  	log.Infof("remove task %d", id)
   180  
   181  	task.Stop()
   182  	delete(a.tasks, id)
   183  	a.taskCount.Dec()
   184  
   185  }
   186  
   187  func (a *taskManager) updateTask(id int64) {
   188  	a.removeTask(id)
   189  
   190  	task, err := a.adaptors.Task.GetById(context.Background(), id)
   191  	if err != nil {
   192  		return
   193  	}
   194  
   195  	a.addTask(task)
   196  }
   197  
   198  func (a *taskManager) IsLoaded(id int64) (loaded bool) {
   199  	a.Lock()
   200  	_, loaded = a.tasks[id]
   201  	a.Unlock()
   202  	return
   203  }
   204  
   205  func (a *taskManager) TaskTelemetry(id int64) telemetry.Telemetry {
   206  	a.Lock()
   207  	defer a.Unlock()
   208  	if task, ok := a.tasks[id]; ok {
   209  		return task.Telemetry()
   210  	}
   211  	return nil
   212  }