package counttime import ( "fmt" // "log" "sync" "time" . "gitp78su.ipnodns.ru/svi/kern" . "gitp78su.ipnodns.ru/svi/kern/kc/helpers" . "gitp78su.ipnodns.ru/svi/kern/krn/ktypes" "wartank/pkg/components/parsetime" ) /* Счётчик обратного временив мсек */ const ( iSleep = time.Millisecond * 100 ) // CountTime -- счётчик обратного времени type CountTime struct { kCtx IKernelCtx val ISafeInt parser *parsetime.ParseTime chTick chan int chCall chan int // Канал для отправки сигналов isWork ISafeBool // Признак работы timeTarget ISafeInt // Целевое время срабатывания block sync.RWMutex } // NewCountTime -- возвращает новый *CountTime func NewCountTime() *CountTime { sf := &CountTime{ kCtx: GetKernelCtx(), val: NewSafeInt(), chTick: make(chan int, 3), chCall: make(chan int, 2), isWork: NewSafeBool(), parser: parsetime.NewParseTime(), timeTarget: NewSafeInt(), } sf.isWork.Set() go sf.makeTick() go sf.run() return sf } // Запускает тикер для секундных интервалов func (sf *CountTime) makeTick() { defer func() { if !sf.isWork.Get() { return } sf.isWork.Reset() close(sf.chTick) // log._rintf("CountTime.makeTick(): работа завершена") }() countSleep := time.Duration(0) for { select { case <-sf.kCtx.Ctx().Done(): // Отмена контекста приложения // log._rintf("CountTime.makeTick(): глобальная отмена контекста\n") return default: if !sf.isWork.Get() { return } if countSleep >= time.Second { sf.chTick <- 1 countSleep = 0 } countSleep += iSleep time.Sleep(iSleep) } } } // Главный цикл обратного отсчёта func (sf *CountTime) run() { for range sf.chTick { timeNow := time.Now().UTC().Unix() if sf.timeTarget.Get() > int(timeNow) { val := sf.timeTarget.Get() - int(timeNow) val -= 1 err := sf.Set(val) Hassert(err == nil, "err=%v", err) continue } sf.chCall <- 1 continue } close(sf.chCall) } // Stop -- останавливает работу счётчика func (sf *CountTime) Stop() { sf.isWork.Reset() } // Get -- возвращает число оставшихся сек func (sf *CountTime) Get() int { return sf.val.Get() } // Parse -- устанавливает число оставшихся сек func (sf *CountTime) Parse(val string) error { sf.block.Lock() defer sf.block.Unlock() if val == "" { return fmt.Errorf("CountTime.Set(): val is empty") } err := sf.parser.Parse(val) Hassert(err == nil, "err=%v", err) _val := sf.parser.Hour().Get()*3600 + sf.parser.Min().Get()*60 + sf.parser.Min().Get() sf.val.Set(_val) _val = int(time.Now().UTC().Unix()) + sf.val.Get() sf.timeTarget.Set(_val) return nil } // Set -- устанавливает число оставшихся сек func (sf *CountTime) Set(val int) error { sf.block.Lock() defer sf.block.Unlock() if val < 0 { return fmt.Errorf("CountTime.Set(): val(%v)<0", val) } sf.val.Set(val) { // Обновить локальные счётчики if val < 60 { sf.parser.Hour().Reset() sf.parser.Min().Reset() err := sf.parser.Sec().Set(val) Hassert(err == nil, "err==%v", err) return nil } if 60 < val && val < 3600 { sf.parser.Hour().Reset() iMin := val / 60 err := sf.parser.Min().Set(iMin) Hassert(err == nil, "err=%v", err) val -= iMin * 60 err = sf.parser.Sec().Set(val) Hassert(err == nil, "err=%v", err) return nil } err := sf.parser.Hour().Set(val / 3600) Hassert(err == nil, "err=%v", err) val -= sf.parser.Hour().Get() * 3600 err = sf.parser.Min().Set(val / 60) Hassert(err == nil, "err=%v", err) val -= sf.parser.Min().Get() * 60 err = sf.parser.Sec().Set(val) Hassert(err == nil, "err=%v", err) val = int(time.Now().UTC().Unix()) + sf.val.Get() sf.timeTarget.Set(val) } return nil } // String -- возвращает строковое представление оставшихся сек func (sf *CountTime) String() string { sf.block.RLock() defer sf.block.RUnlock() return sf.parser.String() } // ChanSig -- возвращает канал чтения тиков func (sf *CountTime) ChanSig() <-chan int { return sf.chCall }