counttime.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package counttime
  2. import (
  3. "fmt"
  4. // "log"
  5. "sync"
  6. "time"
  7. . "gitp78su.ipnodns.ru/svi/kern"
  8. . "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
  9. . "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
  10. "wartank/pkg/components/parsetime"
  11. )
  12. /*
  13. Счётчик обратного временив мсек
  14. */
  15. const (
  16. iSleep = time.Millisecond * 100
  17. )
  18. // CountTime -- счётчик обратного времени
  19. type CountTime struct {
  20. kCtx IKernelCtx
  21. val ISafeInt
  22. parser *parsetime.ParseTime
  23. chTick chan int
  24. chCall chan int // Канал для отправки сигналов
  25. isWork ISafeBool // Признак работы
  26. timeTarget ISafeInt // Целевое время срабатывания
  27. block sync.RWMutex
  28. }
  29. // NewCountTime -- возвращает новый *CountTime
  30. func NewCountTime() *CountTime {
  31. sf := &CountTime{
  32. kCtx: GetKernelCtx(),
  33. val: NewSafeInt(),
  34. chTick: make(chan int, 3),
  35. chCall: make(chan int, 2),
  36. isWork: NewSafeBool(),
  37. parser: parsetime.NewParseTime(),
  38. timeTarget: NewSafeInt(),
  39. }
  40. sf.isWork.Set()
  41. go sf.makeTick()
  42. go sf.run()
  43. return sf
  44. }
  45. // Запускает тикер для секундных интервалов
  46. func (sf *CountTime) makeTick() {
  47. defer func() {
  48. if !sf.isWork.Get() {
  49. return
  50. }
  51. sf.isWork.Reset()
  52. close(sf.chTick)
  53. // log._rintf("CountTime.makeTick(): работа завершена")
  54. }()
  55. countSleep := time.Duration(0)
  56. for {
  57. select {
  58. case <-sf.kCtx.Ctx().Done(): // Отмена контекста приложения
  59. // log._rintf("CountTime.makeTick(): глобальная отмена контекста\n")
  60. return
  61. default:
  62. if !sf.isWork.Get() {
  63. return
  64. }
  65. if countSleep >= time.Second {
  66. sf.chTick <- 1
  67. countSleep = 0
  68. }
  69. countSleep += iSleep
  70. time.Sleep(iSleep)
  71. }
  72. }
  73. }
  74. // Главный цикл обратного отсчёта
  75. func (sf *CountTime) run() {
  76. for range sf.chTick {
  77. timeNow := time.Now().UTC().Unix()
  78. if sf.timeTarget.Get() > int(timeNow) {
  79. val := sf.timeTarget.Get() - int(timeNow)
  80. val -= 1
  81. err := sf.Set(val)
  82. Hassert(err == nil, "err=%v", err)
  83. continue
  84. }
  85. sf.chCall <- 1
  86. continue
  87. }
  88. close(sf.chCall)
  89. }
  90. // Stop -- останавливает работу счётчика
  91. func (sf *CountTime) Stop() {
  92. sf.isWork.Reset()
  93. }
  94. // Get -- возвращает число оставшихся сек
  95. func (sf *CountTime) Get() int {
  96. return sf.val.Get()
  97. }
  98. // Parse -- устанавливает число оставшихся сек
  99. func (sf *CountTime) Parse(val string) error {
  100. sf.block.Lock()
  101. defer sf.block.Unlock()
  102. if val == "" {
  103. return fmt.Errorf("CountTime.Set(): val is empty")
  104. }
  105. err := sf.parser.Parse(val)
  106. Hassert(err == nil, "err=%v", err)
  107. _val := sf.parser.Hour().Get()*3600 + sf.parser.Min().Get()*60 + sf.parser.Min().Get()
  108. sf.val.Set(_val)
  109. _val = int(time.Now().UTC().Unix()) + sf.val.Get()
  110. sf.timeTarget.Set(_val)
  111. return nil
  112. }
  113. // Set -- устанавливает число оставшихся сек
  114. func (sf *CountTime) Set(val int) error {
  115. sf.block.Lock()
  116. defer sf.block.Unlock()
  117. if val < 0 {
  118. return fmt.Errorf("CountTime.Set(): val(%v)<0", val)
  119. }
  120. sf.val.Set(val)
  121. { // Обновить локальные счётчики
  122. if val < 60 {
  123. sf.parser.Hour().Reset()
  124. sf.parser.Min().Reset()
  125. err := sf.parser.Sec().Set(val)
  126. Hassert(err == nil, "err==%v", err)
  127. return nil
  128. }
  129. if 60 < val && val < 3600 {
  130. sf.parser.Hour().Reset()
  131. iMin := val / 60
  132. err := sf.parser.Min().Set(iMin)
  133. Hassert(err == nil, "err=%v", err)
  134. val -= iMin * 60
  135. err = sf.parser.Sec().Set(val)
  136. Hassert(err == nil, "err=%v", err)
  137. return nil
  138. }
  139. err := sf.parser.Hour().Set(val / 3600)
  140. Hassert(err == nil, "err=%v", err)
  141. val -= sf.parser.Hour().Get() * 3600
  142. err = sf.parser.Min().Set(val / 60)
  143. Hassert(err == nil, "err=%v", err)
  144. val -= sf.parser.Min().Get() * 60
  145. err = sf.parser.Sec().Set(val)
  146. Hassert(err == nil, "err=%v", err)
  147. val = int(time.Now().UTC().Unix()) + sf.val.Get()
  148. sf.timeTarget.Set(val)
  149. }
  150. return nil
  151. }
  152. // String -- возвращает строковое представление оставшихся сек
  153. func (sf *CountTime) String() string {
  154. sf.block.RLock()
  155. defer sf.block.RUnlock()
  156. return sf.parser.String()
  157. }
  158. // ChanSig -- возвращает канал чтения тиков
  159. func (sf *CountTime) ChanSig() <-chan int {
  160. return sf.chCall
  161. }