diff --git a/busi/createUser.go b/busi/createUser.go new file mode 100644 index 0000000..e420d26 --- /dev/null +++ b/busi/createUser.go @@ -0,0 +1,21 @@ +package busi + +import "legu.airobot/lib" + +var _ lib.IScene = (*CreateUserScene)(nil) + +// 创角场景 +type CreateUserScene struct { + lib.Action +} + +func (c *CreateUserScene) Info() lib.SceneInfo { + return lib.SceneInfo{ + Name: "创角", + Desc: "", + } +} + +func (c *CreateUserScene) Run(robot lib.IRobot) error { + return nil +} diff --git a/busi/login.go b/busi/login.go new file mode 100644 index 0000000..4435007 --- /dev/null +++ b/busi/login.go @@ -0,0 +1,40 @@ +package busi + +import ( + "github.com/Pallinder/go-randomdata" + "github.com/sirupsen/logrus" + "legu.airobot/lib" + "legu.airobot/pb" +) + +var _ lib.IScene = (*LoginScene)(nil) + +// 登录/注册场景 +type LoginScene struct { + lib.Action +} + +func (l *LoginScene) Info() lib.SceneInfo { + return lib.SceneInfo{ + Name: "login", + Desc: "登录", + } +} + +func (l *LoginScene) Run(robot lib.IRobot) error { + randAccount := randomdata.SillyName() + sid := robot.Get("sid").(string) + + req := &pb.UserLoginReq{ + Account: randAccount, + Sid: sid, + } + rsp := &pb.UserLoginResp{} + + code := robot.SendMsg("user", "login", req, rsp) + if code == pb.ErrorCode_Success { + logrus.Debug(rsp) + } + logrus.Debug(code) + return nil +} diff --git a/go.mod b/go.mod index 1d4d2a4..faa00cc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.18 require ( fyne.io/fyne v1.4.3 fyne.io/fyne/v2 v2.2.4 + github.com/Pallinder/go-randomdata v1.2.0 github.com/gorilla/websocket v1.5.0 + github.com/json-iterator/go v1.1.11 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cast v1.3.1 google.golang.org/protobuf v1.28.1 @@ -25,6 +27,8 @@ require ( github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect diff --git a/go.sum b/go.sum index a565273..adb652d 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I= +github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg= +github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -207,6 +209,7 @@ github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMH github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI= github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -235,8 +238,10 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= diff --git a/lib/action.go b/lib/action.go index 4f9e133..9585de1 100644 --- a/lib/action.go +++ b/lib/action.go @@ -5,13 +5,12 @@ type IAction interface { } type Action struct { - // scene *scene - Id string - Name string - Desc string + Id string + Name string + Desc string } -func NewAction() *Action { +func NewAction(robot *Robot) *Action { a := &Action{} return a } diff --git a/lib/ai.go b/lib/ai.go index 6bab2dd..c90587c 100644 --- a/lib/ai.go +++ b/lib/ai.go @@ -6,15 +6,17 @@ import ( "sync" "github.com/sirupsen/logrus" + "legu.airobot/storage" ) type myAI struct { robots []*Robot scenes []*scene iscenes []IScene - tickets Tickets //票池 - robotCount uint32 //机器人数量 - lock sync.Mutex // + tickets Tickets //票池 + robotCount uint32 //机器人数量 + lock sync.Mutex // + config *storage.Config // } func NewAI(aip AIParam) (*myAI, error) { @@ -26,6 +28,8 @@ func NewAI(aip AIParam) (*myAI, error) { ai := &myAI{ scenes: make([]*scene, 0), robotCount: aip.RobotCount, + config: aip.Config, + iscenes: aip.Scenes, } if err := ai.init(); err != nil { @@ -38,6 +42,7 @@ func NewAI(aip AIParam) (*myAI, error) { func (m *myAI) init() error { var buf bytes.Buffer buf.WriteString("初始化AI") + tickets, err := NewTickets(m.robotCount) if err != nil { return err @@ -51,23 +56,29 @@ func (m *myAI) init() error { } //启动时载入所有Caller +// Deprecated func (m *myAI) LoadCallers() []IScene { return m.iscenes } -// 初始化caller -func (m *myAI) InitCaller(scenes ...IScene) { - m.iscenes = append(m.iscenes, scenes...) +// 根据场景名称获取场景接口 +// Deprecated +func (m *myAI) GetSceneInstance(sceneName string) IScene { + for _, v := range m.iscenes { + if v.Info().Name == sceneName { + return v + } + } + return nil } // 加入机器人 -func (m *myAI) AddRobot(scene *scene) { - // - robot := NewRobot() - robot.SelScene(scene) - m.robots = append(m.robots, robot) -} +// func (m *myAI) AddRobot(scene *scene) { +// robot := NewRobot() +// robot.SelScene(scene) +// m.robots = append(m.robots, robot) +// } // 获取场景下的机器人 func (m *myAI) GetRobots(sceneName string) (robots []*Robot) { @@ -80,6 +91,7 @@ func (m *myAI) GetRobots(sceneName string) (robots []*Robot) { return } +// Deprecated func (m *myAI) CurrentScene() *scene { for _, v := range m.scenes { if v.status == STATUS_ENABLE { @@ -96,31 +108,20 @@ func (m *myAI) appendRobot(robot *Robot) { } func (m *myAI) Start() bool { - if len(m.scenes) == 0 { + if len(m.config.Scenes) == 0 { logrus.Warn("还未设置场景") return false } - // 只有一个场景是启用状态,所有启动的机器人使用一个场景 - scene := m.CurrentScene() - if scene == nil { - logrus.Warn("至少要启用一个场景") - return false - } - // i := uint32(0); i < m.robotCount; i++ go func() { for { m.tickets.Take() go func() { - - // m.AddRobot(scene) - robot := NewRobot() - robot.SelScene(scene) + robot := NewRobot(m.config) + robot.SetScenes(m.iscenes) m.appendRobot(robot) - robot.Start() }() - } }() diff --git a/lib/base.go b/lib/base.go index 36cf7a8..4b9e876 100644 --- a/lib/base.go +++ b/lib/base.go @@ -2,6 +2,12 @@ package lib import "time" +type LoginParam struct { + Account string `json:"account"` + ServerId string `json:"serverId"` + TimeStamp int64 `json:"timestamp"` +} + type RawReq struct { ID int64 Req []byte diff --git a/lib/helper.go b/lib/helper.go index 7a3d046..fabc31a 100644 --- a/lib/helper.go +++ b/lib/helper.go @@ -1,15 +1,54 @@ package lib import ( + "crypto/md5" + "encoding/base64" "fmt" + "io" "math" "strconv" + "time" + + jsoniter "github.com/json-iterator/go" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" "legu.airobot/pb" ) +func Md5(content string) (md string) { + h := md5.New() + _, _ = io.WriteString(h, content) + md = fmt.Sprintf("%x", h.Sum(nil)) + return +} + +func Base64Encode(data []byte) string { + return base64.StdEncoding.EncodeToString(data) +} + +func Base64Decode(data string) string { + b, err := base64.StdEncoding.DecodeString(data) + if err != nil { + fmt.Errorf("base64 decode", err) + return "" + } + return string(b) +} + +func BuildSecStr(sid, account string) string { + jsonByte, _ := jsoniter.Marshal(&LoginParam{ + Account: account, + ServerId: sid, + TimeStamp: time.Now().Unix(), + }) + jsonBase64 := Base64Encode(jsonByte) + // log.Printf("client base64:%s", jsonBase64) + clientMd5key := Md5(jsonBase64) + // log.Printf("client md5:%s", clientMd5key) + return fmt.Sprintf("CE:%s%s", clientMd5key, jsonBase64) +} + func ProtoMarshal(rsp proto.Message, msg *pb.UserMessage) (ok bool) { any, err := anypb.New(rsp) if err != nil { diff --git a/lib/param.go b/lib/param.go index 50f1c78..81b38c2 100644 --- a/lib/param.go +++ b/lib/param.go @@ -7,9 +7,12 @@ import ( "strings" "github.com/sirupsen/logrus" + "legu.airobot/storage" ) type AIParam struct { + Config *storage.Config + Scenes []IScene // 机器人数量 RobotCount uint32 } diff --git a/lib/robot.go b/lib/robot.go index e2a3fd1..dabbd0b 100644 --- a/lib/robot.go +++ b/lib/robot.go @@ -1,8 +1,6 @@ package lib import ( - "errors" - "fmt" "strings" "sync" "sync/atomic" @@ -12,16 +10,10 @@ import ( "github.com/sirupsen/logrus" "google.golang.org/protobuf/proto" "legu.airobot/pb" + "legu.airobot/storage" ) type IRobot interface { - // 启动机器人 - // Start() bool - // 选择场景 - // SelScene(scene *scene) - // 当前场景 - // GetCurrentScene() *scene - // 发送消息 SendMsg(mainType, subType string, req proto.Message, rsp proto.Message) pb.ErrorCode // 存储数据 @@ -34,51 +26,106 @@ type IRobot interface { type Robot struct { IStore - scene *scene - conn *websocket.Conn - data map[string][]byte //caller缓存数据 - status uint32 //状态 - lock sync.Mutex // + account string + sid string + // Deprecated + scene *scene + conn *websocket.Conn + data map[string]interface{} //机器人缓存数据 + status uint32 //状态 + lock sync.Mutex // + sceneQueue *Queue[IScene] //场景队列 + config *storage.Config } -func NewRobot() *Robot { +func NewRobot(config *storage.Config) *Robot { robot := &Robot{ - data: make(map[string][]byte), + data: make(map[string]interface{}), + sceneQueue: NewQueue[IScene](), + config: config, } - + robot.Store("sid", config.Global.SId) return robot } //存数据 -func (a *Robot) Store(key string, data []byte) { +func (a *Robot) Store(key string, data interface{}) { defer a.lock.Unlock() a.lock.Lock() a.data[key] = data } //取数据 -func (a *Robot) Get(key string) []byte { +func (a *Robot) Get(key string) interface{} { defer a.lock.Unlock() a.lock.Lock() return a.data[key] } +// 发送消息 +func (r *Robot) SendMsg(mainType, subType string, req proto.Message, rsp proto.Message) pb.ErrorCode { + start := time.Now() + defer time.Since(start) + + logrus.WithFields(logrus.Fields{"MainType": mainType, "SubType": subType, "req": req, "rsp": rsp}).Debug("发送消息") + + head := &pb.UserMessage{MainType: mainType, SubType: subType} + if mainType == "user" && subType == "login" { + loginReq := req.(*pb.UserLoginReq) + head.Sec = BuildSecStr(loginReq.Sid, loginReq.Account) + } else { + head.Sec = BuildSecStr(r.sid, r.account) + } + + if ProtoMarshal(req, head) { + data, _ := proto.Marshal(head) + if err := r.conn.WriteMessage(websocket.BinaryMessage, data); err != nil { + logrus.WithField("err", err).Error("写数据异常") + return pb.ErrorCode_SystemError + } + + for { + _, data, err := r.conn.ReadMessage() + if err != nil { + logrus.WithField("err", err).Error("读数据异常") + break + } + + if r.checkResp(data, rsp) { + return pb.ErrorCode_Success + } + } + } + + return pb.ErrorCode_Success +} + +// 设置场景队列 +func (a *Robot) SetScenes(scenes []IScene) { + for _, conf := range a.config.Scenes { + for _, v := range scenes { + info := v.Info() + if conf.Name == info.Name { + a.sceneQueue.Add(v) + continue + } + } + } +} + +// Deprecated func (m *Robot) SelScene(scene *scene) { m.scene = scene } +// Deprecated func (m *Robot) GetCurrentScene() *scene { return m.scene } func (m *Robot) Start() bool { - if m.scene == nil { - logrus.Warn("选择一个测试场景") - return false - } - - if len(m.scene.CallerList()) == 0 { - logrus.Warn("还没有给场景添加调用器") + if len(m.sceneQueue.List()) == 0 { + logrus.Warn("没有设置场景队列") return false } @@ -86,7 +133,7 @@ func (m *Robot) Start() bool { dialer := &websocket.Dialer{ HandshakeTimeout: 2 * time.Second, } - conn, _, err := dialer.Dial("", nil) + conn, _, err := dialer.Dial(m.config.Global.WsAddr, nil) if err != nil { logrus.Error(err) return false @@ -111,66 +158,35 @@ func (m *Robot) Start() bool { return true } +func (m *Robot) Stop() bool { + return false +} + func (m *Robot) syncCall() { for { - caller, err := m.scene.callerQueue.Pop() + scene, err := m.sceneQueue.Pop() if err != nil { + logrus.WithField("err", err).Error("取场景") return } - req := caller.BuildReq(m, &pb.UserMessage{}) - - m.callOne(&req) - } - -} - -func (m *Robot) callOne(rawReq *RawReq) *RawResp { - start := time.Now().UnixNano() - rsp, err := m.call(rawReq) - end := time.Now().UnixNano() - elapsedTime := time.Duration(end - start) - - var rawResp RawResp - if err != nil { - errMsg := fmt.Sprintf("Call Error: %s.", err) - rawResp = RawResp{ - ID: rawReq.ID, - Err: errors.New(errMsg), - Elapse: elapsedTime} - } else { - rawResp = RawResp{ - ID: rawReq.ID, - Resp: rsp, - Elapse: elapsedTime} - } - return &rawResp -} - -func (m *Robot) call(rawReq *RawReq) ([]byte, error) { - m.conn.WriteMessage(websocket.BinaryMessage, rawReq.Req) - - var ( - data []byte - err error - ) - for { - _, data, err = m.conn.ReadMessage() - if err != nil { - logrus.Errorf("readMessage err:%v", err) - return nil, err - } - if !m.checkPush(data) { - return data, nil + start := time.Now() + //这里执行会花很长时间 + if err := scene.Run(m); err != nil { + logrus.WithField("err", err).Error("执行业务时发生错误") + break } + elapsedTime := time.Since(start) + info := scene.Info() + logrus.WithField("耗时", elapsedTime.String()).Debug("场景【" + info.Name + "】执行完毕统计") } } -func (m *Robot) checkPush(data []byte) bool { +func (m *Robot) checkResp(data []byte, rsp proto.Message) bool { msg := &pb.UserMessage{} if err := proto.Unmarshal(data, msg); err != nil { - logrus.Error("结果解析失败") + logrus.Error("pb解析失败") return false } methodStr := msg.Data.TypeUrl @@ -180,14 +196,15 @@ func (m *Robot) checkPush(data []byte) bool { if methodName == "NotifyErrorNotifyPush" { push := &pb.NotifyErrorNotifyPush{} if !ProtoUnmarshal(msg, push) { - logrus.Error("unmarsh err") + logrus.Error("pb解析失败") return false } - logrus.WithField("methodName", methodName).WithField("code", push.Code).Debug("收到错误码") - } else { - logrus.WithField("methodName", methodName).Debug("收到推送") + logrus.WithField("methodName", methodName).WithField("code", push.Code).Debug("收到推送") + } + } else { + if !ProtoUnmarshal(msg, rsp) { + return false } - return true } - return false + return true } diff --git a/main.go b/main.go index 3dcfd34..f609600 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ func init() { } registerScenes( + &busi.LoginScene{}, &busi.FriendScene{}, &busi.SociatyScene{}, ) diff --git a/robot.log b/robot.log index e69de29..9e455cb 100644 --- a/robot.log +++ b/robot.log @@ -0,0 +1,9 @@ +{"level":"debug","msg":"创建AI","time":"2022-12-12 18:33:18"} +{"level":"debug","msg":"AI 参数校验通过 机器人数量:1","time":"2022-12-12 18:33:18"} +{"level":"debug","msg":"初始化AI完成 机器人数量:1","time":"2022-12-12 18:33:18"} +{"MainType":"user","SubType":"login","level":"debug","msg":"发送消息","req":{"account":"Sparrowbronze","sid":"dfz"},"rsp":{"data":null,"ex":null,"timeNow":0},"time":"2022-12-12 18:33:18"} +{"level":"debug","msg":"data:{id:\"6397036e088aade4be615603\" uid:\"dfz_6397036e088aade4be615603\" uuid:\"055d2b51-52ff-4b10-9dd2-1bfc865b72ff\" binduid:\"Sparrowbronze\" sid:\"dfz\" lastloginip:\"10.0.0.238:54489\" ctime:1670841198 logintime:1670841198 lv:1} timeNow:1670841198","time":"2022-12-12 18:33:18"} +{"level":"debug","msg":"Success","time":"2022-12-12 18:33:18"} +{"level":"debug","msg":"场景【login】执行完毕统计","time":"2022-12-12 18:33:18","耗时":"24.3072ms"} +{"err":"not found","level":"error","msg":"取场景","time":"2022-12-12 18:33:18"} +{"level":"debug","msg":"机器人运行了","time":"2022-12-12 18:33:18"} diff --git a/test/robot_test.go b/test/robot_test.go index 7537f78..4388eb8 100644 --- a/test/robot_test.go +++ b/test/robot_test.go @@ -13,10 +13,6 @@ func TestAction(t *testing.T) { RobotCount: 1, } ai, _ := lib.NewAI(aip) - //注册caller - friend_recommend := &friend.FriendRecommend{} - friend_recommend.Desc = "" - ai.InitCaller() // 创建场景 scene := lib.NewScene(ai, lib.SceneParam{ @@ -28,12 +24,12 @@ func TestAction(t *testing.T) { scene.AddCaller(&friend.FriendRecommend{}) //加机器人 - ai.AddRobot(scene) + // ai.AddRobot(scene) //运行机器人 - for _, v := range ai.GetRobots("场景1") { - v.Start() - } + // for _, v := range ai.GetRobots("场景1") { + // v.Start() + // } } diff --git a/ui/mainwindow.go b/ui/mainwindow.go index 22993f8..863c9b1 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -117,12 +117,18 @@ func (mw *MainWindow) startContainer() { // } startBtn.Enable() startBtn.OnTapped = func() { - ai, err := lib.NewAI(lib.AIParam{RobotCount: uint32(config.Global.UserCount)}) + param := lib.AIParam{ + Scenes: mw.UIImpl.scenes, + Config: mw.UIImpl.config, + RobotCount: uint32(config.Global.UserCount), + } + ai, err := lib.NewAI(param) if err != nil { dialog.ShowError(err, mw.w) return } - ai.InitCaller(mw.scenes...) + ai.Start() + } content := container.NewCenter(startBtn) mw.changeContent(content)