This commit is contained in:
2025-07-07 20:11:59 +08:00
parent ab0fdbc447
commit 06e3aa2eb3
2009 changed files with 193082 additions and 0 deletions

View File

@ -0,0 +1 @@
package dj

1217
third/plane/dj/dj.go Normal file

File diff suppressed because it is too large Load Diff

203
third/plane/dj/errorcode.go Normal file
View File

@ -0,0 +1,203 @@
package dj
var errorMap map[int]Error
type Error struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
func InitError() {
errorMap = make(map[int]Error)
errors := []Error{
{0, "成功"},
{314000, "设备当前无法支持该操作,建议检查设备当前工作状态"},
{314001, "飞行任务下发失败,请稍后重试"},
{314002, "飞行任务下发失败,请稍后重试"},
{314003, "航线文件格式不兼容,请检查航线文件是否正确"},
{314004, "飞行任务下发失败,请稍后重试或重启机场后重试"},
{314005, "飞行任务下发失败,请稍后重试或重启机场后重试"},
{314006, "飞行器初始化失败,请重启机场后重试"},
{314007, "机场传输航线至飞行器失败,请重启机场后重试"},
{314008, "飞行器起飞前准备超时,请重启机场后重试"},
{314009, "飞行器初始化失败,请重启机场后重试"},
{314010, "航线执行失败,请重启机场后重试"},
{314011, "机场系统异常,无法获取飞行任务执行结果"},
{314012, "飞行器起飞前准备失败,无法执行飞行任务,请重启机场后重试"},
{314013, "飞行任务下发失败,机场无法获取到本次飞行任务的航线,无法执行飞行任务,请稍后重试"},
{314014, "机场系统异常,飞行任务执行失败,请稍后重试"},
{314015, "机场传输精准复拍航线至飞行器失败,无法执行飞行任务,请稍后重试或重启机场后重试"},
{314016, "航线文件解析失败,无法执行飞行任务,请检查航线文件"},
{314017, "机场系统异常,飞行任务执行失败,请稍后重试"},
{314018, "飞行器 RTK 定位异常,无法执行飞行任务,请稍后重试或重启机场后重试"},
{314019, "飞行器 RTK 收敛失败,无法执行飞行任务,请稍后重试或重启机场后重试"},
{314020, "飞行器不在停机坪正中间或飞行器朝向不正确,无法执行飞行任务,请检查飞行器位置和朝向"},
{314021, "飞行器 RTK 定位异常,无法执行飞行任务,请稍后重试或重启机场后重试"},
{316001, "飞行器参数配置失败,请重启机场后重试"},
{316002, "飞行器参数配置失败,请重启机场后重试"},
{316003, "飞行器参数配置失败,请重启机场后重试"},
{316004, "飞行器参数配置失败,请重启机场后重试"},
{316005, "飞行器 RTK 收敛失败,无法执行飞行任务,请重启机场后重试"},
{316006, "飞行器降落前机场未开启舱盖或展开推杆,飞行器无法降落至机场,请尽快至机场部署现场检查飞行器状况"},
{316007, "飞行器初始化失败,请重启机场后重试"},
{316008, "机场获取飞行器控制权失败,无法执行飞行任务,请确认遥控器未锁定控制权"},
{316009, "飞行器电量低于30%,无法执行飞行任务,请充电后重试(建议电量≥50%)"},
{316010, "机场未检测到飞行器,无法执行飞行任务,请检查舱内是否有飞行器,机场与飞行器是否已对频,或重启机场后重试"},
{316011, "飞行器降落位置偏移过大,请检查飞行器是否需要现场摆正"},
{316012, "飞行器起飞前准备失败,无法执行飞行任务,请重启机场后重试"},
{316013, "飞行器起飞前准备失败,无法执行飞行任务,请重启机场后重试"},
{316014, "飞行器起飞前准备失败,无法执行飞行任务,请重启机场后重试"},
{316015, "飞行器 RTK 收敛位置距离机场过远,无法执行飞行任务,请重启机场后重试"},
{316016, "飞行器降落至机场超时,可能是机场与飞行器断连导致,请通过直播查看飞行器是否降落至舱内"},
{316017, "获取飞行器媒体数量超时,可能是机场与飞行器断连导致,请通过直播查看飞行器是否降落至舱内"},
{316018, "飞行任务执行超时,可能是机场与飞行器断连导致,请通过直播查看飞行器是否降落至舱内"},
{316019, "服务器内部错误,无法执行飞行任务,请稍后重试"},
{316020, "飞行器使用的 RTK 信号源错误,请稍后重试"},
{316021, "飞行器 RTK 信号源检查超时,请稍后重试"},
{316022, "飞行器无法执行返航指令,请检查飞行器是否已开机,机场与飞行器是否已断连,请确认无以上问题后重试"},
{316023, "飞行器无法执行返航指令,飞行器已被 B 控接管,请在 B 控操控飞行器,或关闭 B 控后重试"},
{316024, "飞行器执行返航指令失败,请检查飞行器是否已起飞,确认飞行器已起飞后请重试"},
{316025, "飞行器参数配置失败,请稍后重试或重启机场后重试"},
{316026, "机场急停按钮被按下,无法执行飞行任务,请释放急停按钮后重试"},
{316027, "飞行器参数配置超时,请稍后重试或重启机场后重试"},
{316029, "机场急停按钮被按下,飞行器将飞往备降点降落,请立即检查飞行器是否已安全降落并将飞行器放回至机场"},
{317001, "获取飞行器媒体文件数量失败,请重启机场后重试"},
{317002, "飞行器存储格式化失败,飞行器未开机、未连接或未检测到相机,请确认无以上问题后重试,或重启飞行器后重试"},
{317003, "飞行器存储格式化失败,请重启飞行器后重试"},
{317004, "机场媒体文件格式化失败,请稍后重试或重启机场后重试"},
{317005, "飞行器结束录像失败,本次飞行任务的媒体文件可能无法上传"},
{319001, "机场作业中或(设备异常反馈)上传日志中,无法执行飞行任务,请等待当前飞行任务或操作执行完成后重试"},
{319002, "机场系统运行异常,请重启机场后重试"},
{319003, "机场系统运行异常,请重新下发任务"},
{319004, "飞行任务执行超时,已自动终止本次飞行任务"},
{319005, "云端与机场通信异常,无法执行飞行任务"},
{319006, "取消飞行任务失败,飞行任务已经在执行中"},
{319007, "修改飞行任务失败,飞行任务已经在执行中"},
{319008, "机场时间与云端时间不同步,机场无法执行飞行任务"},
{319009, "飞行任务下发失败,请稍后重试或重启机场后重试"},
{319010, "机场固件版本过低,无法执行飞行任务,请升级机场固件为最新版本后重试"},
{319015, "机场正在初始化中,无法执行飞行任务,请等待机场初始化完成后重试"},
{319016, "机场正在执行其他飞行任务,无法执行本次飞行任务"},
{319017, "机场正在处理上次飞行任务媒体文件,无法执行本次飞行任务,请稍后重试"},
{319018, "机场正在自动导出日志中(设备异常反馈),无法执行飞行任务,请稍后重试"},
{319019, "机场正在拉取日志中(设备异常反馈),无法执行飞行任务,请稍后重试"},
{319025, "机场未准备完成,无法执行云端下发的飞行任务,请稍后重试"},
{319026, "飞行器电池电量低于用户设置的任务开始执行的电量,请等待充电完成后再执行飞行任务"},
{319027, "机场或飞行器剩余存储容量过低,无法执行飞行任务,请等待媒体文件上传,机场和飞行器存储容量释放后再执行飞行任务"},
{319999, "机场系统运行异常,请重启机场后重试"},
{321000, "航线执行异常,请稍后重试或重启机场后重试"},
{321004, "航线文件解析失败,无法执行飞行任务,请检查航线文件"},
{321005, "航线缺少断点信息,机场无法执行飞行任务"},
{321257, "飞行任务已在执行中,请勿重复执行"},
{321258, "飞行任务无法终止,请检查飞行器状态"},
{321259, "飞行任务未开始执行,无法终止飞行任务"},
{321260, "飞行任务未开始执行,无法中断飞行任务"},
{321513, "航线规划高度已超过飞行器限高,机场无法执行飞行任务"},
{321514, "任务失败,起点或终点位于限远区域的缓冲区内或超过了限远距离"},
{321515, "航线穿过限飞区,机场无法执行飞行任务"},
{321517, "飞行器触发避障,飞行任务执行被终止"},
{321519, "飞行器接近限飞区或限远距离自动返航,无法完成航线飞行"},
{321523, "飞行器起桨失败,请稍后重试,如果仍报错请联系大疆售后。"},
{321524, "飞行器起飞前准备失败,可能是飞行器无发定位或档位错误导致,请检查飞行器状态"},
{321769, "飞行器卫星定位信号差,无法执行飞行任务,请重启机场后重试"},
{321770, "飞行器挡位错误,无法执行飞行任务,请重启机场后重试"},
{321771, "飞行器返航点未设置,无法执行飞行任务,请重启机场后重试"},
{321772, "飞行器电量低于30%,无法执行飞行任务,请充电后重试(建议电量≥50%)"},
{321773, "飞行器执行飞行任务过程中低电量返航,无法完成航线飞行"},
{321775, "飞行器航线飞行过程中失联,无法完成航线飞行"},
{321776, "飞行器 RTK 收敛失败,无法执行飞行任务,请重启机场后重试"},
{321777, "飞行器未悬停,无法开始执行飞行任务"},
{321778, "用户使用 B 控操控飞行器起桨,机场无法执行飞行任务"},
{322282, "机场执行飞行任务过程中被中断飞行器被云端用户或B控接管"},
{322283, "机场执行飞行任务过程中被用户触发返航,无法完成航线飞行"},
{322539, "航线的断点信息错误,机场无法执行飞行任务"},
{324012, "日志压缩过程超时,所选日志过多,请减少选择的日志后重试"},
{324013, "设备日志列表获取失败,请稍后重试"},
{324014, "设备日志列表为空,请刷新页面或重启机场后重试"},
{324015, "飞行器已关机或未连接,无法获取日志列表,请确认飞行器在舱内,通过远程调试将飞行器开机后重试"},
{324016, "机场存储空间不足,日志压缩失败,请清理机场存储空间或稍后重试"},
{324017, "日志压缩失败,无法获取所选飞行器日志,请刷新页面或重启机场后重试"},
{324018, "日志文件拉取失败,导致本次设备异常反馈上传失败,请稍后重试或重启机场后重试"},
{324019, "因机场网络异常,日志上传失败,请稍后重试。如果连续多次出现该问题,请联系代理商或大疆售后进行网络排障"},
{325001, "云端下发给机场的命令不符合格式要求,机场无法执行"},
{327500, "飞行器镜头除雾失败,请稍后重试"},
{386535, "航线执行异常,请稍后重试或重启机场后重试"},
{513001, "直播失败,飞行器不存在或飞行器类型错误"},
{513002, "直播失败,相机不存在或相机类型错误"},
{513003, "相机已经在直播中,请勿重复开启直播"},
{513005, "直播失败,直播参数清晰度设置错误"},
{513005, "直播失败,直播参数清晰度设置错误"},
{513006, "操作失败,相机未开启直播"},
{513008, "直播失败,设备端图传数据异常"},
{513010, "直播失败,设备无法联网"},
{513011, "操作失败,设备未开启直播"},
{513012, "操作失败,设备已在直播中,不支持切换镜头"},
{513013, "直播失败,直播使用的视频传输协议不支持"},
{513014, "直播失败,直播参数错误或者不完整"},
{513015, "直播异常,网络卡顿"},
{513016, "直播异常,视频解码失败"},
{513099, "直播失败,请稍后重试"},
{514100, "机场运行异常,请重启机场后重试"},
{514101, "推杆闭合失败,请检查停机坪上是否存在异物,飞行器方向是否放反,或重启机场后重试"},
{514102, "推杆展开失败,请检查停机坪上是否存在异物,或重启机场后重试"},
{514103, "飞行器电量低于30%,无法执行飞行任务,请充电后重试(建议电量≥50%)"},
{514104, "飞行器电池开始充电失败,请重启机场后重试"},
{514105, "飞行器电池停止充电失败,请重启机场后重试"},
{514106, "飞行器电源控制异常,请重启机场后重试"},
{514107, "舱盖开启失败,请检查舱盖周围是否存在异物,或重启机场后重试"},
{514108, "舱盖关闭失败,请检查舱盖周围是否存在异物,或重启机场后重试"},
{514109, "飞行器开机失败,请重启机场后重试"},
{514110, "飞行器关机失败,请重启机场后重试"},
{514111, "飞行器慢转收桨控制异常,请重启机场后重试"},
{514112, "飞行器慢转收桨控制异常,请重启机场后重试"},
{514113, "机场推杆与飞行器无法连接,请检查飞行器是否在舱内,推杆闭合时是否被卡住,充电连接器是否脏污或损坏"},
{514114, "获取飞行器电源状态失败,请重启机场后重试"},
{514116, "无法执行当前操作,机场正在执行其他控制指令,请稍后重试"},
{514117, "舱盖开启或关闭未到位,请重启机场后重试"},
{514118, "推杆展开或闭合未到位,请重启机场后重试"},
{514120, "机场与飞行器断连,请重启机场后重试或重新对频"},
{514121, "机场急停按钮被按下,请释放急停按钮"},
{514122, "获取飞行器充电状态失败,请重启机场后重试"},
{514123, "飞行器电池电量过低无法开机"},
{514124, "获取飞行器电池信息失败,无法执行飞行任务,请重启机场后重试"},
{514125, "飞行器电池电量已接近满电状态无法开始充电请使用至95%以下再进行充电"},
{514134, "雨量过大,机场无法执行飞行任务,请稍后重试"},
{514135, "风速过大≥12m/s机场无法执行飞行任务请稍后重试"},
{514136, "机场供电断开,机场无法执行飞行任务,请恢复机场供电后重试"},
{514137, "环境温度过低<-20℃机场无法执行飞行任务请稍后重试"},
{514138, "飞行器电池正在保养中,机场无法执行飞行任务,请等待保养结束后重试"},
{514139, "飞行器电池无法执行保养指令,飞行器电池无需保养"},
{514140, "飞行器电池无法执行保养指令,飞行器电池无需保养"},
{514141, "机场系统运行异常,请重启机场后重试"},
{514142, "飞行器起飞前,机场推杆与飞行器无法连接,请检查飞行器是否在舱内,推杆闭合时是否被卡住,充电连接器是否脏污或损坏"},
{514143, "推杆未闭合或闭合不到位,请稍后重试或重启机场后重试"},
{514144, "舱盖未关闭或关闭不到位,请稍后重试或重启机场后重试"},
{514145, "机场处于现场调试中,无法执行当前操作或执行飞行任务,请断开遥控器和机场的数据线连接后重试"},
{514146, "机场处于远程调试中,无法执行飞行任务,请退出远程调试后重试"},
{514147, "设备升级中,无法执行飞行任务,请等待升级完成后重试"},
{514148, "机场已经在作业中,无法进行远程调试或再次执行飞行任务,请等待当前任务执行完成后重试"},
{514149, "机场系统运行异常,无法执行飞行任务,请重启机场后重试"},
{514150, "设备重启中,无法执行飞行任务,请等待重启完成后重试"},
{514151, "设备升级中,无法执行设备重启指令,请等待升级完成后重试"},
{514153, "机场已退出远程调试模式,无法执行当前操作"},
{514170, "机场系统初始化中,无法执行当前操作或指令,请等待机场系统初始化完成后重试"},
{514171, "云端下发给机场的命令不符合格式要求,机场无法执行"},
{514180, "停止空调制冷或停止空调制热失败,请稍后重试"},
{514181, "开启空调制冷失败,请稍后重试"},
{514182, "开启空调制热失败,请稍后重试"},
{514183, "开启空调除湿失败,请稍后重试"},
{514184, "当前温度低于 0 ℃,无法开启空调制冷"},
{514185, "当前温度高于 45 ℃,无法开启空调制热"},
{514300, "网关异常"},
{514301, "请求超时,连接断开"},
{514302, "网络证书异常,连接失败"},
{514303, "网络异常,连接断开"},
{514304, "机场请求被拒,连接失败"},
}
for _, e := range errors {
errorMap[e.Code] = e
}
}
func GetErrorMap() map[int]Error {
return errorMap
}

