github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/storage/storage.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 storage
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	"go.uber.org/atomic"
    29  
    30  	"github.com/e154/smart-home/adaptors"
    31  	"github.com/e154/smart-home/common/events"
    32  	"github.com/e154/smart-home/common/logger"
    33  	m "github.com/e154/smart-home/models"
    34  	"github.com/e154/smart-home/system/bus"
    35  )
    36  
    37  var (
    38  	log = logger.MustGetLogger("storage")
    39  )
    40  
    41  // Storage ...
    42  type Storage struct {
    43  	adaptors  *adaptors.Adaptors
    44  	pool      sync.Map
    45  	quit      chan struct{}
    46  	inProcess *atomic.Bool
    47  	isStarted *atomic.Bool
    48  	eventBus  bus.Bus
    49  }
    50  
    51  // NewStorage ...
    52  func NewStorage(
    53  	adaptors *adaptors.Adaptors,
    54  	eventBus bus.Bus) *Storage {
    55  	storage := &Storage{
    56  		adaptors:  adaptors,
    57  		pool:      sync.Map{},
    58  		quit:      make(chan struct{}),
    59  		inProcess: atomic.NewBool(false),
    60  		isStarted: atomic.NewBool(true),
    61  		eventBus:  eventBus,
    62  	}
    63  
    64  	go func() {
    65  		ticker := time.NewTicker(time.Minute * 1)
    66  		defer ticker.Stop()
    67  
    68  		for {
    69  			select {
    70  			case <-ticker.C:
    71  				storage.serialize()
    72  			case <-storage.quit:
    73  				return
    74  			}
    75  		}
    76  	}()
    77  
    78  	return storage
    79  }
    80  
    81  // Shutdown ...
    82  func (s *Storage) Shutdown() {
    83  	if !s.isStarted.CompareAndSwap(true, false) {
    84  		return
    85  	}
    86  	close(s.quit)
    87  	s.serialize()
    88  }
    89  
    90  // Search ...
    91  func (s *Storage) Search(name string) (result map[string]string) {
    92  	return s.search(name)
    93  }
    94  
    95  // Push ...
    96  func (s *Storage) Push(name string, v string) (err error) {
    97  	err = s.push(name, v)
    98  	return
    99  }
   100  
   101  // GetByName ...
   102  func (s *Storage) GetByName(name string) (val string, err error) {
   103  	return s.getByName(name)
   104  }
   105  
   106  // Pop ...
   107  func (s *Storage) Pop(name string) (val string, err error) {
   108  	return s.pop(name)
   109  }
   110  
   111  func (s *Storage) push(name string, v string) (err error) {
   112  	s.pool.Store(name, m.Variable{
   113  		Name:    name,
   114  		Changed: true,
   115  		Value:   v,
   116  	})
   117  	return
   118  }
   119  
   120  func (s *Storage) getByName(name string) (val string, err error) {
   121  
   122  	if v, ok := s.pool.Load(name); ok {
   123  		val = v.(m.Variable).Value
   124  		return
   125  	}
   126  	var storage m.Variable
   127  	if storage, err = s.adaptors.Variable.GetByName(context.Background(), name); err != nil {
   128  		return
   129  	}
   130  	val = storage.Value
   131  
   132  	return
   133  }
   134  
   135  func (s *Storage) pop(name string) (val string, err error) {
   136  	val, err = s.getByName(name)
   137  	if err != nil {
   138  		return
   139  	}
   140  	if err = s.adaptors.Variable.Delete(context.Background(), name); err != nil {
   141  		return
   142  	}
   143  	s.pool.Delete(name)
   144  	return
   145  }
   146  
   147  // Serialize ...
   148  func (s *Storage) Serialize() {
   149  	s.serialize()
   150  }
   151  
   152  func (s *Storage) serialize() {
   153  
   154  	if !s.inProcess.CompareAndSwap(false, true) {
   155  		return
   156  	}
   157  	defer s.inProcess.Store(false)
   158  
   159  	var data m.Variable
   160  	var ok bool
   161  
   162  	s.pool.Range(func(key, val interface{}) bool {
   163  		data, ok = val.(m.Variable)
   164  		if !ok {
   165  			return true
   166  		}
   167  
   168  		if !data.Changed {
   169  			return true
   170  		}
   171  
   172  		data.Changed = false
   173  
   174  		s.pool.Store(key, data)
   175  
   176  		if err := s.adaptors.Variable.CreateOrUpdate(context.Background(), data); err != nil {
   177  			log.Error(err.Error())
   178  			return true
   179  		}
   180  		s.eventBus.Publish(fmt.Sprintf("system/models/variables/%s", data.Name), events.EventUpdatedVariableModel{
   181  			Name:  data.Name,
   182  			Value: data.Value,
   183  		})
   184  
   185  		return true
   186  	})
   187  
   188  }
   189  
   190  func (s *Storage) search(sub string) (result map[string]string) {
   191  	result = make(map[string]string)
   192  	s.pool.Range(func(key, val interface{}) bool {
   193  		if strings.Contains(key.(string), sub) {
   194  			if data, ok := val.(m.Variable); ok {
   195  				result[data.Name] = data.Value
   196  			}
   197  		}
   198  
   199  		return true
   200  	})
   201  
   202  	list, _, err := s.adaptors.Variable.Search(context.Background(), sub, 99, 0)
   203  	if err != nil {
   204  		return
   205  	}
   206  	for _, fromDb := range list {
   207  		if _, ok := result[fromDb.Name]; ok {
   208  			continue
   209  		}
   210  		result[fromDb.Name] = fromDb.Value
   211  		s.pool.Store(fromDb.Name, fromDb)
   212  	}
   213  	return
   214  }