down_time.go 5.1 KB

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