259
third/plane/dj/minio.go Normal file
View File

@ -0,0 +1,259 @@
package dj
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go/aws"
awscs "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/gogf/gf/v2/frame/g"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"log"
"os"
"path/filepath"
"time"
)
//// minio的配置信息
//const (
// Endpoint string = coryCommon.Global + ":9999"
// AccessKeyID string = "admin"
// SecretAccessKey string = "12345678"
// BucketName string = "cory-create"
// RoleARN string = "arn:aws:s3:::cory-create/*"
// RoleSessionName string = "anysession"
// Region string = "cn-chengdu"
// UseSSL bool = true
// DurationSeconds int64 = 3600
//
// // Endpoint string = "jl.yj-3d.com:8154"
// // AccessKeyID string = "minioadmin"
// // SecretAccessKey string = "minioadmin"
// // BucketName string = "cory-create"
// // RoleARN string = "arn:aws:s3:::cory-create/*"
// // RoleSessionName string = "anysession"
// // Region string = "cn-chengdu"
// // UseSSL bool = true
// // DurationSeconds int64 = 3600
//)
var (
Endpoint string
AccessKeyID string
SecretAccessKey string
BucketName string
RoleARN string
RoleSessionName string
Region string
UseSSL bool
DurationSeconds int64
)
func init() {
Endpoint = g.Cfg().MustGet(context.Background(), "minio.endpoint").String()
AccessKeyID = g.Cfg().MustGet(context.Background(), "minio.accessKeyID").String()
SecretAccessKey = g.Cfg().MustGet(context.Background(), "minio.secretAccessKey").String()
BucketName = g.Cfg().MustGet(context.Background(), "minio.bucketName").String()
RoleSessionName = g.Cfg().MustGet(context.Background(), "minio.roleSessionName").String()
Region = g.Cfg().MustGet(context.Background(), "minio.region").String()
UseSSL = g.Cfg().MustGet(context.Background(), "minio.useSSL").Bool()
DurationSeconds = g.Cfg().MustGet(context.Background(), "minio.durationSeconds").Int64()
RoleARN = g.Cfg().MustGet(context.Background(), "minio.roleARN").String()
}
var (
IsEnablingSSL = false //是否开启SSL安全 (只针对初始化客户端)
)
// PublicMinioClient 初始化MinIO客户端
func PublicMinioClient() (minioClient *minio.Client, err error) {
// 获取临时凭证
token, accessKey, secretAccessKey := MinioVoucher()
// 初始化MinIO客户端
minioClient, err = minio.New(Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(accessKey, secretAccessKey, token),
Secure: IsEnablingSSL,
})
return
}
// MinioVoucher 获取minio的临时凭证
func MinioVoucher() (sessionToken, accessKey, secretKey string) {
// 创建AWS会话
sess, err := session.NewSession(&aws.Config{
Endpoint: aws.String(Endpoint),
DisableSSL: aws.Bool(UseSSL),
Region: aws.String(Region),
Credentials: awscs.NewStaticCredentials(AccessKeyID, SecretAccessKey, ""),
})
if err != nil {
log.Printf("创建AWS会话错误%v", err)
return
}
// 创建STS服务客户端
stsSvc := sts.New(sess)
// 获取STS凭证
input := &sts.AssumeRoleInput{
RoleArn: aws.String(RoleARN),
RoleSessionName: aws.String(RoleSessionName),
DurationSeconds: aws.Int64(DurationSeconds),
}
result, err := stsSvc.AssumeRole(input)
if err != nil {
fmt.Println("获取STS凭证错误:", err)
return
}
// 获取STS凭证的Token、AccessKey和SecretKey
sessionToken = *result.Credentials.SessionToken
accessKey = *result.Credentials.AccessKeyId
secretKey = *result.Credentials.SecretAccessKey
return
}
/*
TheTemporaryAccessUrlIsObtainedBasedOnThePrefixOfTheSpecifiedBucket 根据指定桶的前缀获取到临时访问URL
bucketName桶名
prefix前缀
expiryURL能够访问的时间最长7天或至少1秒 expiry := 3 * 24 * time.Hour 3天时间访问Url
isRecursion是否递归
*/
func TheTemporaryAccessUrlIsObtainedBasedOnThePrefixOfTheSpecifiedBucket(ctx context.Context, minioClient *minio.Client, bucketName string, prefix string, expiry time.Duration, isRecursion bool) (url []string) {
//1、获取递归集
objectSet := ListAndGenerateURLs(ctx, minioClient, bucketName, prefix, isRecursion)
//2、根据集获取到临时可访问的URL
for _, object := range objectSet {
preSignature := GenerateAnOnlineScopedUrlBasedOnTheObject(ctx, minioClient, bucketName, object.Key, expiry)
url = append(url, preSignature)
}
return
}
/*
DownloadTheFileWithTheSpecifiedPrefixBasedOnTheObjectSet 根据对象集下载指定前缀的文件
bucketName桶名
prefix前缀
filePath下载后保存路径
isRecursion是否递归获取对象集
*/
func DownloadTheFileWithTheSpecifiedPrefixBasedOnTheObjectSet(ctx context.Context, minioClient *minio.Client, bucketName string, prefix string, filePath string, isRecursion bool) (err error) {
objectList := ListAndGenerateURLs(ctx, minioClient, bucketName, prefix, isRecursion)
for _, object := range objectList {
//split := strings.Split(object.Key, "/")
//wz := split[len(split)-1]
//fileName := strings.Split(wz, ".")[0]
//if strings.Contains(fileName, "W") {
// fmt.Println(wz)
//}
err = minioClient.FGetObject(ctx, bucketName, object.Key, filepath.ToSlash(filePath+"/"+object.Key), minio.GetObjectOptions{})
if err != nil {
fmt.Println("下载minio资源失败", err)
return err
}
}
return
}
/*
DeletesTheFileWithTheSpecifiedPrefixBasedOnTheObjectSet 根据对象集删除指定前缀的文件
bucketName桶名
prefix前缀
isRecursion是否递归获取对象集
*/
func DeletesTheFileWithTheSpecifiedPrefixBasedOnTheObjectSet(ctx context.Context, minioClient *minio.Client, bucketName string, prefix string, isRecursion bool) (err error) {
objectList := ListAndGenerateURLs(ctx, minioClient, bucketName, prefix, isRecursion)
for _, object := range objectList {
err = minioClient.RemoveObject(ctx, bucketName, object.Key, minio.RemoveObjectOptions{})
if err != nil {
fmt.Println("删除minio资源失败", err)
return err
}
}
return
}
/*
UploadDataAccordingToTemporaryCredentials 根据临时凭证上传数据
filePath本地文件路径
objectName存储到minio的地址桶+前缀) 示例cory-create/uav/key.txt
*/
func UploadDataAccordingToTemporaryCredentials(filePath string, objectName string) (err error) {
minioClient, err := PublicMinioClient()
if err != nil {
return
}
// 打开文件
file, err := os.Open(filePath)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
// 使用minio-go的PutObject方法上传文件
n, err := minioClient.PutObject(context.Background(), BucketName, objectName, file, -1, minio.PutObjectOptions{})
if err != nil {
fmt.Println("Error uploading file:", err)
return
}
fmt.Printf("Successfully uploaded %s of size %d\n", objectName, n)
return err
}
//=========================================================================================================================
//=========================================================================================================================
//=========================================================================================================================
//=========================================================================================================================
//=========================================================================================================================
//=========================================================================================================================
//=========================================================================================================================
//=========================================================================================================================
/*
ListAndGenerateURLs 获取指定前缀下的对象集
bucketName桶名
prefix前缀获取哪个下面的对象集 示例:"uav/9b0af64e1c274105b157a9781961ed99/DJI_202405170903_001_9b0af64e1c274105b157a9781961ed99"
isRecursion是否递归
*/
func ListAndGenerateURLs(ctx context.Context, minioClient *minio.Client, bucketName string, prefix string, isRecursion bool) (objectInfo []minio.ObjectInfo) {
// 设置递归为 true以获取所有对象包括子目录中的对象
objectCh := minioClient.ListObjects(ctx, bucketName, minio.ListObjectsOptions{
Prefix: prefix,
Recursive: isRecursion,
})
// 等待通道中的对象被填充
for object := range objectCh {
if object.Err != nil {
fmt.Println("获取对象列表失败:", object.Err)
continue
}
objectInfo = append(objectInfo, object)
//fmt.Println("对象名称:", object.Key)
}
return
}
// GenerateAnOnlineScopedUrlBasedOnTheObject 根据对象集生成可在线访问的URL
func GenerateAnOnlineScopedUrlBasedOnTheObject(ctx context.Context, minioClient *minio.Client, bucketName string, objectName string, expiry time.Duration) string {
// 生成预签名的 GET URL
presignedURL, err := minioClient.PresignedGetObject(ctx, bucketName, objectName, expiry, nil)
if err != nil {
log.Fatalf("生成预签名 URL 失败:%v", err)
}
//// 使用预签名 URL 访问对象
//resp, err := minioClie.
//nt.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
//if err != nil {
// log.Fatalf("获取对象失败:%v", err)
//}
//defer resp.Close()
return presignedURL.String()
}
// StatObjectFunc 获取对象的元数据
func StatObjectFunc(ctx context.Context, minioClient *minio.Client, bucketName string, objectName string) (objInfo minio.ObjectInfo, err error) {
objInfo, err = minioClient.StatObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
return
}

