feat: times 包新增 Line 时间线结构,提供了时间线性状态的实现
This commit is contained in:
parent
2fe797e1c2
commit
a9c84caa52
|
@ -0,0 +1,247 @@
|
|||
package times
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewStateLine 创建一个从左向右由早到晚的状态时间线
|
||||
func NewStateLine[State generic.Basic](zero State) *StateLine[State] {
|
||||
return &StateLine[State]{
|
||||
states: []State{zero},
|
||||
points: []time.Time{{}},
|
||||
trigger: [][]func(){{}},
|
||||
}
|
||||
}
|
||||
|
||||
// StateLine 状态时间线
|
||||
type StateLine[State generic.Basic] struct {
|
||||
states []State // 每个时间点对应的状态
|
||||
points []time.Time // 每个时间点
|
||||
trigger [][]func() // 每个时间点对应的触发器
|
||||
}
|
||||
|
||||
// Check 根据状态顺序检查时间线是否合法
|
||||
// - missingAllowed: 是否允许状态缺失,如果为 true,则状态可以不连续,如果为 false,则状态必须连续
|
||||
//
|
||||
// 状态不连续表示时间线中存在状态缺失,例如:
|
||||
// - 状态为 [1, 2, 3, 4, 5] 的时间线,如果 missingAllowed 为 true,则状态为 [1, 3, 5] 也是合法的
|
||||
// - 状态为 [1, 2, 3, 4, 5] 的时间线,如果 missingAllowed 为 false,则状态为 [1, 3, 5] 是不合法的
|
||||
func (slf *StateLine[State]) Check(missingAllowed bool, states ...State) bool {
|
||||
var indexStored int
|
||||
var indexInput int
|
||||
|
||||
for indexStored < len(slf.states) && indexInput < len(states) {
|
||||
if slf.states[indexStored] == states[indexInput] {
|
||||
indexStored++
|
||||
indexInput++
|
||||
} else if missingAllowed {
|
||||
indexInput++
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
//如果存储序列还有剩余, 而输入序列已经遍历完
|
||||
if indexStored != len(slf.states) && indexInput == len(states) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 如果输入序列还有剩余, 而存储序列已经遍历完
|
||||
if indexStored == len(slf.states) && indexInput != len(states) && !missingAllowed {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// GetMissingStates 获取缺失的状态
|
||||
func (slf *StateLine[State]) GetMissingStates(states ...State) []State {
|
||||
var missing = make([]State, 0, len(states))
|
||||
for _, state := range states {
|
||||
if !slice.Contains(slf.states, state) {
|
||||
missing = append(missing, state)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
// HasState 检查时间线中是否包含指定状态
|
||||
func (slf *StateLine[State]) HasState(state State) bool {
|
||||
return slice.Contains(slf.states, state)
|
||||
}
|
||||
|
||||
// String 获取时间线的字符串表示
|
||||
func (slf *StateLine[State]) String() string {
|
||||
var parts []string
|
||||
for i := 0; i < len(slf.states); i++ {
|
||||
parts = append(parts, fmt.Sprintf("[%v] %v", slf.states[i], slf.points[i]))
|
||||
}
|
||||
return strings.Join(parts, " > ")
|
||||
}
|
||||
|
||||
// AddState 添加一个状态到时间线中,状态不能与任一时间点重合,否则将被忽略
|
||||
// - onTrigger: 该状态绑定的触发器,该触发器不会被主动执行,需要主动获取触发器执行
|
||||
func (slf *StateLine[State]) AddState(state State, t time.Time, onTrigger ...func()) *StateLine[State] {
|
||||
if slice.Contains(slf.states, state) {
|
||||
return slf
|
||||
}
|
||||
// 将 t 按照从左到右由早到晚的顺序插入到 points 中
|
||||
for i := 0; i < len(slf.points); i++ {
|
||||
if slf.points[i].After(t) {
|
||||
slf.points = append(slf.points[:i], append([]time.Time{t}, slf.points[i:]...)...)
|
||||
slf.states = append(slf.states[:i], append([]State{state}, slf.states[i:]...)...)
|
||||
slf.trigger = append(slf.trigger[:i], append([][]func(){onTrigger}, slf.trigger[i:]...)...)
|
||||
return slf
|
||||
}
|
||||
}
|
||||
slf.points = append(slf.points, t)
|
||||
slf.states = append(slf.states, state)
|
||||
slf.trigger = append(slf.trigger, onTrigger)
|
||||
return slf
|
||||
}
|
||||
|
||||
// GetTimeByState 获取指定状态的时间点
|
||||
func (slf *StateLine[State]) GetTimeByState(state State) time.Time {
|
||||
for i := 0; i < len(slf.states); i++ {
|
||||
if slf.states[i] == state {
|
||||
return slf.points[i]
|
||||
}
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// GetNextTimeByState 获取指定状态的下一个时间点
|
||||
func (slf *StateLine[State]) GetNextTimeByState(state State) time.Time {
|
||||
for i := 0; i < len(slf.states); i++ {
|
||||
if slf.states[i] == state && i < len(slf.points)-1 {
|
||||
return slf.points[i+1]
|
||||
}
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// GetPrevTimeByState 获取指定状态的上一个时间点
|
||||
func (slf *StateLine[State]) GetPrevTimeByState(state State) time.Time {
|
||||
for i := len(slf.states) - 1; i >= 0; i-- {
|
||||
if slf.states[i] == state && i > 0 {
|
||||
return slf.points[i-1]
|
||||
}
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// GetIndexByState 获取指定状态的索引
|
||||
func (slf *StateLine[State]) GetIndexByState(state State) int {
|
||||
for i := 0; i < len(slf.states); i++ {
|
||||
if slf.states[i] == state {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// GetStateByTime 获取指定时间点的状态
|
||||
func (slf *StateLine[State]) GetStateByTime(t time.Time) State {
|
||||
for i := len(slf.points) - 1; i >= 0; i-- {
|
||||
if i == len(slf.points)-1 && slf.points[i].Before(t) {
|
||||
return slf.states[0]
|
||||
}
|
||||
if slf.points[i].Before(t) {
|
||||
return slf.states[i]
|
||||
}
|
||||
}
|
||||
return slf.states[0]
|
||||
}
|
||||
|
||||
// GetTimeByIndex 获取指定索引的时间点
|
||||
func (slf *StateLine[State]) GetTimeByIndex(index int) time.Time {
|
||||
return slf.points[index]
|
||||
}
|
||||
|
||||
// GetNextStateTimeByIndex 获取指定索引的下一个时间点
|
||||
func (slf *StateLine[State]) GetNextStateTimeByIndex(index int) time.Time {
|
||||
return slf.points[index+1]
|
||||
}
|
||||
|
||||
// GetPrevStateTimeByIndex 获取指定索引的上一个时间点
|
||||
func (slf *StateLine[State]) GetPrevStateTimeByIndex(index int) time.Time {
|
||||
return slf.points[index-1]
|
||||
}
|
||||
|
||||
// GetStateIndexByTime 获取指定时间点的索引
|
||||
func (slf *StateLine[State]) GetStateIndexByTime(t time.Time) int {
|
||||
for i := len(slf.points) - 1; i >= 0; i-- {
|
||||
if slf.points[i].Before(t) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// GetStateCount 获取状态数量
|
||||
func (slf *StateLine[State]) GetStateCount() int {
|
||||
return len(slf.states)
|
||||
}
|
||||
|
||||
// GetStateByIndex 获取指定索引的状态
|
||||
func (slf *StateLine[State]) GetStateByIndex(index int) State {
|
||||
return slf.states[index]
|
||||
}
|
||||
|
||||
// GetTriggerByTime 获取指定时间点的触发器
|
||||
func (slf *StateLine[State]) GetTriggerByTime(t time.Time) []func() {
|
||||
for i := len(slf.points) - 1; i >= 0; i-- {
|
||||
if slf.points[i].Before(t) {
|
||||
return slf.trigger[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTriggerByIndex 获取指定索引的触发器
|
||||
func (slf *StateLine[State]) GetTriggerByIndex(index int) []func() {
|
||||
return slf.trigger[index]
|
||||
}
|
||||
|
||||
// GetTriggerByState 获取指定状态的触发器
|
||||
func (slf *StateLine[State]) GetTriggerByState(state State) []func() {
|
||||
for i := 0; i < len(slf.states); i++ {
|
||||
if slf.states[i] == state {
|
||||
return slf.trigger[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTriggerToState 给指定状态添加触发器
|
||||
func (slf *StateLine[State]) AddTriggerToState(state State, onTrigger ...func()) *StateLine[State] {
|
||||
for i := 0; i < len(slf.states); i++ {
|
||||
if slf.states[i] == state {
|
||||
slf.trigger[i] = append(slf.trigger[i], onTrigger...)
|
||||
return slf
|
||||
}
|
||||
}
|
||||
return slf
|
||||
}
|
||||
|
||||
// Range 按照时间顺序遍历时间线
|
||||
func (slf *StateLine[State]) Range(handler func(index int, state State, t time.Time) bool) {
|
||||
for i := 0; i < len(slf.points); i++ {
|
||||
if !handler(i, slf.states[i], slf.points[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RangeReverse 按照时间逆序遍历时间线
|
||||
func (slf *StateLine[State]) RangeReverse(handler func(index int, state State, t time.Time) bool) {
|
||||
for i := len(slf.points) - 1; i >= 0; i-- {
|
||||
if !handler(i, slf.states[i], slf.points[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package times_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/times"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewStateLine(t *testing.T) {
|
||||
sl := times.NewStateLine(0)
|
||||
sl.AddState(1, time.Now())
|
||||
sl.AddState(2, time.Now().Add(-times.Hour))
|
||||
|
||||
sl.Range(func(index int, state int, ts time.Time) bool {
|
||||
t.Log(index, state, ts)
|
||||
return true
|
||||
})
|
||||
|
||||
t.Log(sl.GetState(time.Now()))
|
||||
}
|
Loading…
Reference in New Issue