mirror of
https://github.com/duanhf2012/origin.git
synced 2026-02-12 22:54:43 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cf071a444 | ||
|
|
e6c09064bf | ||
|
|
84ab0cb84a | ||
|
|
22fe00173b | ||
|
|
8e0ed62fca | ||
|
|
7116b509e9 | ||
|
|
73d384361d | ||
|
|
ce56b19fe8 | ||
|
|
1367d776e6 | ||
|
|
987d35ff15 |
121
README.md
121
README.md
@@ -661,51 +661,7 @@ Module1 Release.
|
|||||||
第四章:事件使用
|
第四章:事件使用
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
事件是origin中一个重要的组成部分,可以在同一个node中的service与service或者与module之间进行事件通知。系统内置的几个服务,如:TcpService/HttpService等都是通过事件功能实现。他也是一个典型的观察者设计模型。在event中有两个类型的interface,一个是event.IEventProcessor它提供注册与卸载功能,另一个是event.IEventHandler提供消息广播等功能。
|
事件是origin中一个重要的组成部分,可以在服务与各module之间进行事件通知。它也是一个典型的观察者设计模型。在event中有两个类型的interface,一个是event.IEventProcessor它提供注册与卸载功能,另一个是event.IEventHandler提供消息广播等功能。
|
||||||
|
|
||||||
在目录simple_event/TestService4.go中
|
|
||||||
|
|
||||||
```
|
|
||||||
package simple_event
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/duanhf2012/origin/v2/event"
|
|
||||||
"github.com/duanhf2012/origin/v2/node"
|
|
||||||
"github.com/duanhf2012/origin/v2/service"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
//自定义事件类型,必需从event.Sys_Event_User_Define开始
|
|
||||||
//event.Sys_Event_User_Define以内给系统预留
|
|
||||||
EVENT1 event.EventType =event.Sys_Event_User_Define+1
|
|
||||||
)
|
|
||||||
|
|
||||||
func init(){
|
|
||||||
node.Setup(&TestService4{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestService4 struct {
|
|
||||||
service.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *TestService4) OnInit() error {
|
|
||||||
//10秒后触发广播事件
|
|
||||||
slf.AfterFunc(time.Second*10,slf.TriggerEvent)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *TestService4) TriggerEvent(){
|
|
||||||
//广播事件,传入event.Event对象,类型为EVENT1,Data可以自定义任何数据
|
|
||||||
//这样,所有监听者都可以收到该事件
|
|
||||||
slf.GetEventHandler().NotifyEvent(&event.Event{
|
|
||||||
Type: EVENT1,
|
|
||||||
Data: "event data.",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
在目录simple_event/TestService5.go中
|
在目录simple_event/TestService5.go中
|
||||||
|
|
||||||
@@ -713,53 +669,68 @@ func (slf *TestService4) TriggerEvent(){
|
|||||||
package simple_event
|
package simple_event
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/duanhf2012/origin/v2/event"
|
"github.com/duanhf2012/origin/v2/event"
|
||||||
"github.com/duanhf2012/origin/v2/node"
|
"github.com/duanhf2012/origin/v2/node"
|
||||||
"github.com/duanhf2012/origin/v2/service"
|
"github.com/duanhf2012/origin/v2/service"
|
||||||
|
"github.com/duanhf2012/origin/v2/util/timer"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init(){
|
func init() {
|
||||||
node.Setup(&TestService5{})
|
node.Setup(&TestService5{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
//自定义事件类型,必需从event.Sys_Event_User_Define开始
|
||||||
|
//event.Sys_Event_User_Define以内给系统预留
|
||||||
|
EVENT1 event.EventType = event.Sys_Event_User_Define + 1
|
||||||
|
)
|
||||||
|
|
||||||
type TestService5 struct {
|
type TestService5 struct {
|
||||||
service.Service
|
service.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestModule struct {
|
type TestModule struct {
|
||||||
service.Module
|
service.Module
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *TestModule) OnInit() error{
|
func (slf *TestModule) OnInit() error {
|
||||||
//在当前node中查找TestService4
|
//在TestModule中注册监听EVENT1事件
|
||||||
pService := node.GetService("TestService4")
|
slf.GetEventProcessor().RegEventReceiverFunc(EVENT1, slf.GetEventHandler(), slf.OnModuleEvent)
|
||||||
|
|
||||||
//在TestModule中,往TestService4中注册EVENT1类型事件监听
|
return nil
|
||||||
pService.(*TestService4).GetEventProcessor().RegEventReciverFunc(EVENT1,slf.GetEventHandler(),slf.OnModuleEvent)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *TestModule) OnModuleEvent(ev event.IEvent){
|
// OnModuleEvent 模块监听事件回调
|
||||||
event := ev.(*event.Event)
|
func (slf *TestModule) OnModuleEvent(ev event.IEvent) {
|
||||||
fmt.Printf("OnModuleEvent type :%d data:%+v\n",event.GetEventType(),event.Data)
|
event := ev.(*event.Event)
|
||||||
|
fmt.Printf("OnModuleEvent type :%d data:%+v\n", event.GetEventType(), event.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnInit 服务初始化函数,在安装服务时,服务将自动调用OnInit函数
|
||||||
//服务初始化函数,在安装服务时,服务将自动调用OnInit函数
|
|
||||||
func (slf *TestService5) OnInit() error {
|
func (slf *TestService5) OnInit() error {
|
||||||
//通过服务名获取服务对象
|
//在服务中注册监听EVENT1类型事件
|
||||||
pService := node.GetService("TestService4")
|
slf.RegEventReceiverFunc(EVENT1, slf.GetEventHandler(), slf.OnServiceEvent)
|
||||||
|
slf.AddModule(&TestModule{})
|
||||||
|
|
||||||
////在TestModule中,往TestService4中注册EVENT1类型事件监听
|
slf.AfterFunc(time.Second*10, slf.TriggerEvent)
|
||||||
pService.(*TestService4).GetEventProcessor().RegEventReciverFunc(EVENT1,slf.GetEventHandler(),slf.OnServiceEvent)
|
return nil
|
||||||
slf.AddModule(&TestModule{})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *TestService5) OnServiceEvent(ev event.IEvent){
|
// OnServiceEvent 服务监听事件回调
|
||||||
event := ev.(*event.Event)
|
func (slf *TestService5) OnServiceEvent(ev event.IEvent) {
|
||||||
fmt.Printf("OnServiceEvent type :%d data:%+v\n",event.Type,event.Data)
|
event := ev.(*event.Event)
|
||||||
|
fmt.Printf("OnServiceEvent type :%d data:%+v\n", event.Type, event.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *TestService5) TriggerEvent(t *timer.Timer) {
|
||||||
|
//广播事件,传入event.Event对象,类型为EVENT1,Data可以自定义任何数据
|
||||||
|
//这样,所有监听者都可以收到该事件
|
||||||
|
slf.GetEventHandler().NotifyEvent(&event.Event{
|
||||||
|
Type: EVENT1,
|
||||||
|
Data: "event data.",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -768,8 +739,8 @@ func (slf *TestService5) OnServiceEvent(ev event.IEvent){
|
|||||||
程序运行10秒后,调用slf.TriggerEvent函数广播事件,于是在TestService5中会收到
|
程序运行10秒后,调用slf.TriggerEvent函数广播事件,于是在TestService5中会收到
|
||||||
|
|
||||||
```
|
```
|
||||||
OnServiceEvent type :1001 data:event data.
|
OnServiceEvent type :2 data:event data.
|
||||||
OnModuleEvent type :1001 data:event data.
|
OnModuleEvent type :2 data:event data.
|
||||||
```
|
```
|
||||||
|
|
||||||
在上面的TestModule中监听的事情,当这个Module被Release时监听会自动卸载。
|
在上面的TestModule中监听的事情,当这个Module被Release时监听会自动卸载。
|
||||||
|
|||||||
@@ -297,12 +297,12 @@ func GetRpcClient(nodeId string, serviceMethod string,filterRetire bool, clientL
|
|||||||
if nodeId != rpc.NodeIdNull {
|
if nodeId != rpc.NodeIdNull {
|
||||||
pClient,retire := GetCluster().GetRpcClient(nodeId)
|
pClient,retire := GetCluster().GetRpcClient(nodeId)
|
||||||
if pClient == nil {
|
if pClient == nil {
|
||||||
return fmt.Errorf("cannot find nodeid %d!", nodeId), nil
|
return fmt.Errorf("cannot find nodeid %s", nodeId), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//如果需要筛选掉退休结点
|
//如果需要筛选掉退休结点
|
||||||
if filterRetire == true && retire == true {
|
if filterRetire == true && retire == true {
|
||||||
return fmt.Errorf("cannot find nodeid %d!", nodeId), nil
|
return fmt.Errorf("cannot find nodeid %s", nodeId), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
clientList = append(clientList,pClient)
|
clientList = append(clientList,pClient)
|
||||||
@@ -466,19 +466,24 @@ func (cls *Cluster) GetNodeInfo(nodeId string) (NodeInfo,bool) {
|
|||||||
return nodeInfo.nodeInfo,true
|
return nodeInfo.nodeInfo,true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *Cluster) CanDiscoveryService(fromMasterNodeId string,serviceName string) bool{
|
func (cls *Cluster) CanDiscoveryService(fromMasterNodeId string,serviceName string) bool{
|
||||||
canDiscovery := true
|
canDiscovery := true
|
||||||
|
|
||||||
for i:=0;i<len(dc.GetLocalNodeInfo().DiscoveryService);i++{
|
splitServiceName := strings.Split(serviceName,":")
|
||||||
masterNodeId := dc.GetLocalNodeInfo().DiscoveryService[i].MasterNodeId
|
if len(splitServiceName) == 2 {
|
||||||
|
serviceName = splitServiceName[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i:=0;i<len(cls.GetLocalNodeInfo().DiscoveryService);i++{
|
||||||
|
masterNodeId := cls.GetLocalNodeInfo().DiscoveryService[i].MasterNodeId
|
||||||
//无效的配置,则跳过
|
//无效的配置,则跳过
|
||||||
if masterNodeId == rpc.NodeIdNull && len(dc.GetLocalNodeInfo().DiscoveryService[i].ServiceList)==0 {
|
if masterNodeId == rpc.NodeIdNull && len(cls.GetLocalNodeInfo().DiscoveryService[i].ServiceList)==0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
canDiscovery = false
|
canDiscovery = false
|
||||||
if masterNodeId == fromMasterNodeId || masterNodeId == rpc.NodeIdNull {
|
if masterNodeId == fromMasterNodeId || masterNodeId == rpc.NodeIdNull {
|
||||||
for _,discoveryService := range dc.GetLocalNodeInfo().DiscoveryService[i].ServiceList {
|
for _,discoveryService := range cls.GetLocalNodeInfo().DiscoveryService[i].ServiceList {
|
||||||
if discoveryService == serviceName {
|
if discoveryService == serviceName {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,12 @@ func (dc *OriginDiscoveryMaster) OnNatsDisconnect(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ds *OriginDiscoveryMaster) OnNodeConnected(nodeId string) {
|
func (ds *OriginDiscoveryMaster) OnNodeConnected(nodeId string) {
|
||||||
|
var notifyDiscover rpc.SubscribeDiscoverNotify
|
||||||
|
notifyDiscover.IsFull = true
|
||||||
|
notifyDiscover.NodeInfo = ds.nodeInfo
|
||||||
|
notifyDiscover.MasterNodeId = cluster.GetLocalNodeInfo().NodeId
|
||||||
|
|
||||||
|
ds.GoNode(nodeId, SubServiceDiscover, ¬ifyDiscover)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *OriginDiscoveryMaster) OnNodeDisconnect(nodeId string) {
|
func (ds *OriginDiscoveryMaster) OnNodeDisconnect(nodeId string) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const (
|
|||||||
Sys_Event_Retire EventType = -11
|
Sys_Event_Retire EventType = -11
|
||||||
Sys_Event_EtcdDiscovery EventType = -12
|
Sys_Event_EtcdDiscovery EventType = -12
|
||||||
Sys_Event_Gin_Event EventType = -13
|
Sys_Event_Gin_Event EventType = -13
|
||||||
|
Sys_Event_FrameTick EventType = -14
|
||||||
|
|
||||||
Sys_Event_User_Define EventType = 1
|
Sys_Event_User_Define EventType = 1
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -328,13 +328,13 @@ func startNode(args interface{}) error {
|
|||||||
myName, mErr := sysprocess.GetMyProcessName()
|
myName, mErr := sysprocess.GetMyProcessName()
|
||||||
//当前进程名获取失败,不应该发生
|
//当前进程名获取失败,不应该发生
|
||||||
if mErr != nil {
|
if mErr != nil {
|
||||||
log.SInfo("get my process's name is error,", mErr.Error())
|
log.Info("get my process's name is error",log.ErrorAttr("err", mErr))
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
//进程id存在,而且进程名也相同,被认为是当前进程重复运行
|
//进程id存在,而且进程名也相同,被认为是当前进程重复运行
|
||||||
if cErr == nil && name == myName {
|
if cErr == nil && name == myName {
|
||||||
log.SInfo(fmt.Sprintf("repeat runs are not allowed,node is %s,processid is %d",strNodeId,processId))
|
log.Info("repeat runs are not allowed",log.String("nodeId",strNodeId),log.Int("processId",processId))
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -145,14 +145,12 @@ func DefaultReportFunction(name string,callNum int,costTime time.Duration,record
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var strReport string
|
|
||||||
strReport = "Profiler report tag "+name+":\n"
|
|
||||||
var average int64
|
var average int64
|
||||||
if callNum>0 {
|
if callNum>0 {
|
||||||
average = costTime.Milliseconds()/int64(callNum)
|
average = costTime.Milliseconds()/int64(callNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
strReport += fmt.Sprintf("process count %d,take time %d Milliseconds,average %d Milliseconds/per.\n",callNum,costTime.Milliseconds(),average)
|
log.Info("Profiler report tag "+name,log.Int("process count",callNum),log.Int64("take time",costTime.Milliseconds()),log.Int64("average",average))
|
||||||
elem := record.Front()
|
elem := record.Front()
|
||||||
var strTypes string
|
var strTypes string
|
||||||
for elem!=nil {
|
for elem!=nil {
|
||||||
@@ -163,11 +161,9 @@ func DefaultReportFunction(name string,callNum int,costTime time.Duration,record
|
|||||||
strTypes = "slow process"
|
strTypes = "slow process"
|
||||||
}
|
}
|
||||||
|
|
||||||
strReport += fmt.Sprintf("%s:%s is take %d Milliseconds\n",strTypes,pRecord.RecordName,pRecord.CostTime.Milliseconds())
|
log.Info("Profiler report type",log.String("Types",strTypes),log.String("RecordName",pRecord.RecordName),log.Int64("take time",pRecord.CostTime.Milliseconds()))
|
||||||
elem = elem.Next()
|
elem = elem.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.SInfo("report",strReport)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Report() {
|
func Report() {
|
||||||
|
|||||||
@@ -151,28 +151,28 @@ func (s *Service) Start() {
|
|||||||
s.startStatus = true
|
s.startStatus = true
|
||||||
atomic.StoreInt32(&s.isRelease,0)
|
atomic.StoreInt32(&s.isRelease,0)
|
||||||
var waitRun sync.WaitGroup
|
var waitRun sync.WaitGroup
|
||||||
|
log.Info(s.GetName()+" service is running",)
|
||||||
|
s.self.(IService).OnStart()
|
||||||
|
|
||||||
for i:=int32(0);i< s.goroutineNum;i++{
|
for i:=int32(0);i< s.goroutineNum;i++{
|
||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
waitRun.Add(1)
|
waitRun.Add(1)
|
||||||
go func(){
|
go func(){
|
||||||
log.Info(s.GetName()+" service is running",)
|
|
||||||
waitRun.Done()
|
waitRun.Done()
|
||||||
s.Run()
|
s.run()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
waitRun.Wait()
|
waitRun.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Run() {
|
func (s *Service) run() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
var bStop = false
|
var bStop = false
|
||||||
|
|
||||||
concurrent := s.IConcurrent.(*concurrent.Concurrent)
|
concurrent := s.IConcurrent.(*concurrent.Concurrent)
|
||||||
concurrentCBChannel := concurrent.GetCallBackChannel()
|
concurrentCBChannel := concurrent.GetCallBackChannel()
|
||||||
|
|
||||||
s.self.(IService).OnStart()
|
|
||||||
for{
|
for{
|
||||||
var analyzer *profiler.Analyzer
|
var analyzer *profiler.Analyzer
|
||||||
select {
|
select {
|
||||||
|
|||||||
203
sysmodule/frametimer/FrameGroup.go
Normal file
203
sysmodule/frametimer/FrameGroup.go
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
package frametimer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/duanhf2012/origin/v2/event"
|
||||||
|
"github.com/duanhf2012/origin/v2/log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TimerCB func(context.Context, FrameTimerID)
|
||||||
|
|
||||||
|
type _timerHeap struct {
|
||||||
|
timers []*timerData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *_timerHeap) Len() int {
|
||||||
|
return len(h.timers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *_timerHeap) Less(i, j int) bool {
|
||||||
|
return h.timers[i].frameNum < h.timers[j].frameNum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *_timerHeap) Swap(i, j int) {
|
||||||
|
h.timers[i], h.timers[j] = h.timers[j], h.timers[i]
|
||||||
|
h.timers[i].idx = i
|
||||||
|
h.timers[j].idx = j
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *_timerHeap) Push(x interface{}) {
|
||||||
|
td := x.(*timerData)
|
||||||
|
h.timers = append(h.timers, td)
|
||||||
|
td.idx = len(h.timers) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *_timerHeap) Pop() (ret interface{}) {
|
||||||
|
l := len(h.timers)
|
||||||
|
h.timers, ret = h.timers[:l-1], h.timers[l-1]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type FrameGroup struct {
|
||||||
|
groupID FrameGroupID
|
||||||
|
timerHeap _timerHeap
|
||||||
|
ft *FrameTimer
|
||||||
|
|
||||||
|
preTickGlobalFrameNum FrameNumber // 上次tick全局帧
|
||||||
|
preGlobalFrameNum FrameNumber // 上次设置的全局帧,用于更新FrameTimer.mapFrameGroup关系
|
||||||
|
frameNum FrameNumber // 当前帧
|
||||||
|
|
||||||
|
pause bool // 暂停状态
|
||||||
|
multiple uint8 // 位数,默认1倍,只允许1-5倍数
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *FrameGroup) init() {
|
||||||
|
fg.timerHeap.timers = make([]*timerData, 0, 512)
|
||||||
|
fg.groupID = fg.ft.genGroupID()
|
||||||
|
fg.multiple = 1
|
||||||
|
heap.Init(&fg.timerHeap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *FrameGroup) convertGlobalFrameNum(frameNum FrameNumber) FrameNumber {
|
||||||
|
return fg.ft.getGlobalFrameNumber() + (frameNum-fg.frameNum)/FrameNumber(fg.multiple)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *FrameGroup) refreshMinFrame() {
|
||||||
|
if fg.timerHeap.Len() == 0 || fg.pause {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
globalFrameNum := fg.convertGlobalFrameNum(fg.timerHeap.timers[0].frameNum)
|
||||||
|
fg.ft.refreshGroupMinFrame(fg.groupID, fg.preGlobalFrameNum, globalFrameNum)
|
||||||
|
fg.preGlobalFrameNum = globalFrameNum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *FrameGroup) tick(globalFrame FrameNumber) {
|
||||||
|
fg.frameNum = fg.frameNum + (globalFrame-fg.preTickGlobalFrameNum)*FrameNumber(fg.multiple)
|
||||||
|
fg.preTickGlobalFrameNum = globalFrame
|
||||||
|
|
||||||
|
fg.onceTick()
|
||||||
|
|
||||||
|
fg.refreshMinFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *FrameGroup) onceTick() {
|
||||||
|
for fg.timerHeap.Len() > 0 {
|
||||||
|
if fg.timerHeap.timers[0].frameNum > fg.frameNum {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
t := heap.Pop(&fg.timerHeap).(*timerData)
|
||||||
|
|
||||||
|
ev := event.NewEvent()
|
||||||
|
ev.Type = event.Sys_Event_FrameTick
|
||||||
|
ev.Data = t
|
||||||
|
fg.ft.NotifyEvent(ev)
|
||||||
|
fg.ft.removeTimerData(t.timerID)
|
||||||
|
|
||||||
|
if t.tickerFrameNum != 0 {
|
||||||
|
fg.addTicker(t.timerID, t.tickerFrameNum, t.ctx, t.cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *FrameGroup) addTimer(timerID FrameTimerID, frame FrameNumber, ctx context.Context, cb TimerCB) {
|
||||||
|
nextFrame := fg.frameNum + frame
|
||||||
|
|
||||||
|
td := fg.ft.addTimerData(timerID, nextFrame, 0, ctx, cb)
|
||||||
|
heap.Push(&fg.timerHeap, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *FrameGroup) addTicker(timerID FrameTimerID, frame FrameNumber, ctx context.Context, cb TimerCB) {
|
||||||
|
nextFrame := fg.frameNum + frame
|
||||||
|
|
||||||
|
td := fg.ft.addTimerData(timerID, nextFrame, frame, ctx, cb)
|
||||||
|
heap.Push(&fg.timerHeap, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMultiple 设置倍数,允许倍数范围1-5
|
||||||
|
func (fg *FrameGroup) SetMultiple(multiple uint8) error {
|
||||||
|
if fg.multiple == multiple {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if multiple < 0 || multiple > maxMultiple {
|
||||||
|
return errors.New("invalid multiplier")
|
||||||
|
}
|
||||||
|
|
||||||
|
fg.multiple = multiple
|
||||||
|
|
||||||
|
fg.refreshMinFrame()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FrameAfterFunc 创建After定时器
|
||||||
|
func (fg *FrameGroup) FrameAfterFunc(timerID *FrameTimerID, d time.Duration, ctx context.Context, cb TimerCB) {
|
||||||
|
fg.ft.locker.Lock()
|
||||||
|
defer fg.ft.locker.Unlock()
|
||||||
|
|
||||||
|
frame := FrameNumber(d / fg.ft.oneFrameTime)
|
||||||
|
newTimerID := fg.ft.genTimerID()
|
||||||
|
|
||||||
|
fg.addTimer(newTimerID, frame, ctx, cb)
|
||||||
|
*timerID = newTimerID
|
||||||
|
fg.refreshMinFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FrameNewTicker 创建Ticker定时器
|
||||||
|
func (fg *FrameGroup) FrameNewTicker(timerID *FrameTimerID, d time.Duration, ctx context.Context, cb TimerCB) {
|
||||||
|
fg.ft.locker.Lock()
|
||||||
|
defer fg.ft.locker.Unlock()
|
||||||
|
|
||||||
|
frame := FrameNumber(d / fg.ft.oneFrameTime)
|
||||||
|
newTimerID := fg.ft.genTimerID()
|
||||||
|
|
||||||
|
fg.addTicker(newTimerID, frame, ctx, cb)
|
||||||
|
*timerID = newTimerID
|
||||||
|
fg.refreshMinFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause 暂停定时器组
|
||||||
|
func (fg *FrameGroup) Pause() {
|
||||||
|
fg.ft.locker.Lock()
|
||||||
|
defer fg.ft.locker.Unlock()
|
||||||
|
|
||||||
|
fg.pause = true
|
||||||
|
fg.ft.removeGroup(fg.groupID, fg.preGlobalFrameNum)
|
||||||
|
fg.preGlobalFrameNum = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume 唤醒定时器组
|
||||||
|
func (fg *FrameGroup) Resume() {
|
||||||
|
fg.ft.locker.Lock()
|
||||||
|
defer fg.ft.locker.Unlock()
|
||||||
|
|
||||||
|
fg.pause = false
|
||||||
|
fg.refreshMinFrame()
|
||||||
|
fg.preTickGlobalFrameNum = fg.ft.globalFrameNum
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelTimer 关闭定时器
|
||||||
|
func (fg *FrameGroup) CancelTimer(timerID FrameTimerID) {
|
||||||
|
fg.ft.locker.Lock()
|
||||||
|
defer fg.ft.locker.Unlock()
|
||||||
|
|
||||||
|
td := fg.ft.getTimerData(timerID)
|
||||||
|
if td == nil {
|
||||||
|
log.Error("cannot find timer", log.Uint64("timerID", uint64(timerID)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
heap.Remove(&fg.timerHeap, td.idx)
|
||||||
|
fg.ft.removeGroup(fg.groupID, fg.preGlobalFrameNum)
|
||||||
|
fg.preGlobalFrameNum = 0
|
||||||
|
fg.refreshMinFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *FrameGroup) Close(){
|
||||||
|
fg.ft.removeGroup(fg.groupID, fg.preGlobalFrameNum)
|
||||||
|
delete(fg.ft.mapGroup,fg.groupID)
|
||||||
|
}
|
||||||
199
sysmodule/frametimer/FrameTimerModule.go
Normal file
199
sysmodule/frametimer/FrameTimerModule.go
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package frametimer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/duanhf2012/origin/v2/event"
|
||||||
|
"github.com/duanhf2012/origin/v2/log"
|
||||||
|
"github.com/duanhf2012/origin/v2/service"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultFps = 50
|
||||||
|
const defaultSleepInterval = time.Millisecond * 3
|
||||||
|
const maxFps = 1000
|
||||||
|
const maxMultiple = 5
|
||||||
|
|
||||||
|
type FrameGroupID uint64
|
||||||
|
type FrameTimerID uint64
|
||||||
|
type FrameNumber uint64
|
||||||
|
|
||||||
|
type timerData struct {
|
||||||
|
frameNum FrameNumber
|
||||||
|
timerID FrameTimerID
|
||||||
|
idx int
|
||||||
|
cb TimerCB
|
||||||
|
tickerFrameNum FrameNumber
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type FrameTimer struct {
|
||||||
|
service.Module
|
||||||
|
fps uint32
|
||||||
|
oneFrameTime time.Duration
|
||||||
|
ticker *time.Ticker
|
||||||
|
|
||||||
|
mapFrameGroup map[FrameNumber]map[FrameGroupID]struct{}
|
||||||
|
mapGroup map[FrameGroupID]*FrameGroup
|
||||||
|
globalFrameNum FrameNumber // 当前帧
|
||||||
|
|
||||||
|
maxTimerID FrameTimerID
|
||||||
|
maxGroupID FrameGroupID
|
||||||
|
|
||||||
|
mapTimer map[FrameTimerID]*timerData
|
||||||
|
timerDataPool *sync.Pool
|
||||||
|
|
||||||
|
locker sync.Mutex
|
||||||
|
sleepInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) getTimerData(timerID FrameTimerID) *timerData {
|
||||||
|
return ft.mapTimer[timerID]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) addTimerData(timerID FrameTimerID, frameNum FrameNumber, tickerFrameNum FrameNumber, ctx context.Context, cb TimerCB) *timerData {
|
||||||
|
td := ft.timerDataPool.Get().(*timerData)
|
||||||
|
td.timerID = timerID
|
||||||
|
td.frameNum = frameNum
|
||||||
|
td.cb = cb
|
||||||
|
td.idx = -1
|
||||||
|
td.tickerFrameNum = tickerFrameNum
|
||||||
|
|
||||||
|
ft.mapTimer[timerID] = td
|
||||||
|
return td
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) removeTimerData(timerID FrameTimerID) {
|
||||||
|
td := ft.mapTimer[timerID]
|
||||||
|
if td == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ft.timerDataPool.Put(td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) genGroupID() FrameGroupID {
|
||||||
|
ft.maxGroupID++
|
||||||
|
return ft.maxGroupID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) genTimerID() FrameTimerID {
|
||||||
|
ft.maxTimerID++
|
||||||
|
return ft.maxTimerID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) getGlobalFrameNumber() FrameNumber {
|
||||||
|
return ft.globalFrameNum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) frameTick() {
|
||||||
|
preFrameNum := ft.globalFrameNum
|
||||||
|
ft.globalFrameNum++
|
||||||
|
|
||||||
|
ft.locker.Lock()
|
||||||
|
defer ft.locker.Unlock()
|
||||||
|
for i := preFrameNum; i <= ft.globalFrameNum; i++ {
|
||||||
|
mapGroup := ft.mapFrameGroup[i]
|
||||||
|
delete(ft.mapFrameGroup, i)
|
||||||
|
for groupID := range mapGroup {
|
||||||
|
group := ft.mapGroup[groupID]
|
||||||
|
if group == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
group.tick(ft.globalFrameNum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) removeGroup(groupID FrameGroupID, frameNum FrameNumber) {
|
||||||
|
delete(ft.mapFrameGroup[frameNum], groupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) refreshGroupMinFrame(groupID FrameGroupID, preFrameNum FrameNumber, newFrameNum FrameNumber) {
|
||||||
|
ft.removeGroup(groupID, preFrameNum)
|
||||||
|
|
||||||
|
mapGroup := ft.mapFrameGroup[newFrameNum]
|
||||||
|
if mapGroup == nil {
|
||||||
|
mapGroup = make(map[FrameGroupID]struct{}, 6)
|
||||||
|
ft.mapFrameGroup[newFrameNum] = mapGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
mapGroup[groupID] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *FrameTimer) OnInit() error {
|
||||||
|
ft.mapFrameGroup = make(map[FrameNumber]map[FrameGroupID]struct{}, 1024)
|
||||||
|
ft.mapGroup = make(map[FrameGroupID]*FrameGroup, 1024)
|
||||||
|
ft.mapTimer = make(map[FrameTimerID]*timerData, 2048)
|
||||||
|
ft.timerDataPool = &sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return &timerData{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if ft.fps == 0 {
|
||||||
|
ft.fps = defaultFps
|
||||||
|
}
|
||||||
|
|
||||||
|
ft.GetEventProcessor().RegEventReceiverFunc(event.Sys_Event_FrameTick, ft.GetEventHandler(), func(e event.IEvent) {
|
||||||
|
ev := e.(*event.Event)
|
||||||
|
td, ok := ev.Data.(*timerData)
|
||||||
|
if !ok {
|
||||||
|
log.Error("convert *timerData error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
td.cb(td.ctx, td.timerID)
|
||||||
|
event.DeleteEvent(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
ft.oneFrameTime = time.Second / time.Duration(ft.fps)
|
||||||
|
ft.ticker = time.NewTicker(ft.oneFrameTime)
|
||||||
|
|
||||||
|
if ft.sleepInterval == 0 {
|
||||||
|
ft.sleepInterval = defaultSleepInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
preTime := time.Now()
|
||||||
|
var preFrame FrameNumber
|
||||||
|
|
||||||
|
for {
|
||||||
|
time.Sleep(ft.sleepInterval)
|
||||||
|
frameMax := FrameNumber(time.Now().Sub(preTime) / ft.oneFrameTime)
|
||||||
|
for i := preFrame + 1; i <= frameMax; i++ {
|
||||||
|
ft.frameTick()
|
||||||
|
}
|
||||||
|
|
||||||
|
preFrame = frameMax
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFps 设置帧率,越大误差越低。如果有倍数加速需求,可以适当加大fps,以减少误差。默认50fps
|
||||||
|
func (ft *FrameTimer) SetFps(fps uint32) {
|
||||||
|
if fps > maxFps {
|
||||||
|
fps = maxFps
|
||||||
|
}
|
||||||
|
|
||||||
|
ft.fps = fps
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAccuracyInterval 设置时间间隔精度,在循环中sleep该时间进行判断。实际上因为sleep有误差,所以暂时不使用fps得出。默认为3ms
|
||||||
|
func (ft *FrameTimer) SetAccuracyInterval(interval time.Duration) {
|
||||||
|
ft.sleepInterval = interval
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGroup 创建定时器组
|
||||||
|
func (ft *FrameTimer) NewGroup() *FrameGroup {
|
||||||
|
var group FrameGroup
|
||||||
|
group.ft = ft
|
||||||
|
group.init()
|
||||||
|
|
||||||
|
ft.locker.Lock()
|
||||||
|
defer ft.locker.Unlock()
|
||||||
|
ft.mapGroup[group.groupID] = &group
|
||||||
|
return &group
|
||||||
|
}
|
||||||
@@ -90,7 +90,7 @@ func (gm *GinModule) StartTLS(certFile, keyFile string) {
|
|||||||
|
|
||||||
func (gm *GinModule) Stop(ctx context.Context) {
|
func (gm *GinModule) Stop(ctx context.Context) {
|
||||||
if err := gm.srv.Shutdown(ctx); err != nil {
|
if err := gm.srv.Shutdown(ctx); err != nil {
|
||||||
log.SError("Server Shutdown", slog.Any("error", err))
|
log.Error("Server Shutdown", slog.Any("error", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func Logger() gin.HandlerFunc {
|
|||||||
// 响应状态码
|
// 响应状态码
|
||||||
statusCode := c.Writer.Status()
|
statusCode := c.Writer.Status()
|
||||||
|
|
||||||
log.SDebug(fmt.Sprintf(
|
log.Debug(fmt.Sprintf(
|
||||||
"%s | %3d | %s %10s | \033[44;37m%-6s\033[0m %s %s | %10v | \"%s\" \"%s\"",
|
"%s | %3d | %s %10s | \033[44;37m%-6s\033[0m %s %s | %10v | \"%s\" \"%s\"",
|
||||||
colorForStatus(statusCode),
|
colorForStatus(statusCode),
|
||||||
statusCode,
|
statusCode,
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
package math
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/duanhf2012/origin/v2/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NumberType interface {
|
|
||||||
int | int8 | int16 | int32 | int64 | float32 | float64 | uint | uint8 | uint16 | uint32 | uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type SignedNumberType interface {
|
|
||||||
int | int8 | int16 | int32 | int64 | float32 | float64
|
|
||||||
}
|
|
||||||
|
|
||||||
type FloatType interface {
|
|
||||||
float32 | float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func Max[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
|
||||||
if number1 > number2 {
|
|
||||||
return number1
|
|
||||||
}
|
|
||||||
|
|
||||||
return number2
|
|
||||||
}
|
|
||||||
|
|
||||||
func Min[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
|
||||||
if number1 < number2 {
|
|
||||||
return number1
|
|
||||||
}
|
|
||||||
|
|
||||||
return number2
|
|
||||||
}
|
|
||||||
|
|
||||||
func Abs[NumType SignedNumberType](Num NumType) NumType {
|
|
||||||
if Num < 0 {
|
|
||||||
return -1 * Num
|
|
||||||
}
|
|
||||||
|
|
||||||
return Num
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddSafe[NumType NumberType](number1 NumType, number2 NumType) (NumType, bool) {
|
|
||||||
ret := number1 + number2
|
|
||||||
if number2 > 0 && ret < number1 {
|
|
||||||
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
|
||||||
return ret, false
|
|
||||||
} else if number2 < 0 && ret > number1 {
|
|
||||||
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
|
||||||
return ret, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func SubSafe[NumType NumberType](number1 NumType, number2 NumType) (NumType, bool) {
|
|
||||||
ret := number1 - number2
|
|
||||||
if number2 > 0 && ret > number1 {
|
|
||||||
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
|
||||||
return ret, false
|
|
||||||
} else if number2 < 0 && ret < number1 {
|
|
||||||
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
|
||||||
return ret, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func MulSafe[NumType NumberType](number1 NumType, number2 NumType) (NumType, bool) {
|
|
||||||
ret := number1 * number2
|
|
||||||
if number1 == 0 || number2 == 0 {
|
|
||||||
return ret, true
|
|
||||||
}
|
|
||||||
|
|
||||||
if ret/number2 == number1 {
|
|
||||||
return ret, true
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
|
||||||
return ret, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func Add[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
|
||||||
ret, _ := AddSafe(number1, number2)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func Sub[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
|
||||||
ret, _ := SubSafe(number1, number2)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mul[NumType NumberType](number1 NumType, number2 NumType) NumType {
|
|
||||||
ret, _ := MulSafe(number1, number2)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// 安全的求比例
|
|
||||||
func PercentRateSafe[NumType NumberType, OutNumType NumberType](maxValue int64, rate NumType, numbers ...NumType) (OutNumType, bool) {
|
|
||||||
// 比例不能为负数
|
|
||||||
if rate < 0 {
|
|
||||||
log.Stack("rate must not positive")
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if rate == 0 {
|
|
||||||
// 比例为0
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := int64(rate)
|
|
||||||
for _, number := range numbers {
|
|
||||||
number64 := int64(number)
|
|
||||||
result, success := MulSafe(number64, ret)
|
|
||||||
if !success {
|
|
||||||
// 基数*比例越界了,int64都越界了,没办法了
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = result
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = ret / 10000
|
|
||||||
if ret > maxValue {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return OutNumType(ret), true
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
package rand
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
}
|
|
||||||
|
|
||||||
func RandGroup(p ...uint32) int {
|
|
||||||
if p == nil {
|
|
||||||
panic("args not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
r := make([]uint32, len(p))
|
|
||||||
for i := 0; i < len(p); i++ {
|
|
||||||
if i == 0 {
|
|
||||||
r[0] = p[0]
|
|
||||||
} else {
|
|
||||||
r[i] = r[i-1] + p[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rl := r[len(r)-1]
|
|
||||||
if rl == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
rn := uint32(rand.Int63n(int64(rl)))
|
|
||||||
for i := 0; i < len(r); i++ {
|
|
||||||
if rn < r[i] {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
panic("bug")
|
|
||||||
}
|
|
||||||
|
|
||||||
func RandInterval(b1, b2 int32) int32 {
|
|
||||||
if b1 == b2 {
|
|
||||||
return b1
|
|
||||||
}
|
|
||||||
|
|
||||||
min, max := int64(b1), int64(b2)
|
|
||||||
if min > max {
|
|
||||||
min, max = max, min
|
|
||||||
}
|
|
||||||
return int32(rand.Int63n(max-min+1) + min)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RandIntervalN(b1, b2 int32, n uint32) []int32 {
|
|
||||||
if b1 == b2 {
|
|
||||||
return []int32{b1}
|
|
||||||
}
|
|
||||||
|
|
||||||
min, max := int64(b1), int64(b2)
|
|
||||||
if min > max {
|
|
||||||
min, max = max, min
|
|
||||||
}
|
|
||||||
l := max - min + 1
|
|
||||||
if int64(n) > l {
|
|
||||||
n = uint32(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := make([]int32, n)
|
|
||||||
m := make(map[int32]int32)
|
|
||||||
for i := uint32(0); i < n; i++ {
|
|
||||||
v := int32(rand.Int63n(l) + min)
|
|
||||||
|
|
||||||
if mv, ok := m[v]; ok {
|
|
||||||
r[i] = mv
|
|
||||||
} else {
|
|
||||||
r[i] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
lv := int32(l - 1 + min)
|
|
||||||
if v != lv {
|
|
||||||
if mv, ok := m[lv]; ok {
|
|
||||||
m[v] = mv
|
|
||||||
} else {
|
|
||||||
m[v] = lv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l--
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
86
util/smath/smath.go
Normal file
86
util/smath/smath.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package smath
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duanhf2012/origin/v2/log"
|
||||||
|
"github.com/duanhf2012/origin/v2/util/typedef"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Max[NumType typedef.Number](number1 NumType, number2 NumType) NumType {
|
||||||
|
if number1 > number2 {
|
||||||
|
return number1
|
||||||
|
}
|
||||||
|
|
||||||
|
return number2
|
||||||
|
}
|
||||||
|
|
||||||
|
func Min[NumType typedef.Number](number1 NumType, number2 NumType) NumType {
|
||||||
|
if number1 < number2 {
|
||||||
|
return number1
|
||||||
|
}
|
||||||
|
|
||||||
|
return number2
|
||||||
|
}
|
||||||
|
|
||||||
|
func Abs[NumType typedef.Signed|typedef.Float](Num NumType) NumType {
|
||||||
|
if Num < 0 {
|
||||||
|
return -1 * Num
|
||||||
|
}
|
||||||
|
|
||||||
|
return Num
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSafe[NumType typedef.Number](number1 NumType, number2 NumType) (NumType, bool) {
|
||||||
|
ret := number1 + number2
|
||||||
|
if number2 > 0 && ret < number1 {
|
||||||
|
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
||||||
|
return ret, false
|
||||||
|
} else if number2 < 0 && ret > number1 {
|
||||||
|
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
||||||
|
return ret, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubSafe[NumType typedef.Number](number1 NumType, number2 NumType) (NumType, bool) {
|
||||||
|
ret := number1 - number2
|
||||||
|
if number2 > 0 && ret > number1 {
|
||||||
|
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
||||||
|
return ret, false
|
||||||
|
} else if number2 < 0 && ret < number1 {
|
||||||
|
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
||||||
|
return ret, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func MulSafe[NumType typedef.Number](number1 NumType, number2 NumType) (NumType, bool) {
|
||||||
|
ret := number1 * number2
|
||||||
|
if number1 == 0 || number2 == 0 {
|
||||||
|
return ret, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret/number2 == number1 {
|
||||||
|
return ret, true
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Stack("Calculation overflow", log.Any("number1", number1), log.Any("number2", number2))
|
||||||
|
return ret, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Add[NumType typedef.Number](number1 NumType, number2 NumType) NumType {
|
||||||
|
ret, _ := AddSafe(number1, number2)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sub[NumType typedef.Number](number1 NumType, number2 NumType) NumType {
|
||||||
|
ret, _ := SubSafe(number1, number2)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mul[NumType typedef.Number](number1 NumType, number2 NumType) NumType {
|
||||||
|
ret, _ := MulSafe(number1, number2)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
107
util/srand/slice.go
Normal file
107
util/srand/slice.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package srand
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duanhf2012/origin/v2/util/typedef"
|
||||||
|
"math/rand"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sum[E ~[]T, T typedef.Number](arr E) T {
|
||||||
|
var sum T
|
||||||
|
for i := range arr {
|
||||||
|
sum += arr[i]
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func SumFunc[E ~[]V, V any, T typedef.Number](arr E, getValue func(i int) T) T {
|
||||||
|
var sum T
|
||||||
|
for i := range arr {
|
||||||
|
sum += getValue(i)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func Shuffle[E ~[]T, T any](arr E) {
|
||||||
|
rand.Shuffle(len(arr), func(i, j int) {
|
||||||
|
arr[i], arr[j] = arr[j], arr[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandOne[E ~[]T, T any](arr E) T {
|
||||||
|
return arr[rand.Intn(len(arr))]
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandN[E ~[]T, T any](arr E, num int) []T {
|
||||||
|
index := make([]int, 0, len(arr))
|
||||||
|
for i := range arr {
|
||||||
|
index = append(index, i)
|
||||||
|
}
|
||||||
|
Shuffle(index)
|
||||||
|
if len(index) > num {
|
||||||
|
index = index
|
||||||
|
}
|
||||||
|
ret := make([]T, 0, len(index))
|
||||||
|
for i := range index {
|
||||||
|
ret = append(ret, arr[index[i]])
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandWeight[E ~[]T, T typedef.Integer](weights E) int {
|
||||||
|
totalWeight := Sum(weights)
|
||||||
|
if totalWeight <= 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
t := T(rand.Intn(int(totalWeight)))
|
||||||
|
for i := range weights {
|
||||||
|
if t < weights[i] {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
t -= weights[i]
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandWeightFunc[E ~[]U, U any, T typedef.Integer](arr E, getWeight func(i int) T) int {
|
||||||
|
weights := make([]T, 0, len(arr))
|
||||||
|
for i := range arr {
|
||||||
|
weights = append(weights, getWeight(i))
|
||||||
|
}
|
||||||
|
return RandWeight(weights)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Get[E ~[]T, T any, U typedef.Integer](arr E, index U) (ret T, ok bool) {
|
||||||
|
if index < 0 || int(index) >= len(arr) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = arr[index]
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPointer[E ~[]T, T any, U typedef.Integer](arr E, index U) *T {
|
||||||
|
if index < 0 || int(index) >= len(arr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &arr[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFunc[E ~[]T, T any](arr E, f func(T) bool) (ret T, ok bool) {
|
||||||
|
index := slices.IndexFunc(arr, f)
|
||||||
|
if index < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr[index], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPointerFunc[E ~[]T, T any](arr E, f func(T) bool) *T {
|
||||||
|
index := slices.IndexFunc(arr, f)
|
||||||
|
if index < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &arr[index]
|
||||||
|
}
|
||||||
25
util/typedef/type.go
Normal file
25
util/typedef/type.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package typedef
|
||||||
|
|
||||||
|
type Signed interface {
|
||||||
|
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Unsigned interface {
|
||||||
|
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Float interface {
|
||||||
|
~float32 | ~float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Integer interface {
|
||||||
|
Signed|Unsigned
|
||||||
|
}
|
||||||
|
|
||||||
|
type Number interface {
|
||||||
|
Signed|Unsigned|Float
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ordered interface {
|
||||||
|
Number|Float|~string
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user