View File

@ -0,0 +1,88 @@
package event
// eventHandlerInfo定义了事件处理器的信息
type eventHandlerInfo struct {
handler EventHandler // 事件处理函数
once bool // 是否只执行一次
}
// EventHandler定义了事件处理函数的类型
type EventHandler func(params ...any)
// Event表示一个事件对象
type Event struct {
handlers []eventHandlerInfo // 事件处理器列表
}
// Attach将事件处理器附加到事件对象中并返回处理器在列表中的索引
func (e *Event) Attach(handler EventHandler) int {
handlerInfo := eventHandlerInfo{handler, true} //默认只执行一次
for i, h := range e.handlers {
if h.handler == nil {
e.handlers[i] = handlerInfo
return i
}
}
e.handlers = append(e.handlers, handlerInfo)
return len(e.handlers) - 1
}
// Detach从事件对象中移除指定索引处的处理器
func (e *Event) Detach(handle int) {
e.handlers[handle].handler = nil
}
// Once将事件处理器附加到事件对象中并将处理器标记为只执行一次
func (e *Event) Once(handler EventHandler) {
i := e.Attach(handler)
e.handlers[i].once = true
}
// EventPublisher表示事件发布者
type EventPublisher struct {
event Event // 事件对象
}
// Event返回事件对象的指针
func (p *EventPublisher) Event() *Event {
return &p.event
}
// Publish触发事件依次执行事件对象中的处理器并在处理器标记为只执行一次时将其从事件对象中移除
func (p *EventPublisher) Publish(params ...any) {
for i, h := range p.event.handlers {
if h.handler != nil {
h.handler(params...)
if h.once {
p.event.Detach(i)
}
}
}
}
func GetEvent(fn func(params ...any)) *EventPublisher {
publisher := &EventPublisher{}
// 定义一个事件处理器函数
handler := fn
// 将事件处理器附加到事件列表中
publisher.Event().Attach(handler)
return publisher
}
/*func main() {
// 在这里编写您的代码逻辑
// 创建一个事件发布者
publisher := &EventPublisher{}
// 定义一个事件处理器函数
handler := func() {
fmt.Println("Event handled!")
}
// 将事件处理器附加到事件列表中
publisher.Event().Attach(handler)
// 发布事件
publisher.Publish()
}*/

