github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/scripts/engine_watcher.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 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 scripts 20 21 import ( 22 "fmt" 23 "sync" 24 25 "github.com/e154/smart-home/common" 26 "github.com/e154/smart-home/common/events" 27 m "github.com/e154/smart-home/models" 28 "github.com/e154/smart-home/system/bus" 29 ) 30 31 type EngineWatcher struct { 32 eventBus bus.Bus 33 scriptService *scriptService 34 f func(engine *Engine) 35 fBefore func(engine *Engine) 36 structures *Pull 37 functions *Pull 38 mx *sync.RWMutex 39 script *m.Script 40 engine *Engine 41 } 42 43 func NewEngineWatcher(script *m.Script, s *scriptService, eventBus bus.Bus) *EngineWatcher { 44 w := &EngineWatcher{ 45 eventBus: eventBus, 46 scriptService: s, 47 mx: &sync.RWMutex{}, 48 script: script, 49 structures: NewPull(), 50 functions: NewPull(), 51 } 52 53 w.engine, _ = w.scriptService.NewEngine(nil) 54 w.structures.Range(func(key, value interface{}) bool { 55 w.engine.PushStruct(key.(string), value) 56 return true 57 }) 58 w.functions.Range(func(key, value interface{}) bool { 59 w.engine.PushFunction(key.(string), value) 60 return true 61 }) 62 63 if script.Id != 0 { 64 _ = eventBus.Subscribe(fmt.Sprintf("system/models/scripts/%d", script.Id), w.eventHandler) 65 } 66 67 return w 68 } 69 70 func (w *EngineWatcher) Stop() { 71 if w.engine.model != nil && w.engine.model.Id != 0 { 72 _ = w.eventBus.Unsubscribe(fmt.Sprintf("system/models/scripts/%d", w.engine.model.Id), w.eventHandler) 73 } 74 } 75 76 func (w *EngineWatcher) Spawn(f func(engine *Engine)) { 77 w.mx.RLock() 78 defer w.mx.RUnlock() 79 80 w.engine, _ = w.scriptService.NewEngine(&m.Script{ 81 Id: w.script.Id, 82 Lang: common.ScriptLangJavascript, 83 }) 84 w.structures.Range(func(key, value interface{}) bool { 85 w.engine.PushStruct(key.(string), value) 86 return true 87 }) 88 w.functions.Range(func(key, value interface{}) bool { 89 w.engine.PushFunction(key.(string), value) 90 return true 91 }) 92 93 if w.fBefore != nil { 94 w.fBefore(w.engine) 95 } 96 97 if _, err := w.engine.EvalScript(w.script); err != nil { 98 if w.script.Id != 0 { 99 log.Errorf("script id: %d, %s", w.script.Id, err.Error()) 100 } else { 101 log.Error(err.Error()) 102 } 103 } 104 105 if f != nil { 106 w.f = f 107 w.f(w.engine) 108 } 109 } 110 111 func (w *EngineWatcher) BeforeSpawn(f func(engine *Engine)) { 112 if f == nil { 113 return 114 } 115 w.mx.Lock() 116 defer w.mx.Unlock() 117 w.fBefore = f 118 } 119 120 func (w *EngineWatcher) Engine() *Engine { 121 w.mx.RLock() 122 defer w.mx.RUnlock() 123 return w.engine 124 } 125 126 // eventHandler ... 127 func (w *EngineWatcher) eventHandler(_ string, message interface{}) { 128 129 switch msg := message.(type) { 130 case events.EventUpdatedScriptModel: 131 go w.eventUpdatedScript(msg) 132 case events.EventRemovedScriptModel: 133 go w.eventScriptDeleted(msg) 134 } 135 } 136 137 func (w *EngineWatcher) eventUpdatedScript(msg events.EventUpdatedScriptModel) { 138 139 if msg.Script == nil { 140 return 141 } 142 143 w.script = msg.Script 144 145 w.Spawn(w.f) 146 147 log.Infof("script '%s' (%d) updated", msg.Script.Name, msg.ScriptId) 148 } 149 150 func (w *EngineWatcher) eventScriptDeleted(msg events.EventRemovedScriptModel) { 151 if w.engine.model != nil { 152 _ = w.eventBus.Unsubscribe(fmt.Sprintf("system/models/scripts/%d", w.script.Id), w.eventHandler) 153 } 154 155 var err error 156 if w.engine, err = w.scriptService.NewEngine(nil); err != nil { 157 log.Error(err.Error()) 158 return 159 } 160 } 161 162 func (w *EngineWatcher) PushStruct(name string, str interface{}) { 163 w.structures.Push(name, str) 164 if w.engine != nil { 165 w.engine.PushStruct(name, str) 166 } 167 } 168 169 func (w *EngineWatcher) PopStruct(name string) { 170 w.structures.Pop(name) 171 } 172 173 func (w *EngineWatcher) PushFunction(name string, f interface{}) { 174 w.functions.Push(name, f) 175 if w.engine != nil { 176 w.engine.PushFunction(name, f) 177 } 178 } 179 180 func (w *EngineWatcher) PopFunction(name string) { 181 w.functions.Pop(name) 182 }