| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- // package down_time -- счётчик обратного времени в мсек
- package down_time
- import (
- "context"
- "fmt"
- // "log"
- "sync"
- "time"
- "wartank/pkg/components/parsetime"
- "wartank/pkg/components/safebool"
- "wartank/pkg/components/safeint"
- "wartank/pkg/types"
- )
- const (
- спатьИнтервал = time.Millisecond * 100 // Малый интервал сна в 100 мсек
- )
- // ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.)
- type ВремОбрат struct {
- сцена types.ИСцена
- парсер *parsetime.ParseTime // Парсер значения
- времяЗнач *safeint.SafeInt // Фактическое значение счётчика
- времПорог *safeint.SafeInt // Целевое время срабатывания
- еслиРаботает *safebool.SafeBool // Признак работы
- канТик chan int // Канал секундных интервалов сна (для отображения)
- канВызов chan int // Канал для отправки сигналов (для верхнего уровня)
- кнт context.Context // Контекст для счётчика времени
- фнОтмена func() // Функция отмены контекста для счётчика времени
- блок sync.RWMutex
- }
- // НовВремОбрат -- возвращает новый *CountTime
- func НовВремОбрат(сцена types.ИСцена, знач int) *ВремОбрат {
- if сцена == nil {
- panic("НовВремОбрат(): ИСцена == nil")
- }
- кнт, фнОтмена := context.WithCancel(сцена.Кнт())
- сам := &ВремОбрат{
- сцена: сцена,
- времяЗнач: safeint.NewSafeInt(),
- канТик: make(chan int, 5),
- канВызов: make(chan int, 2),
- еслиРаботает: safebool.NewSafeBool(),
- парсер: parsetime.NewParseTime(),
- времПорог: safeint.NewSafeInt(),
- кнт: кнт,
- фнОтмена: фнОтмена,
- }
- знач = int(time.Now().UTC().Unix()) + знач
- сам.времПорог.Уст(знач)
- сам.еслиРаботает.Уст()
- go сам.сделатьТик()
- go сам.пуск()
- go сам.закрыть()
- return сам
- }
- // Запускает тикер для секундных интервалов
- func (сам *ВремОбрат) сделатьТик() {
- спатьСчёт := 0 // Счётчик сна (10 периодов -- 1 секунда)
- for {
- select {
- case <-сам.кнт.Done(): // Отмена контекста бота
- // log._rintf("CountTime.makeTick(): отмена контекста бота\n")
- return
- default:
- if !сам.еслиРаботает.Получ() {
- сам.фнОтмена()
- return
- }
- if спатьСчёт >= 10 {
- сам.канТик <- 1
- спатьСчёт = 0
- }
- time.Sleep(спатьИнтервал)
- спатьСчёт++
- }
- }
- }
- // Главный цикл обратного отсчёта
- func (sf *ВремОбрат) пуск() {
- for range sf.канТик {
- time.Sleep(time.Millisecond * 100)
- timeNow := time.Now().UTC().Unix()
- if sf.времПорог.Get() > int(timeNow) {
- continue
- }
- close(sf.канВызов)
- sf.фнОтмена()
- return
- }
- }
- // Стоп -- останавливает работу cчётчика
- func (sf *ВремОбрат) Стоп() {
- sf.еслиРаботает.Сброс()
- }
- // Получ -- возвращает число оставшихся сек
- func (sf *ВремОбрат) Получ() int {
- return sf.времяЗнач.Get()
- }
- // устанавливает число оставшихся сек
- func (sf *ВремОбрат) parse(val string) error {
- sf.блок.Lock()
- defer sf.блок.Unlock()
- if val == "" {
- return fmt.Errorf("CountTime.parse(): val is empty")
- }
- sf.парсер.Parse(val)
- _val := sf.парсер.Hour().Get()*3600 + sf.парсер.Min().Get()*60 + sf.парсер.Min().Get()
- sf.времяЗнач.Уст(_val)
- _val = int(time.Now().UTC().Unix()) + sf.времяЗнач.Get()
- sf.времПорог.Уст(_val)
- return nil
- }
- // устанавливает число оставшихся сек
- func (sf *ВремОбрат) set_val(val int) error {
- sf.блок.Lock()
- defer sf.блок.Unlock()
- if val < 0 {
- return fmt.Errorf("CountTime.set_val(): val(%v)<0", val)
- }
- sf.времяЗнач.Уст(val)
- { // Обновить локальные счётчики
- if val < 60 {
- sf.парсер.Hour().Reset()
- sf.парсер.Min().Reset()
- sf.парсер.Sec().Set(val)
- return nil
- }
- if 60 < val && val < 3600 {
- sf.парсер.Hour().Reset()
- iMin := val / 60
- sf.парсер.Min().Set(iMin)
- val -= iMin * 60
- sf.парсер.Sec().Set(val)
- return nil
- }
- sf.парсер.Hour().Set(val / 3600)
- val -= sf.парсер.Hour().Get() * 3600
- sf.парсер.Min().Set(val / 60)
- val -= sf.парсер.Min().Get() * 60
- sf.парсер.Sec().Set(val)
- // val = int(time.Now().UTC().Unix()) + sf.val.Get()
- }
- return nil
- }
- // Стр -- возвращает строковое представление оставшихся сек
- func (sf *ВремОбрат) Стр() string {
- sf.блок.RLock()
- defer sf.блок.RUnlock()
- timeNow := time.Now().UTC().Unix()
- val := sf.времПорог.Get() - int(timeNow)
- go sf.set_val(val)
- return sf.парсер.String()
- }
- // КаналСиг -- возвращает канал чтения тиков
- func (sf *ВремОбрат) КаналСиг() <-chan int {
- return sf.канВызов
- }
- func (сам *ВремОбрат) закрыть() {
- <-сам.кнт.Done()
- сам.блок.Lock()
- defer сам.блок.Unlock()
- if !сам.еслиРаботает.Получ() {
- return
- }
- сам.еслиРаботает.Сброс()
- close(сам.канТик)
- }
|