125
third/plane/globe/globe.go Normal file
View File

@ -0,0 +1,125 @@
package globe
import (
"errors"
"github.com/gogf/gf/v2/net/ghttp"
"gorm.io/gorm"
"net/http"
"strconv"
"sync"
)
const (
ALL = -1 //所有
ENABLE = 1
DISABLE = 0
DESC = "desc"
ASC = "asc"
PAGE = 1
PAGESIZE = 10
ONLINE = 1
OFFLINE = 0
DEFAULTPWD = "123456"
DEFAULTUSR = "admin"
)
var DepartName = "一级部门" //默认的一级部门名称
var IS_OFFLINE_VERSION = true //是否为单机版本
const SOURCE = "static/source/"
type WS_MAP map[string]*ghttp.WebSocket
var WS_Status_map WS_MAP //状态数据ws
var WS_Progress_drone_open_map WS_MAP //飞行器开机进度ws
var WS_Progress_drone_close_map WS_MAP //飞行器关机进度ws
var WS_Progress_cover_open_map WS_MAP //机库打开舱盖进度ws
var WS_Progress_cover_close_map WS_MAP //机库关闭舱盖进度ws
var WS_Progress_device_reboot_map WS_MAP //机库重启进度ws
var WS_Progress_putter_open_map WS_MAP //推杆展开进度ws
var WS_Progress_putter_close_map WS_MAP //推杆闭合进度ws
var WS_Reply WS_MAP //状态恢复
var MutexRw sync.RWMutex //map的读写锁
const (
TILESET = "tileset"
BIM = "bim"
LAYER = "layer"
TERRAIN = "terrain"
POINT = "point"
LINE = "line"
AREA = "area"
MODEL = "model"
KML = "kml"
GEOJSON = "geojson"
)
const (
PAK = ".pak"
MBTILES = ".mbtiles"
CLT = ".clt"
JCT = ".jct"
)
var (
PORT = "80"
HOST = ""
PROTOCOL = ""
KEY = ""
CRT = ""
)
const (
HTTP = "http"
HTTPS = "https"
)
func GetErrors(msg string) error {
return errors.New(msg)
}
func GetAddr() string {
//单机版本时 无代理,需要补全地址
if IS_OFFLINE_VERSION {
return PROTOCOL + "://" + HOST + ":" + PORT + "/"
}
//网络版时 有代理 不需要补全地址
return ""
}
/*clt数据包*/
type Tile struct {
MD5 string `json:"md5"`
PATH string `json:"path"`
Tile []byte `json:"tile"`
Type string `json:"type"`
}
func RenderData(request *ghttp.Request, data []byte) {
request.Response.Header().Set("Cache-Control", "private,max-age="+strconv.Itoa(60*60))
request.Response.WriteHeader(http.StatusOK)
request.Response.Writer.Write(data)
}
func CloseDB(db *gorm.DB) {
s, err := db.DB()
if err != nil {
return
}
s.Close()
}
var IS_AUTH_SUCCESS = false //是否授权通过
func CheckAuth() bool {
return IS_AUTH_SUCCESS
}
func SetAuth(auth bool) {
IS_AUTH_SUCCESS = auth
}
/*坐标点*/
type Point struct {
Lng float64 `json:"lng" v:"required"`
Lat float64 `json:"lat" v:"required"`
Alt float64 `json:"alt" v:"required"`
}

