down_time.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // package down_time -- счётчик обратного времени в мсек
  2. package down_time
  3. import (
  4. "context"
  5. "fmt"
  6. // "log"
  7. "sync"
  8. "time"
  9. . "wartank/app/lev0/alias"
  10. . "wartank/app/lev0/types"
  11. "wartank/app/lev1/product/parser_time"
  12. . "gitp78su.ipnodns.ru/svi/kern"
  13. . "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
  14. )
  15. const (
  16. спатьИнтервал = time.Millisecond * 1000 // Малый интервал сна в 100 мсек
  17. )
  18. // ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.)
  19. type ВремОбрат struct {
  20. сцена ИАренаКонтекст // Сцена, которой принадлежит отсчёт времени
  21. остатПарсер ИПарсерВремя // Парсер значения (мсек)
  22. текущ ISafeInt // Фактическое значение счётчика в мсек
  23. лимит ISafeInt // Целевое время срабатывания в мсек
  24. еслиРаботает ISafeBool // Признак работы
  25. канВызов chan int // Канал для отправки сигналов (для верхнего уровня)
  26. кнт context.Context // Контекст для счётчика времени
  27. фнОтмена func() // Функция отмены контекста для счётчика времени
  28. блок sync.RWMutex
  29. }
  30. // НовВремОбрат -- возвращает новый *CountTime
  31. func НовВремОбрат(сцена ИАренаКонтекст, время АМилСек) *ВремОбрат {
  32. if сцена == nil {
  33. panic("НовВремОбрат(): ИСцена == nil")
  34. }
  35. кнт, фнОтмена := context.WithCancel(сцена.Контекст())
  36. сам := &ВремОбрат{
  37. сцена: сцена,
  38. текущ: NewSafeInt(),
  39. канВызов: make(chan int, 2),
  40. еслиРаботает: NewSafeBool(),
  41. остатПарсер: parser_time.НовПарсерВремя(),
  42. лимит: NewSafeInt(),
  43. кнт: кнт,
  44. фнОтмена: фнОтмена,
  45. }
  46. мСек := АМилСек(time.Now().UTC().UnixMilli()) + время
  47. сам.лимит.Set(int(мСек))
  48. сам.еслиРаботает.Set()
  49. go сам.пуск()
  50. go сам.закрыть()
  51. _ = ИВремяОстат(сам)
  52. return сам
  53. }
  54. // ПолучМилСек -- возвращает оставшееся хранимое время остатка
  55. func (сам *ВремОбрат) ПолучМилСек() АМилСек {
  56. return сам.остатПарсер.ПолучМилСек()
  57. }
  58. // Запускает тикер для интервалов сна (через каждые 1000 мСек)
  59. func (сам *ВремОбрат) пуск() {
  60. defer close(сам.канВызов)
  61. фнЖдать := func() {
  62. time.Sleep(спатьИнтервал)
  63. timeNow := time.Now().UTC().UnixMilli()
  64. цТекущ := сам.текущ.Get()
  65. цТекущ -= 1000
  66. if цТекущ < 0 {
  67. цТекущ = 0
  68. }
  69. сам.текущ.Set(цТекущ)
  70. if сам.лимит.Get() > int(timeNow) || цТекущ > 0 {
  71. return
  72. }
  73. сам.канВызов <- 1
  74. сам.лимит.Set(int(timeNow) + int(сам.остатПарсер.ПолучМилСек()))
  75. }
  76. for {
  77. select {
  78. case <-сам.кнт.Done(): // Отмена контекста тикера (а может и сцены, может и бота)
  79. return
  80. default:
  81. фнЖдать()
  82. }
  83. }
  84. }
  85. // Сброс -- сбрасывает оставшееся время в ноль
  86. func (сам *ВремОбрат) Сброс() {
  87. сам.остатПарсер.Сброс()
  88. сам.текущ.Reset()
  89. сам.лимит.Reset()
  90. }
  91. // Стоп -- останавливает работу счётчика
  92. func (сам *ВремОбрат) Стоп() {
  93. сам.фнОтмена()
  94. }
  95. // Уст -- устанавливает число оставшихся сек
  96. func (сам *ВремОбрат) Уст(время АВремя) error {
  97. сам.блок.Lock()
  98. defer сам.блок.Unlock()
  99. if ош := сам.остатПарсер.Уст(время); ош != nil {
  100. return fmt.Errorf("ВремОбрат(): ошибка при установке времени, ош=\n\t%w", ош)
  101. }
  102. _val := сам.остатПарсер.ПолучМилСек()
  103. сам.текущ.Set(int(_val))
  104. val := int(time.Now().UTC().UnixMilli()) + сам.текущ.Get()
  105. сам.лимит.Set(val)
  106. return nil
  107. }
  108. // String -- возвращает строковое представление оставшихся сек
  109. func (сам *ВремОбрат) String() string {
  110. сам.блок.RLock()
  111. defer сам.блок.RUnlock()
  112. цОстат := сам.текущ.Get()
  113. остат := time.Millisecond * time.Duration(цОстат)
  114. стрВрем := остат.String()
  115. return стрВрем
  116. }
  117. // КаналСиг -- возвращает канал чтения тиков
  118. func (сам *ВремОбрат) КаналСиг() <-chan int {
  119. return сам.канВызов
  120. }
  121. func (сам *ВремОбрат) закрыть() {
  122. <-сам.кнт.Done()
  123. сам.блок.Lock()
  124. defer сам.блок.Unlock()
  125. if !сам.еслиРаботает.Get() {
  126. return
  127. }
  128. сам.еслиРаботает.Set()
  129. }