You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

244 lines
8.0 KiB

package mcp
import (
"context"
"yunyan/lego/core"
"yunyan/lego/core/cbase"
"yunyan/lego/sys/log"
"yunyan/sys/googlemap"
"yunyan/utils"
"fmt"
"sync"
"github.com/mark3labs/mcp-go/mcp"
)
// 天气卡片数据 结构统一化输出
type GoogleNavigationCard struct {
Origin string `json:"origin"` //起始点
Destination string `json:"destination"` //目标点
Start string `json:"start"` //起始点
End string `json:"end"` //目标点
Itmes []GoogleNavigationItem `json:"items"` //导航信息
}
type GoogleNavigationItem struct {
Ntype string `json:"ntype"` //类型
Enable bool `json:"enable"` //启用
Image string `json:"image"` //图片
MapUrl string `json:"mapurl"` //地图链接
Destination string `json:"destination"` //目标点
Distance string `json:"distance"` //距离
Duration string `json:"duration"` //时间
Path []struct {
Action string `json:"action"` //动作 右转
Distance string `json:"distance"` //距离 距离
} `json:"path"` //路径
Transits []struct {
Name string `json:"name"` //搅动方式
Distance string `json:"distance"` //距离
} `json:"transits"` //路径
}
// 天气查询
type tool_google_map_route_navigation struct {
cbase.ModuleCompBase
module *Mcp
}
// 组件初始化接口
func (this *tool_google_map_route_navigation) Init(service core.IService, module core.IModule, comp core.IModuleComp, opt core.IModuleOptions) (err error) {
this.ModuleCompBase.Init(service, module, comp, opt)
this.module = module.(*Mcp)
return
}
func (this *tool_google_map_route_navigation) Start() (err error) {
err = this.ModuleCompBase.Start()
if this.module.AddTool(ToolGroup_OVERSEAS, this.Tool(), this.Handl) {
if err := googlemap.OnInit(this.module.service.GetSettings().Sys["googlemap"]); err != nil {
panic(fmt.Sprintf("init sys.googlemap err: %s", err.Error()))
} else {
log.Infof("init sys.googlemap success!")
}
}
return
}
func (this *tool_google_map_route_navigation) Tool() (tool mcp.Tool) {
return mcp.NewTool("maps_google_route_navigation",
mcp.WithDescription("根据用户提供的起点和终点以及出行方式,提供导航服务"),
mcp.WithString("origins",
mcp.Description("起始点纬经度坐标,比如30,120。坐标格式为:纬度,经度"),
mcp.Required(),
),
mcp.WithString("destination",
mcp.Description("目标点纬经度坐标,比如30,120。坐标格式为:纬度,经度"),
mcp.Required(),
),
mcp.WithString("type",
mcp.Description("交通类型,0:不指定交通工具,1驾车,2骑行,3公交,4步行"),
mcp.DefaultNumber(0),
),
)
}
// 工具处理接口
// 参数:
// - ctx: 上下文
// - request: 工具调用请求,包含起点终点坐标与交通类型
// 返回值:
// - result: 工具调用结果,包含导航卡片或失败提示
// - err: 处理过程中的错误,当导航失败时返回非空错误
// 异常:
// - 当参数缺失、路径规划失败或内部错误时返回错误
func (this *tool_google_map_route_navigation) Handl(ctx context.Context, request mcp.CallToolRequest) (result *mcp.CallToolResult, err error) {
var (
origins, destination string
startaddress string
destinationaddress string
direction_driving *googlemap.RootResponse
direction_walking *googlemap.RootResponse
direction_bicycling *googlemap.RootResponse
direction_tsransit *googlemap.RootResponse
card *GoogleNavigationCard
errs map[string]error
errlock sync.Mutex
context string
ok bool
)
this.module.Debug("tool_google_map_route_navigation",
log.Field{Key: "request.Params.Arguments", Value: request.GetRawArguments()},
)
if origins, err = request.RequireString("origins"); err != nil || origins == "" {
err = fmt.Errorf("必要参数:origins丢失")
context = "获取目标地址失败"
return
}
if destination, err = request.RequireString("destination"); err != nil || destination == "" {
err = fmt.Errorf("必要参数:destination丢失")
context = "获取目标地址失败"
return
}
var wg sync.WaitGroup
errs = make(map[string]error)
wg.Add(4)
go func() {
defer wg.Done()
if direction_driving, err = googlemap.Navigation(origins, destination, "driving"); err != nil {
errlock.Lock()
errs["driving"] = err
errlock.Unlock()
}
}()
go func() {
defer wg.Done()
if direction_walking, err = googlemap.Navigation(origins, destination, "walking"); err != nil {
errlock.Lock()
errs["walking"] = err
errlock.Unlock()
}
}()
go func() {
defer wg.Done()
if direction_bicycling, err = googlemap.Navigation(origins, destination, "bicycling"); err != nil {
errlock.Lock()
errs["bicycling"] = err
errlock.Unlock()
}
}()
go func() {
defer wg.Done()
if direction_tsransit, err = googlemap.Navigation(origins, destination, "transit"); err != nil {
errlock.Lock()
errs["transit"] = err
errlock.Unlock()
}
}()
wg.Wait()
// 任意一种路径规划成功则认为成功
if direction_driving != nil || direction_walking != nil || direction_bicycling != nil || direction_tsransit != nil {
ok = true
}
if !ok {
context = "导航失败,请检查起点和终点是否正确"
this.module.Debug("tool_google_map_route_navigation fail",
log.Field{Key: "origins", Value: origins},
log.Field{Key: "destination", Value: destination},
log.Field{Key: "errs", Value: errs},
)
// 返回非空错误避免上层对nil结果解引用造成panic
err = fmt.Errorf("导航失败")
return
} else {
// 选择第一个非空的路径结果用于地址填充
if direction_driving != nil {
startaddress = direction_driving.OriginAddr
destinationaddress = direction_driving.DestinationAddr
} else if direction_walking != nil {
startaddress = direction_walking.OriginAddr
destinationaddress = direction_walking.DestinationAddr
} else if direction_bicycling != nil {
startaddress = direction_bicycling.OriginAddr
destinationaddress = direction_bicycling.DestinationAddr
} else if direction_tsransit != nil {
startaddress = direction_tsransit.OriginAddr
destinationaddress = direction_tsransit.DestinationAddr
}
card = &GoogleNavigationCard{
Origin: origins,
Destination: destination,
Start: startaddress,
End: destinationaddress,
Itmes: make([]GoogleNavigationItem, 4),
}
card.Itmes[0] = goglepathTransNavigationItem("driving", direction_driving)
card.Itmes[1] = goglepathTransNavigationItem("walking", direction_walking)
card.Itmes[2] = goglepathTransNavigationItem("bicycling", direction_bicycling)
card.Itmes[3] = goglepathTransNavigationItem("transit", direction_tsransit)
context = utils.ToString(map[string]interface{}{
"data": card,
"iscard": true,
"broadcast": false,
"result": fmt.Sprintf("正在为您导航到:%s", destinationaddress),
})
this.module.Debug("tool_google_map_route_navigation success",
log.Field{Key: "origin", Value: startaddress},
log.Field{Key: "address", Value: destinationaddress},
log.Field{Key: "card", Value: card},
)
}
result = &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: context,
},
},
}
return
}
func goglepathTransNavigationItem(ntype string, root *googlemap.RootResponse) (item GoogleNavigationItem) {
item.Ntype = ntype
if root == nil || root.Route == nil || root.Route.Paths == nil || len(root.Route.Paths) == 0 {
return
}
path := root.Route.Paths[0]
item = GoogleNavigationItem{
Ntype: ntype,
Enable: true,
Image: path.Image,
MapUrl: path.MapUrl,
Distance: path.Distance,
Duration: path.Cost.Duration,
}
item.Path = make([]struct {
Action string `json:"action"` //动作 右转
Distance string `json:"distance"` //距离 距离
}, len(path.Steps))
for i, v := range path.Steps {
item.Path[i].Action = v.Navi.Action
item.Path[i].Distance = fmt.Sprintf("%v", v.StepDistance)
}
return
}