197
third/plane/mqtt/emqx.go Normal file
View File

@ -0,0 +1,197 @@
package mqtt
import (
"context"
"fmt"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gtimer"
"github.com/tiger1103/gfast/v3/third/plane/dj"
"github.com/tiger1103/gfast/v3/third/plane/event"
"strings"
"time"
)
var timer *gtimer.Timer
var client mqtt.Client
var eventMap map[string]*event.EventPublisher
const (
osd = "osd" //设备端定频向云平台推送的设备属性properties 具体内容范围参见物模型内容
state = "state" //设备端按需上报向云平台推送的设备属性properties 具体内容范围参见物模型内容
services_reply = "services_reply" //设备对 service 的回复、处理结果
events = "events" //设备端向云平台发送的,需要关注和处理的事件。 比如SD满了飞机解禁禁飞区等信息事件范围参见物模型内容
status = "status" //设备上下线、更新拓扑
set_reply = "set_reply" //设备属性设置的响应
requests = "requests" //设备端向云平台发送请求,为了获取一些信息,比如上传的临时凭证
services = "services" //云平台向设备发送的服务具体service identifier 见物模型内容)。
)
var MsgCallBackMap map[string]CB
func InitMQTT() {
eventMap = make(map[string]*event.EventPublisher)
timer = gtimer.New()
initMsgs()
dj.InitError()
connect()
}
func initMsgs() {
MsgCallBackMap = make(map[string]CB)
msgs := []CB{
{osd, dealOsd, nil},
{state, deal_state, nil},
{requests, deal_requests, nil},
{services_reply, deal_services_reply, nil},
{status, deal_status, nil},
{events, deal_events, nil},
{services, deal_services, nil},
}
for _, msg := range msgs {
MsgCallBackMap[msg.Flag] = msg
}
}
type CB struct {
Flag string
Recv func(mqtt.Client, mqtt.Message) //接收消息
Reply func(mqtt.Client, mqtt.Message) //回复消息
}
func connect() {
host := g.Cfg().MustGet(gctx.New(), "mqtt.host").String()
port := g.Cfg().MustGet(gctx.New(), "mqtt.port").String()
username := g.Cfg().MustGet(gctx.New(), "mqtt.username").String()
password := g.Cfg().MustGet(gctx.New(), "mqtt.password").String()
clientid := g.Cfg().MustGet(gctx.New(), "mqtt.clientid").String()
topics := g.Cfg().MustGet(gctx.New(), "mqtt.topics").String()
fmt.Println(host, port, username, password, clientid, topics, "tcp://"+host+":"+port)
opts := mqtt.NewClientOptions()
opts.AddBroker("tcp://" + host + ":" + port) // 设置MQTT代理服务器地址
opts.SetClientID(clientid) // 设置客户端ID
opts.SetPassword(password) // 设置客户端ID
opts.SetUsername(username) // 设置客户端ID
// 处理消息接收
opts.SetDefaultPublishHandler(receiveHandler)
opts.SetConnectionLostHandler(disconnected)
opts.SetOnConnectHandler(connected)
opts.SetKeepAlive(1000 * time.Second) //超时等待,防止客户端掉线
opts.SetAutoReconnect(true)
client = mqtt.NewClient(opts)
// 连接MQTT代理服务器
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
arr := strings.Split(topics, ",")
for _, topic := range arr {
// 订阅主题
fmt.Println("订阅主题", topic)
if token := client.Subscribe(topic, 0, nil); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
}
//fmt.Println(topic)
}
//ctx := gctx.New()
////格式化
//DeviceFormat(ctx, "4SEDL9C001X8GE")
//DroneFormat(ctx, "4SEDL9C001X8GE")
//DeviceFormat(ctx, "7CTDM4100BG7HV")
//DroneFormat(ctx, "7CTDM4100BG7HV")
//飞机升级
//OtaCreateFunc(ctx, "8UUXN4P00A06NK")
//gtimer.SetTimeout(gctx.New(), 2*time.Second, func(ctx context.Context) {
//sn := "4SEDL9C001X8GE"
//err, d := speaker_audio_play_start(sn)
//if err != nil {
// fmt.Println(err)
// return
//}
//整一个pcm的音频文件
// point := dj.Point{}
// point.Longitude = 106.541923087
// point.Latitude = 23.458531397
// point.Height = 823.8873291015625 + 10
// //err, d := Flight_authority_grab(sn)
// //if err != nil {
// // return
// //}
// err, d := Fly_to_point(sn, point)
//fmt.Println(d)
// fmt.Println(err)
//
// sn = "4TADL210010014" //中煤广西机库的sn
// //// //sn = "4TADL2E001002D" //机库的sn
// //// //sn = "1581F5BMD2323001S928" //飞机的sn
// ////dev_sn := "1581F5BMD23280014131" //吉林飞机的sn
// //dev_sn := "1581F5BMD238V00172JR" //吉林飞机的sn
// //camera_index := "165-0-7" //机库的摄像头id
// ////camera_index = "53-0-0" //飞机摄像头1的id
// //camera_index = "39-0-7" //飞机摄像头2的id
// //Debug_mode_open(sn) //调试模式,先调试模式才能飞行器开机,推流不需要調試
// //Debug_mode_close(sn)
// //err, d := Cover_open(sn) //打开舱盖
// //err, d := Cover_close(sn)
// //err, d := Drone_open(sn) //飞行器开机
// //err, d := Drone_close(sn) //飞行器关机
// //err, d := Device_reboot(sn)
// //
// //fmt.Println(d)
// //if err != nil {
// // fmt.Println(err)
// //} //打开舱盖
// ////fmt.Println("打开舱盖")
// //err, d := Live_start_push(sn, sn, camera_index)
// //err, d := Live_stop_push(sn, sn, camera_index)
//
// //fmt.Println(d)
// //if err != nil {
// // fmt.Println(err)
// // return
// //} //关闭舱盖
// ////fmt.Println(d.Data.Result)
// //// //device_reboot()
// //// //if err != nil {
// //// // return
// //// //}
// //// //fmt.Println(d)
// //// //live_stop_push(sn, sn, camera_index)
// ////
//})
}
func receiveHandler(client mqtt.Client, msg mqtt.Message) {
topic := msg.Topic()
arrs := strings.Split(topic, "/")
tp := arrs[len(arrs)-1]
if v, ok := MsgCallBackMap[tp]; ok {
v.Recv(client, msg)
if v.Reply != nil {
v.Reply(client, msg)
}
} else {
fmt.Println("非法topic", topic)
}
}
func disconnected(client mqtt.Client, err error) {
fmt.Println("断开了,准备重连")
if err != nil {
fmt.Println(err)
g.Log("uav").Error(gctx.New(), err)
}
timer.Start()
timer.Add(gctx.New(), time.Second*5, func(ctx context.Context) {
connect()
})
}
func connected(client mqtt.Client) {
fmt.Println("链接成功")
timer.Stop()
}

