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 }