| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- // package down_time -- счётчик обратного времени в мсек
- package down_time
- import (
- "context"
- "fmt"
- // "log"
- "sync"
- "time"
- . "wartank/app/lev0/alias"
- . "wartank/app/lev0/types"
- "wartank/app/lev1/product/parser_time"
- . "gitp78su.ipnodns.ru/svi/kern"
- . "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
- )
- const (
- спатьИнтервал = time.Millisecond * 1000 // Малый интервал сна в 100 мсек
- )
- // ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.)
- type ВремОбрат struct {
- сцена ИАренаКонтекст // Сцена, которой принадлежит отсчёт времени
- остатПарсер ИПарсерВремя // Парсер значения (мсек)
- текущ ISafeInt // Фактическое значение счётчика в мсек
- лимит ISafeInt // Целевое время срабатывания в мсек
- еслиРаботает ISafeBool // Признак работы
- канВызов chan int // Канал для отправки сигналов (для верхнего уровня)
- кнт context.Context // Контекст для счётчика времени
- фнОтмена func() // Функция отмены контекста для счётчика времени
- блок sync.RWMutex
- }
- // НовВремОбрат -- возвращает новый *CountTime
- func НовВремОбрат(сцена ИАренаКонтекст, время АМилСек) *ВремОбрат {
- if сцена == nil {
- panic("НовВремОбрат(): ИСцена == nil")
- }
- кнт, фнОтмена := context.WithCancel(сцена.Контекст())
- сам := &ВремОбрат{
- сцена: сцена,
- текущ: NewSafeInt(),
- канВызов: make(chan int, 2),
- еслиРаботает: NewSafeBool(),
- остатПарсер: parser_time.НовПарсерВремя(),
- лимит: NewSafeInt(),
- кнт: кнт,
- фнОтмена: фнОтмена,
- }
- мСек := АМилСек(time.Now().UTC().UnixMilli()) + время
- сам.лимит.Set(int(мСек))
- сам.еслиРаботает.Set()
- go сам.пуск()
- go сам.закрыть()
- _ = ИВремяОстат(сам)
- return сам
- }
- // ПолучМилСек -- возвращает оставшееся хранимое время остатка
- func (сам *ВремОбрат) ПолучМилСек() АМилСек {
- return сам.остатПарсер.ПолучМилСек()
- }
- // Запускает тикер для интервалов сна (через каждые 1000 мСек)
- func (сам *ВремОбрат) пуск() {
- defer close(сам.канВызов)
- фнЖдать := func() {
- time.Sleep(спатьИнтервал)
- timeNow := time.Now().UTC().UnixMilli()
- цТекущ := сам.текущ.Get()
- цТекущ -= 1000
- if цТекущ < 0 {
- цТекущ = 0
- }
- сам.текущ.Set(цТекущ)
- if сам.лимит.Get() > int(timeNow) || цТекущ > 0 {
- return
- }
- сам.канВызов <- 1
- сам.лимит.Set(int(timeNow) + int(сам.остатПарсер.ПолучМилСек()))
- }
- for {
- select {
- case <-сам.кнт.Done(): // Отмена контекста тикера (а может и сцены, может и бота)
- return
- default:
- фнЖдать()
- }
- }
- }
- // Сброс -- сбрасывает оставшееся время в ноль
- func (сам *ВремОбрат) Сброс() {
- сам.остатПарсер.Сброс()
- сам.текущ.Reset()
- сам.лимит.Reset()
- }
- // Стоп -- останавливает работу счётчика
- func (сам *ВремОбрат) Стоп() {
- сам.фнОтмена()
- }
- // Уст -- устанавливает число оставшихся сек
- func (сам *ВремОбрат) Уст(время АВремя) error {
- сам.блок.Lock()
- defer сам.блок.Unlock()
- if ош := сам.остатПарсер.Уст(время); ош != nil {
- return fmt.Errorf("ВремОбрат(): ошибка при установке времени, ош=\n\t%w", ош)
- }
- _val := сам.остатПарсер.ПолучМилСек()
- сам.текущ.Set(int(_val))
- val := int(time.Now().UTC().UnixMilli()) + сам.текущ.Get()
- сам.лимит.Set(val)
- return nil
- }
- // String -- возвращает строковое представление оставшихся сек
- func (сам *ВремОбрат) String() string {
- сам.блок.RLock()
- defer сам.блок.RUnlock()
- цОстат := сам.текущ.Get()
- остат := time.Millisecond * time.Duration(цОстат)
- стрВрем := остат.String()
- return стрВрем
- }
- // КаналСиг -- возвращает канал чтения тиков
- func (сам *ВремОбрат) КаналСиг() <-chan int {
- return сам.канВызов
- }
- func (сам *ВремОбрат) закрыть() {
- <-сам.кнт.Done()
- сам.блок.Lock()
- defer сам.блок.Unlock()
- if !сам.еслиРаботает.Get() {
- return
- }
- сам.еслиРаботает.Set()
- }
|