airobot/lib/robot.go
2022-12-12 08:30:21 +08:00

185 lines
3.4 KiB
Go

package lib
import (
"errors"
"fmt"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/gorilla/websocket"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
"legu.airobot/pb"
)
type IRobot interface {
// 启动机器人
Start() bool
// 选择场景
SelScene(scene *scene)
// 当前场景
GetCurrentScene() *scene
}
type Robot struct {
IStore
scene *scene
conn *websocket.Conn
data map[string][]byte //caller缓存数据
status uint32 //状态
lock sync.Mutex //
}
func NewRobot() *Robot {
robot := &Robot{
data: make(map[string][]byte),
}
return robot
}
//存数据
func (a *Robot) Store(key string, data []byte) {
defer a.lock.Unlock()
a.lock.Lock()
a.data[key] = data
}
//取数据
func (a *Robot) Get(key string) []byte {
defer a.lock.Unlock()
a.lock.Lock()
return a.data[key]
}
func (m *Robot) SelScene(scene *scene) {
m.scene = scene
}
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("还没有给场景添加调用器")
return false
}
// 创建链接
dialer := &websocket.Dialer{
HandshakeTimeout: 2 * time.Second,
}
conn, _, err := dialer.Dial("", nil)
if err != nil {
logrus.Error(err)
return false
}
m.conn = conn
//检查具备启动的状态
if !atomic.CompareAndSwapUint32(
&m.status, STATUS_ORIGINAL, STATUS_STARTING) {
if !atomic.CompareAndSwapUint32(&m.status, STATUS_STOPPED, STATUS_STARTING) {
return false
}
}
//设置状态为已启动
atomic.StoreUint32(&m.status, STATUS_STARTED)
m.syncCall()
logrus.Debug("机器人运行了")
return true
}
func (m *Robot) syncCall() {
for {
caller, err := m.scene.callerQueue.Pop()
if err != nil {
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
}
}
}
func (m *Robot) checkPush(data []byte) bool {
msg := &pb.UserMessage{}
if err := proto.Unmarshal(data, msg); err != nil {
logrus.Error("结果解析失败")
return false
}
methodStr := msg.Data.TypeUrl
methodName := SubStr(methodStr, 20, len(methodStr))
if strings.HasSuffix(methodName, "Push") {
if methodName == "NotifyErrorNotifyPush" {
push := &pb.NotifyErrorNotifyPush{}
if !ProtoUnmarshal(msg, push) {
logrus.Error("unmarsh err")
return false
}
logrus.WithField("methodName", methodName).WithField("code", push.Code).Debug("收到错误码")
} else {
logrus.WithField("methodName", methodName).Debug("收到推送")
}
return true
}
return false
}