View File

@ -0,0 +1,31 @@
package mqtt
// LiveStreamingCapabilityUpdateEntity 直播能力更新 UP
type LiveStreamingCapabilityUpdateEntity struct {
Tid string `json:"tid"`
Bid string `json:"bid"`
Timestamp int64 `json:"timestamp"`
Gateway string `json:"gateway"`
Method string `json:"method"`
Data struct {
LiveCapacity struct {
AvailableVideoNumber int `json:"available_video_number"`
CoexistVideoNumberMax int `json:"coexist_video_number_max"`
DeviceList []struct {
Sn string `json:"sn"`
AvailableVideoNumber int `json:"available_video_number"`
CoexistVideoNumberMax int `json:"coexist_video_number_max"`
CameraList []struct {
CameraIndex string `json:"camera_index"`
AvailableVideoNumber int `json:"available_video_number"`
CoexistVideoNumberMax int `json:"coexist_video_number_max"`
VideoList []struct {
VideoIndex string `json:"video_index"`
VideoType string `json:"video_type"`
SwitchableVideoTypes []string `json:"switchable_video_types"`
} `json:"video_list"`
} `json:"camera_list"`
} `json:"device_list"`
} `json:"live_capacity"`
} `json:"data"`
}

1282
third/plane/mqtt/msg_deal.go Normal file

File diff suppressed because it is too large Load Diff

334
third/plane/plane.go Normal file
View File

