package main import ( "errors" "strconv" "time" "github.com/sirupsen/logrus" "lc/common/mqtt" "lc/common/onvif/profiles/ptz" "lc/common/onvif/soap" "lc/common/protocol" ) var ( NotSupportPtz = errors.New("不支持云台PTZ") ) // ContinuousMove direction 0:停止移动;1:向左边;2:向右边;3:向上;4:向下;5:缩小;6:放大 // speed/255.0,speed的范围[0,255] func (o *LcDevice) ContinuousMove(profileToken string, direction, speed uint8) error { XAddr, ok := o.endpoints["ptz"] if !ok { return NotSupportPtz } p := ptz.NewPTZ(o.client, XAddr) var x float32 = 0.0 var y float32 = 0.0 var z float32 = 0.0 s := float32(speed) / 255 switch direction { case 0: //停止移动,用初始值,全零 case 1: //向左边移动 x = -s case 2: //向右边移动 x = s case 3: //向上移动 y = s case 4: //向下移动 y = -s case 5: //缩小 z = -s case 6: //放大 z = s } _, err := p.ContinuousMove(&ptz.ContinuousMove{ ProfileToken: ptz.ReferenceToken(profileToken), Velocity: ptz.PTZSpeed{ PanTilt: ptz.Vector2D{ X: x, Y: y, //Space: "http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace", }, Zoom: ptz.Vector1D{ X: z, //Space: "http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace", }, }, }) return err } // GetProsets 获取所有预置点并上报到平台 func (o *LcDevice) GetProsets(profileToken string, filter bool) ([]protocol.PresetInfo, error) { XAddr, ok := o.endpoints["ptz"] if !ok { return nil, NotSupportPtz } //新球机需要重新生成签名dsz 20230704 client := soap.NewClient(soap.WithTimeout(time.Second * 0)) client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password)) o.client = client p := ptz.NewPTZ(o.client, XAddr) resp, err := p.GetPresets(&ptz.GetPresets{ProfileToken: ptz.ReferenceToken(profileToken)}) if err != nil { return nil, err } var Data []protocol.PresetInfo for _, v := range resp.Preset { //海康某球机默认包含300个预置点,需要过滤默认的预置点 if filter { if v.PTZPosition.PanTilt.X == 0 && v.PTZPosition.PanTilt.Y == 1 && v.PTZPosition.Zoom.X == 0 { continue } } pi := protocol.PresetInfo{Token: string(v.Token), Name: string(v.Name), X: v.PTZPosition.PanTilt.Y, Y: v.PTZPosition.PanTilt.Y, Z: v.PTZPosition.Zoom.X} Data = append(Data, pi) } return Data, nil } // SetProset 设置预置点 func (o *LcDevice) SetProset(profileToken, presetToken, presetName string) (string, error) { XAddr, ok := o.endpoints["ptz"] if !ok { return "", NotSupportPtz } //新球机需要重新生成签名dsz 20230704 client := soap.NewClient(soap.WithTimeout(time.Second * 0)) client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password)) o.client = client p := ptz.NewPTZ(o.client, XAddr) resp, err := p.SetPreset(&ptz.SetPreset{ ProfileToken: ptz.ReferenceToken(profileToken), PresetName: presetName, PresetToken: ptz.ReferenceToken(presetToken), }) if err != nil { if _, ok := err.(*soap.SOAPFault); ok { //可能是token错误 resp, err = p.SetPreset(&ptz.SetPreset{ ProfileToken: ptz.ReferenceToken(profileToken), PresetName: presetName, }) } } if err == nil { return string(resp.PresetToken), nil } return "", err } // DeleteProset 删除预置点 func (o *LcDevice) DeleteProset(profileToken, presetToken string) error { XAddr, ok := o.endpoints["ptz"] if !ok { return NotSupportPtz } //新球机需要重新生成签名dsz 20230704 client := soap.NewClient(soap.WithTimeout(time.Second * 0)) client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password)) o.client = client p := ptz.NewPTZ(o.client, XAddr) _, err := p.RemovePreset(&ptz.RemovePreset{ ProfileToken: ptz.ReferenceToken(profileToken), PresetToken: ptz.ReferenceToken(presetToken), }) return err } // GotoProset 跳转到指定的预置点 func (o *LcDevice) GotoProset(profileToken, presetToken string) error { XAddr, ok := o.endpoints["ptz"] if !ok { return NotSupportPtz } //新球机需要重新生成签名dsz 20230704 client := soap.NewClient(soap.WithTimeout(time.Second * 0)) client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password)) o.client = client p := ptz.NewPTZ(o.client, XAddr) _, err := p.GotoPreset(&ptz.GotoPreset{ ProfileToken: ptz.ReferenceToken(profileToken), PresetToken: ptz.ReferenceToken(presetToken), }) return err } // SetHomePosition 跳转到指定的预置点 func (o *LcDevice) SetHomePosition(profileToken string) error { XAddr, ok := o.endpoints["ptz"] if !ok { return NotSupportPtz } //新球机需要重新生成签名dsz 20230704 client := soap.NewClient(soap.WithTimeout(time.Second * 0)) client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password)) o.client = client p := ptz.NewPTZ(o.client, XAddr) _, err := p.SetHomePosition(&ptz.SetHomePosition{ProfileToken: ptz.ReferenceToken(profileToken)}) return err } // GotoHomePosition 跳转到指定的预置点 func (o *LcDevice) GotoHomePosition(profileToken string) error { XAddr, ok := o.endpoints["ptz"] if !ok { return NotSupportPtz } //新球机需要重新生成签名dsz 20230704 client := soap.NewClient(soap.WithTimeout(time.Second * 0)) client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password)) o.client = client p := ptz.NewPTZ(o.client, XAddr) _, err := p.GotoHomePosition(&ptz.GotoHomePosition{ProfileToken: ptz.ReferenceToken(profileToken)}) return err } func (o *LcDevice) HandleTpSPtzComm(m mqtt.Message) { //新球机需要重新生成签名dsz 20230704 client := soap.NewClient(soap.WithTimeout(time.Second * 0)) client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password)) o.client = client var obj protocol.Pack_PTZCommonInfo if err := obj.DeCode(m.PayloadString()); err != nil { return } if o.onvifDev.Code != obj.Id { return } profiletoken := o.GetProfileToken(obj.Data.ProfileToken) //0:获取所有预置点,1:设置预置点,2:删除预置点,3:跳转到预置点,4:设置home点,5:跳转到home点 switch obj.Data.Flag { case 0: arrpis, err := o.GetProsets(profiletoken, true) var ret protocol.Pack_PresetInfo strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, obj.Seq, err, obj.Data.Flag, arrpis) GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PRESETS_ACK), strRet, mqtt.AtMostOnce, ToCloud) case 1: token, err := o.SetProset(profiletoken, obj.Data.PresetToken, obj.Data.PresetName) var ( ret protocol.Pack_IPCSetPresetACK file string x, y, z float32 = 0, 0, 0 ) if err == nil { //预置位设置成功,则抓拍一张图片 tmpfile := o.onvifDev.Code + "_" + strconv.FormatUint(obj.Seq, 10) + ".jpg" err = o.Snapshot2(tmpfile) if err == nil { file = tmpfile } //获取x,y,z if arrpis, errx := o.GetProsets(profiletoken, true); errx == nil { for _, pis := range arrpis { if pis.Name == obj.Data.PresetName && pis.Token == token { x = pis.X y = pis.Y z = pis.Z break } } } } strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, token, obj.Data.PresetName, file, x, y, z, obj.Seq, err) GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PRESET_ACK), strRet, mqtt.AtMostOnce, ToAll) case 2: err := o.DeleteProset(profiletoken, obj.Data.PresetToken) var ret protocol.Pack_IPCCommonACK strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err) GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll) case 3: err := o.GotoProset(profiletoken, obj.Data.PresetToken) var ret protocol.Pack_IPCCommonACK strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err) GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll) case 4: err := o.SetHomePosition(profiletoken) var ret protocol.Pack_IPCCommonACK strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err) GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll) case 5: err := o.GotoHomePosition(profiletoken) var ret protocol.Pack_IPCCommonACK strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err) GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll) case 6: arrpis, err := o.GetProsets(profiletoken, false) var ret protocol.Pack_PresetInfo strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, obj.Seq, err, obj.Data.Flag, arrpis) GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PRESETS_ACK), strRet, mqtt.AtMostOnce, ToCloud) default: logrus.Errorf("Handle_PTZ:不支持的flag[%d]", obj.Data.Flag) } } func (o *LcDevice) HandleTpOnvifPtz(m mqtt.Message) { var obj protocol.Pack_PTZMoveInfo if err := obj.DeCode(m.PayloadString()); err != nil { return } if o.onvifDev.Code != obj.Id { return } //新球机需要重新生成签名dsz 20230704 client := soap.NewClient(soap.WithTimeout(time.Second * 0)) client.AddHeader(soap.NewWSSSecurityHeader(o.onvifDev.User, o.onvifDev.Password)) o.client = client token := o.GetProfileToken(obj.Data.ProfileToken) err := o.ContinuousMove(token, obj.Data.Direction, obj.Data.Speed) var ret protocol.Pack_IPCCommonACK strRet, err := ret.EnCode(o.onvifDev.Code, appConfig.GID, "", obj.Seq, err) GetMQTTMgr().Publish(GetTopic(o.GetDevType(), o.onvifDev.Code, protocol.TP_ONVIF_PTZ_COMM_ACK), strRet, mqtt.AtMostOnce, ToAll) }