@ -0,0 +1,334 @@
package plane
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/tiger1103/gfast/v3/internal/app/system/service"
"github.com/tiger1103/gfast/v3/third/plane/dj"
"github.com/tiger1103/gfast/v3/third/plane/globe"
"github.com/tiger1103/gfast/v3/third/plane/mqtt"
"strconv"
)
type PLANE struct {
}
func InitPlaneApi(group *ghttp.RouterGroup) {
init_map()
group.Group("/plane", func(group *ghttp.RouterGroup) {
group.Bind(new(PLANE))
group.Middleware(service.Middleware().Ctx, service.Middleware().Auth)
})
}
func init_map() {
globe.WS_Status_map = make(globe.WS_MAP)
globe.WS_Progress_drone_open_map = make(globe.WS_MAP)
globe.WS_Progress_drone_close_map = make(globe.WS_MAP)
globe.WS_Progress_cover_open_map = make(globe.WS_MAP)
globe.WS_Progress_cover_close_map = make(globe.WS_MAP)
globe.WS_Progress_device_reboot_map = make(globe.WS_MAP)
globe.WS_Progress_putter_close_map = make(globe.WS_MAP)
globe.WS_Progress_putter_open_map = make(globe.WS_MAP)
globe.WS_Reply = make(globe.WS_MAP)
}
type UsualRes struct {
}
type DeviceGoHomeReq struct {
g.Meta `path:"gohome" summary:"一键返航" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) DeviceGoHome(ctx context.Context, req *DeviceGoHomeReq) (res *UsualRes, err error) {
err, d := mqtt.Return_home(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
return
}
type DeviceListReq struct {
g.Meta `path:"list" summary:"获取飞机列表" method:"get" tags:"无人机相关"`
}
type DeviceListRes struct {
}
func (receiver PLANE) DeviceList(ctx context.Context, req *DeviceListReq) (res *DeviceListRes, err error) {
return nil, err
}
type DeviceRebootReq struct {
g.Meta `path:"device_reboot" summary:"机库重启" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) DeviceReboot(ctx context.Context, req *DeviceRebootReq) (res *UsualRes, err error) {
err, d := mqtt.Device_reboot(req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
return
}
type ClosePutterReq struct {
g.Meta `path:"close_putter" summary:"关闭推杆" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) ClosePutterReq(ctx context.Context, req *ClosePutterReq) (res *UsualRes, err error) {
err, d := mqtt.Putter_close(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"push_rod": 2})
return
}
type OpenPutterReq struct {
g.Meta `path:"open_putter" summary:"打开推杆" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) OpenPutterReq(ctx context.Context, req *OpenPutterReq) (res *UsualRes, err error) {
err, d := mqtt.Putter_open(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"push_rod": 1})
return
}
type OpenChargeReq struct {
g.Meta `path:"open_charge" summary:"打开充电" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) OpenChargeReq(ctx context.Context, req *OpenChargeReq) (res *UsualRes, err error) {
err, d := mqtt.Charge_open(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"recharge": 1})
return
}
type CloseChargeReq struct {
g.Meta `path:"close_charge" summary:"关闭充电" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) CloseCharge(ctx context.Context, req *CloseChargeReq) (res *UsualRes, err error) {
err, d := mqtt.Charge_close(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"recharge": 2})
return
}
type CloseDroneReq struct {
g.Meta `path:"close_drone" summary:"飞行器关机(调试模式下)" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) CloseDrone(ctx context.Context, req *CloseDroneReq) (res *UsualRes, err error) {
err, d := mqtt.Drone_close(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"power": 2})
return
}
type OpenDroneReq struct {
g.Meta `path:"open_drone" summary:"飞行器开机(调试模式下)" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) OpenDrone(ctx context.Context, req *OpenDroneReq) (res *UsualRes, err error) {
err, d := mqtt.Drone_open(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"power": 1})
return
}
type CloseCoverReq struct {
g.Meta `path:"close_cover" summary:"关闭舱盖(调试模式下)" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) CloseCover(ctx context.Context, req *CloseCoverReq) (res *UsualRes, err error) {
err, d := mqtt.Cover_close(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"hatch_cover": 2})
return
}
type OpenCoverReq struct {
g.Meta `path:"open_cover" summary:"打开舱盖(调试模式下)" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) OpenCover(ctx context.Context, req *OpenCoverReq) (res *UsualRes, err error) {
err, d := mqtt.Cover_open(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"hatch_cover": 1})
return
}
type CloseDebugReq struct {
g.Meta `path:"close_debug" summary:"关闭调试模式" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) CloseDebug(ctx context.Context, req *CloseDebugReq) (res *UsualRes, err error) {
err, d := mqtt.Debug_mode_close(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"debug": 2})
return
}
type OpenDebugReq struct {
g.Meta `path:"open_debug" summary:"打开调试模式" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
}
func (receiver PLANE) OpenDebug(ctx context.Context, req *OpenDebugReq) (res *UsualRes, err error) {
err, d := mqtt.Debug_mode_open(ctx, req.GatewaySn)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
g.DB().Model("manage_button_state").
Where("mq_client_id", req.GatewaySn).
Update(g.Map{"debug": 1})
return
}
type StopLiveReq struct {
g.Meta `path:"stop_live" summary:"停止直播" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
Devsn string `json:"devsn" v:"required" dc:"设备sn"`
Camera_index string `json:"camera_index" v:"required" dc:"摄像头"`
}
func (receiver PLANE) StopLive(ctx context.Context, req *StopLiveReq) (res *UsualRes, err error) {
err, d := mqtt.Live_stop_push(req.GatewaySn, req.Devsn, req.Camera_index)
if err != nil {
return nil, err
}
if d.Data.Result > 0 {
err = geterror(d.Data.Result)
return
}
return
}
type StartLiveReq struct {
g.Meta `path:"start_live" summary:"开始直播" method:"post" tags:"无人机相关"`
GatewaySn string `json:"gateway_sn" v:"required" dc:"机库sn"`
Devsn string `json:"devsn" v:"required" dc:"设备sn"`
Camera_index string `json:"camera_index" v:"required" dc:"摄像头"`
}
type StartLiveRes struct {
Url string `json:"url"`
}
func geterror(code int) error {
if v, ok := dj.GetErrorMap()[code]; ok {
return globe.GetErrors(v.Msg)
} else {
return globe.GetErrors("未知错误:" + strconv.Itoa(code))
}
}
func (receiver PLANE) StartLive(ctx context.Context, req *StartLiveReq) (res *StartLiveRes, err error) {
err, d := mqtt.Live_start_push(req.GatewaySn, req.Devsn, req.Camera_index)
if err != nil {
return nil, err
}
if d.Data.Result > 0 && d.Data.Result != 513003 /*正在直播中 ,可直接返回地址*/ {
err = geterror(d.Data.Result)
return
}
res = &StartLiveRes{}
res.Url = d.Data.Url
return
}

107
third/plane/progress.go Normal file
View File

@ -0,0 +1,107 @@
package plane
import (
"context"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/tiger1103/gfast/v3/third/plane/globe"
)
type Progerss_putter_close_Req struct {
g.Meta `path:"progress/putter_close" summary:"推杆闭合进度" method:"get" tags:"无人机进度数据相关ws"`
}
func (receiver PLANE) Progerss_putter_close(ctx context.Context, req *Progerss_putter_close_Req) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Progress_putter_close_map)
}
type Progerss_putter_open_Req struct {
g.Meta `path:"progress/putter_open" summary:"推杆展开进度" method:"get" tags:"无人机进度数据相关ws"`
}
func (receiver PLANE) Progerss_putter_open(ctx context.Context, req *Progerss_putter_open_Req) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Progress_putter_open_map)
}
type Progerss_Reply_Req struct {
g.Meta `path:"progress/err_reply" summary:"up回复错误状态" method:"get" tags:"无人机进度数据相关ws"`
}
func (receiver PLANE) Progerss_Reply(ctx context.Context, req *Progerss_Reply_Req) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Reply)
}
type Progerss_device_reboot_Req struct {
g.Meta `path:"progress/device_reboot" summary:"机库重启进度推送" method:"get" tags:"无人机进度数据相关ws"`
}
func (receiver PLANE) Progerss_device_reboot(ctx context.Context, req *Progerss_device_reboot_Req) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Progress_device_reboot_map)
}
type Progerss_cover_close_Req struct {
g.Meta `path:"progress/cover_close" summary:"机库关闭舱盖进度推送" method:"get" tags:"无人机进度数据相关ws"`
}
func (receiver PLANE) Progerss_cover_close(ctx context.Context, req *Progerss_cover_close_Req) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Progress_cover_close_map)
}
type Progerss_cover_open_Req struct {
g.Meta `path:"progress/cover_open" summary:"机库打开舱盖进度推送" method:"get" tags:"无人机进度数据相关ws"`
}
func (receiver PLANE) Progerss_cover_open(ctx context.Context, req *Progerss_cover_open_Req) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Progress_cover_open_map)
}
type Progerss_drone_close_Req struct {
g.Meta `path:"progress/drone_close" summary:"飞行器关机进度推送" method:"get" tags:"无人机进度数据相关ws"`
}
func (receiver PLANE) Progerss_drone_close(ctx context.Context, req *Progerss_drone_close_Req) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Progress_drone_close_map)
}
type Progerss_drone_open_Req struct {
g.Meta `path:"progress/drone_open" summary:"飞行器开机进度推送" method:"get" tags:"无人机进度数据相关ws"`
}
func (receiver PLANE) Progerss_drone_open(ctx context.Context, req *Progerss_drone_open_Req) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Progress_drone_open_map)
}
type DeviceStatusReq struct {
g.Meta `path:"device_status" summary:"ws推送飞机状态" method:"get" tags:"无人机进度数据相关ws"`
}
type DeviceStatusRes struct {
Code int `json:"code"`
Data interface{} `json:"data"`
}
func (receiver PLANE) DeviceStatus(ctx context.Context, req *DeviceStatusReq) (res *UsualRes, err error) {
return save_ws(ctx, &globe.WS_Status_map)
}
func save_ws(ctx context.Context, _map *globe.WS_MAP) (res *UsualRes, err error) {
r := ghttp.RequestFromCtx(ctx)
ws, err := r.WebSocket()
if err != nil {
return nil, err
}
ip := r.Get("token").String()
globe.MutexRw.Lock()
(*_map)[ip] = ws
globe.MutexRw.Unlock()
fmt.Println("ws连接了", ip)
for {
_, _, err = ws.ReadMessage()
if err != nil {
fmt.Println("ws断开了", ip)
delete(*_map, ip)
return nil, err
}
}
}

View File

@ -0,0 +1,157 @@
package _struct
import "encoding/xml"
// 飞向首航点模式
const (
Safely = "safely"
PointToPoint = "pointToPoint"
)
// 航线结束动作
const (
GoHome = "goHome"
AutoLand = "autoLand"
GotoFirstWaypoint = "gotoFirstWaypoint"
)
//失控是否继续执行航线
const (
GoContinue = "goContinue" //继续执行航线
ExecuteLostAction = "executeLostAction" //退出航线,执行失控动作
)
// 失控动作类型
const (
GoBack = "goBack" //返航。飞行器从失控位置飞向起飞点
Landing = "landing" //降落。飞行器从失控位置原地降落
Hover = "hover" //悬停。飞行器从失控位置悬停
)
// 预定义模板类型
const (
Waypoint = "waypoint" //航点飞行
Mapping2d = "mapping2d" //建图航拍
Mapping3d = "mapping3d" //倾斜摄影
MappingStrip = "mappingStrip" //航带飞行
)
// Placemark 结构表示 KML 的地点标记
type Placemark struct {
Point Point `xml:"Point"`
Index int `xml:"wpml:index"`
EllipsoidHeight float64 `xml:"wpml:ellipsoidHeight"`
Height float64 `xml:"wpml:height"`
UseGlobalSpeed int `xml:"wpml:useGlobalSpeed"`
UseGlobalHeadingParam int `xml:"wpml:useGlobalHeadingParam"`
UseGlobalTurnParam int `xml:"wpml:useGlobalTurnParam"`
UseStraightLine int `xml:"wpml:useStraightLine"`
//ActionGroup ActionGroup `xml:"wpml:actionGroup"`
}
// Point 结构表示地点坐标
type Point struct {
Coordinates string `xml:"coordinates"`
}
type PayloadParam struct {
PayloadPositionIndex int `xml:"wpml:payloadPositionIndex"`
ImageFormat string `xml:"wpml:imageFormat"`
}
type Doc struct {
Author string `xml:"wpml:author"`
UpdateTime int64 `xml:"wpml:updateTime"`
CreateTime int64 `xml:"wpml:createTime"`
MissionConfig MissionConfig `xml:"wpml:missionConfig"`
Folder interface{} `xml:"Folder"`
}
type PublicTemplate struct {
TemplateType string `xml:"wpml:templateType"`
TemplateId int `xml:"wpml:templateId"`
AutoFlightSpeed float64 `xml:"wpml:autoFlightSpeed"`
WaylineCoordinateSysParam struct {
CoordinateMode string `xml:"wpml:coordinateMode"`
HeightMode string `xml:"wpml:heightMode"`
GlobalShootHeight float64 `xml:"wpml:globalShootHeight"`
PositioningType string `xml:"wpml:positioningType"`
SurfaceFollowModeEnable int `xml:"wpml:surfaceFollowModeEnable"`
SurfaceRelativeHeight string `xml:"wpml:surfaceRelativeHeight"`
} `xml:"wpml:waylineCoordinateSysParam"`
}
type ActionGroup struct {
ActionGroupId int `xml:"wpml:actionGroupId"`
ActionGroupStartIndex int `xml:"wpml:actionGroupStartIndex"`
ActionGroupEndIndex int `xml:"wpml:actionGroupEndIndex"`
ActionGroupMode string `xml:"wpml:actionGroupMode"`
ActionTrigger struct {
ActionTriggerType string `xml:"actionTriggerType"`
} `xml:"actionTrigger"`
Action Action `xml:"action"`
}
type Action struct {
ActionId int `xml:"actionId"`
ActionActuatorFunc string `xml:"actionActuatorFunc"`
ActionActuatorFuncParam ActionActuatorFuncParam `xml:"actionActuatorFuncParam"`
}
type ActionActuatorFuncParam struct {
FileSuffix string `xml:"fileSuffix"`
PayloadPositionIndex string `xml:"payloadPositionIndex"`
}
type MissionConfig struct {
FlyToWaylineMode string `xml:"wpml:flyToWaylineMode"`
FinishAction string `xml:"wpml:finishAction"`
ExitOnRCLost string `xml:"wpml:exitOnRCLost"`
ExecuteRCLostAction string `xml:"wpml:executeRCLostAction"`
TakeOffSecurityHeight float64 `xml:"wpml:takeOffSecurityHeight"`
GlobalTransitionalSpeed float64 `xml:"wpml:globalTransitionalSpeed"`
GlobalRTHHeight float64 `xml:"wpml:globalRTHHeight"`
//TakeOffRefPoint xml.Name `xml:"wpml:takeOffRefPoint"`
//
//TakeOffRefPointAGLHeight xml.Name `xml:"wpml:takeOffRefPointAGLHeight"`
//DroneInfo struct {
// DroneEnumValue xml.Name `xml:"wpml:droneEnumValue"`
// DroneSubEnumValue xml.Name `xml:"wpml:droneSubEnumValue"`
//} `xml:"wpml:droneInfo"`
//PayloadInfo struct {
// PayloadEnumValue xml.Name `xml:"wpml:payloadEnumValue"`
// PayloadSubEnumValue xml.Name `xml:"wpml:payloadSubEnumValue"`
// PayloadPositionIndex xml.Name `xml:"wpml:payloadPositionIndex"`
//} `xml:"wpml:payloadInfo"`
}
type WayPointTemplate struct {
PublicTemplate
GlobalWaypointTurnMode string `xml:"wpml:globalWaypointTurnMode"` //全局航点类型全局航点转弯模式coordinateTurn协调转弯不过点提前转弯 toPointAndStopWithDiscontinuityCurvature直线飞行飞行器到点停 toPointAndStopWithContinuityCurvature曲线飞行飞行器到点停 toPointAndPassWithContinuityCurvature曲线飞行飞行器过点不停
GlobalUseStraightLine int `xml:"wpml:globalUseStraightLine"` //全局航段轨迹是否尽量贴合直线
GimbalPitchMode string `xml:"wpml:gimbalPitchMode"` //云台俯仰角控制模式 manual手动控制。飞行器从一个航点飞向下一个航点的过程中
// 支持用户手动控制云台的俯仰角度。若无用户控制,则保持飞离航点时的云台俯仰角度。
//usePointSetting依照每个航点设置。飞行器从一个航点飞向下一个航点的过程中云台俯仰角均匀过渡至下一个航点的俯仰角。
GlobalHeight float64 `xml:"wpml:globalHeight"` //全局航线高度(相对起飞点高度
Placemark []Placemark `xml:"Placemark"`
//UseGlobalTransitionalSpeed string `xml:"wpml:useGlobalTransitionalSpeed"`
//TransitionalSpeed string `xml:"wpml:transitionalSpeed"`
//GlobalWaypointHeadingParam struct {
// WaypointHeadingMode string `xml:"wpml:waypointHeadingMode"`
// WaypointHeadingAngle string `xml:"wpml:waypointHeadingAngle"`
// WaypointPoiPoint string `xml:"wpml:waypointPoiPoint"`
// WaypointHeadingPathMode string `xml:"wpml:waypointHeadingPathMode"`
//} `xml:"wpml:globalWaypointHeadingParam"` //全局偏航角模式参数
}
// KML 结构表示完整的 KML 文件
type KML struct {
//XMLName xml.Attr `xml:"kml"`
XMLName xml.Name `xml:"http://www.opengis.net/kml/2.2 kml"`
XmlnsStream string `xml:"xmlns:wpml,attr"`
//Test []Test `xml:"test"`
Document Doc `xml:"Document"`
}

View File

@ -0,0 +1,78 @@
package template
import (
"encoding/xml"
"fmt"
"github.com/gogf/gf/v2/os/gtime"
"github.com/tiger1103/gfast/v3/third/plane/wayline/struct"
"os"
)
func CreateWayPointTempalteWpml() {
}
func CreateWayPointTempalteKml() {
kml := _struct.KML{
//XmlnsStream: "http://www.dji.com/wpmz/1.0.3",
Document: _struct.Doc{
Author: "重庆远界大数据研究院有限公司",
CreateTime: gtime.Now().TimestampMilli(),
UpdateTime: gtime.Now().TimestampMilli(),
},
}
msconfig := _struct.MissionConfig{
FlyToWaylineMode: _struct.Safely,
FinishAction: _struct.GoHome,
ExitOnRCLost: _struct.ExecuteLostAction,
ExecuteRCLostAction: _struct.Hover,
TakeOffSecurityHeight: 5,
GlobalTransitionalSpeed: 5,
GlobalRTHHeight: 5,
}
//设置公共配置
kml.Document.MissionConfig = msconfig
tp := _struct.WayPointTemplate{}
tp.TemplateType = _struct.Waypoint //航点飞行
tp.TemplateId = 0 //模板ID * 注在一个kmz文件内该ID唯一。建议从0开始单调连续递增。在template.kml和waylines.wpml文件中将使用该id将模板与所生成的可执行航线进行关联。
tp.AutoFlightSpeed = 5 //全局航线飞行速度
tp.WaylineCoordinateSysParam.CoordinateMode = "WGS84"
tp.WaylineCoordinateSysParam.HeightMode = "EGM96"
tp.WaylineCoordinateSysParam.PositioningType = "GPS"
tp.WaylineCoordinateSysParam.SurfaceFollowModeEnable = 0
tp.GlobalHeight = 100
kml.Document.Folder = tp
createXML(kml, "template.xml")
}
func createXML(v interface{}, file string) {
// 生成 XML
xmlBytes, err := xml.MarshalIndent(v, "", " ")
if err != nil {
fmt.Println("生成XML失败:", err)
return
}
// 将 XML 写入文件
xmlFile, err := os.Create(file)
if err != nil {
fmt.Println("创建文件失败:", err)
return
}
defer xmlFile.Close()
xmlWithVersion := []byte(xml.Header + string(xmlBytes))
_, err = xmlFile.Write(xmlWithVersion)
if err != nil {
fmt.Println("写入文件失败:", err)
return
}
fmt.Println("生成的KML文件output.kml")
}
//func CreateWayPointTempalte(points []globe.Point) {
//
//}

View File

@ -0,0 +1 @@
package wayline