696 lines
22 KiB
Go
696 lines
22 KiB
Go
package solaranalyzer
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/gogf/gf/v2/database/gdb"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"github.com/samber/lo"
|
||
"github.com/tidwall/gjson"
|
||
"github.com/tiger1103/gfast/v3/internal/app/system/dao"
|
||
"github.com/tiger1103/gfast/v3/internal/app/system/model/do"
|
||
"github.com/tiger1103/gfast/v3/internal/app/system/model/entity"
|
||
"github.com/tiger1103/gfast/v3/library/liberr"
|
||
|
||
"github.com/tomchavakis/geojson/geometry"
|
||
"github.com/tomchavakis/turf-go"
|
||
)
|
||
|
||
type AIResult struct {
|
||
SolarPanels []XYPosition // 光伏板的坐标点
|
||
Brackets []XYPosition // 支架的坐标点
|
||
Pillars []XYPosition // 立柱的坐标点
|
||
Holes []XYPosition // 钻孔的坐标点
|
||
}
|
||
|
||
// ToGeoPoints 将坐标转换为经纬度坐标
|
||
func (r *AIResult) ToGeoPoints(tifPath string) *AIResult {
|
||
r.SolarPanels = convertToLongitudeAndLatitude(tifPath, r.SolarPanels) // 光伏板
|
||
r.Brackets = convertToLongitudeAndLatitude(tifPath, r.Brackets) // 支架
|
||
r.Pillars = convertToLongitudeAndLatitude(tifPath, r.Pillars) // 立柱
|
||
r.Holes = convertToLongitudeAndLatitude(tifPath, r.Holes) // 钻孔
|
||
return r
|
||
}
|
||
|
||
// IsCircleContainsPoint 判断圆形是否包含点,返回桩点的主键ID
|
||
func (r *AIResult) IsCircleContainsPoint(m map[string]map[string]GeoPoint, usePillars string) []int {
|
||
var ids []int
|
||
|
||
var elements []XYPosition
|
||
switch usePillars {
|
||
case drillingHole:
|
||
elements = r.Holes
|
||
case pillar:
|
||
elements = r.Pillars
|
||
case bracket:
|
||
elements = r.Brackets
|
||
}
|
||
|
||
for _, aiPillar := range elements {
|
||
// AI 识别点
|
||
aiPoint := geometry.Point{Lng: aiPillar.X, Lat: aiPillar.Y}
|
||
|
||
// 方阵层
|
||
for _, points := range m {
|
||
// 立柱层
|
||
for _, dbPillar := range points {
|
||
circle := dbPillar.Coordinates.ExpandToCircle()
|
||
|
||
if ok, err := turf.PointInPolygon(aiPoint, circle); ok && err == nil {
|
||
ids = append(ids, dbPillar.ID)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return ids
|
||
}
|
||
|
||
// GetFromDB 从数据库中获取光伏板、支架、桩点的数据
|
||
func (r *AIResult) GetFromDB(projectID string) ([]Point, map[string]map[string]GeoPoint) {
|
||
// 光伏板、支架
|
||
var solarPanels []Point
|
||
var pillars map[string]map[string]GeoPoint
|
||
|
||
if len(r.SolarPanels) != 0 {
|
||
if points, err := getSolarPanelCenters(projectID); err == nil {
|
||
solarPanels = points
|
||
}
|
||
}
|
||
|
||
if len(r.Pillars) != 0 {
|
||
pillars = getPillars(projectID)
|
||
}
|
||
|
||
return solarPanels, pillars
|
||
}
|
||
|
||
// 传入 AI 结果
|
||
// 1. 将其周围的组串,全部标记为已完成
|
||
// 2. 根据其 【方阵ID】【Type = 13】更新其在 work_status 中的总数 ok
|
||
// 3. 根据当前的 Name Etc: G1.123.1.1 将 pv_module 中的 G1.123.1 状态修改为已完成
|
||
// 4. 记录该数据由 AI 识别。
|
||
func (r *AIResult) Run(projectID string, id int) {
|
||
// 获取光伏板和支架,桩点的数据
|
||
panels, brackets := r.GetFromDB(projectID)
|
||
fmt.Println("(内容)光伏板和支架: ", panels)
|
||
fmt.Println("(内容)桩点: ", brackets)
|
||
|
||
// 预处理光伏板为多边形
|
||
polygons := make([]geometry.Polygon, len(panels))
|
||
for i, rect := range panels {
|
||
coords := make([]geometry.Point, len(rect.Points))
|
||
for j, p := range rect.Points {
|
||
coords[j] = geometry.Point{Lng: p.X, Lat: p.Y}
|
||
}
|
||
polygons[i] = geometry.Polygon{Coordinates: []geometry.LineString{{Coordinates: coords}}}
|
||
}
|
||
fmt.Println("(内容)预处理光伏板为多边形: ", polygons)
|
||
fmt.Println("(内容)光伏板坐标后点: ", r.SolarPanels)
|
||
// 光伏板的主键ID
|
||
var matchedRectangles []int
|
||
// 匹配AI识别的光伏板中心点
|
||
for _, point := range r.SolarPanels {
|
||
for i, polygon := range polygons {
|
||
if ok, err := turf.PointInPolygon(geometry.Point{Lng: point.X, Lat: point.Y}, polygon); err == nil && ok {
|
||
matchedRectangles = append(matchedRectangles, panels[i].ID)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
_, err := dao.ManageTaskResult.Ctx(context.Background()).Insert(
|
||
lo.Map(matchedRectangles, func(v int, _ int) do.ManageTaskResult {
|
||
return do.ManageTaskResult{
|
||
TaskId: id,
|
||
PvId: v,
|
||
}
|
||
}))
|
||
liberr.ErrIsNil(context.Background(), err)
|
||
|
||
if len(matchedRectangles) > 0 { // TODO: 去除写入数据库的逻辑
|
||
// if err := ProcessMatchedRectangles(matchedRectangles); err != nil {
|
||
// g.Log("uav").Error(context.Background(), "更新匹配到的光伏板失败: ", err)
|
||
// }
|
||
}
|
||
|
||
// 更新钻孔
|
||
if len(r.Holes) > 0 {
|
||
ids := r.IsCircleContainsPoint(brackets, drillingHole)
|
||
if len(ids) > 0 {
|
||
if err := processPillarIDs(ids, 12, id); err != nil {
|
||
g.Log("uav").Error(context.Background(), "更新匹配到的支架失败: ", err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新支架和桩点
|
||
if len(r.Pillars) > 0 {
|
||
ids := r.IsCircleContainsPoint(brackets, pillar)
|
||
if len(ids) > 0 {
|
||
if err := processPillarIDs(ids, 13, id); err != nil {
|
||
g.Log("uav").Error(context.Background(), "更新匹配到的支架失败: ", err)
|
||
}
|
||
}
|
||
}
|
||
|
||
if len(r.Brackets) > 0 {
|
||
ids := r.IsCircleContainsPoint(brackets, bracket)
|
||
if len(ids) > 0 {
|
||
if err := processPillarIDs(ids, 14, id); err != nil {
|
||
g.Log("uav").Error(context.Background(), "更新匹配到的支架失败: ", err)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 通过主键ID,可以从 qianqi_guangfuban_ids_zhijia 表中查询到对应的 “方阵ID” 和 "子项目ID"
|
||
// 将得到的结果分组,第一层为 方阵ID,第二层 G01.12.18,第三层数组,[ G01.12.18.1,G01.12.18.2,G01.12.18.3 ]
|
||
// 如果第三层中有数据,则从数据库中,向前后查询相邻组串,并将其组串修改为已完成,同时取出第二层和方阵ID,用于更新 pv_module 和 work_status 表中的数据。
|
||
func processPillarIDs(ids []int, t, id int) error {
|
||
return nil
|
||
|
||
// 查询 qianqi_guangfuban_ids_zhijia 表
|
||
var zhijias []entity.QianqiGuangfubanIdsZhijia
|
||
if err := dao.QianqiGuangfubanIdsZhijia.Ctx(context.Background()).
|
||
Fields("id,fangzhen_id,sub_projectid,name").
|
||
WhereIn(dao.QianqiGuangfubanIdsZhijia.Columns().Id, ids).
|
||
Scan(&zhijias); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 第一层方阵ID, 第二层支架名称, 第三层支架信息
|
||
realGeoPoint := make(map[string]map[string][]entity.QianqiGuangfubanIdsZhijia)
|
||
for _, zhijia := range zhijias {
|
||
fangzhenID := zhijia.FangzhenId // 方阵ID
|
||
zhijiaName := zhijia.Name[:strings.LastIndex(zhijia.Name, ".")] // 支架名称
|
||
|
||
if _, ok := realGeoPoint[fangzhenID]; !ok {
|
||
realGeoPoint[fangzhenID] = make(map[string][]entity.QianqiGuangfubanIdsZhijia)
|
||
}
|
||
|
||
realGeoPoint[fangzhenID][zhijiaName] = append(realGeoPoint[fangzhenID][zhijiaName], zhijia)
|
||
}
|
||
|
||
// 查询支架及其相邻的组串
|
||
for _, geoPoints := range realGeoPoint {
|
||
for pointKey, pointList := range geoPoints {
|
||
if len(pointList) > 1 {
|
||
firstPoint := pointList[0]
|
||
|
||
simulationID := firstPoint.FangzhenId
|
||
namePattern := firstPoint.Name[:strings.LastIndex(firstPoint.Name, ".")] + ".%"
|
||
|
||
var updatedPoints []entity.QianqiGuangfubanIdsZhijia
|
||
if err := dao.QianqiGuangfubanIdsZhijia.Ctx(context.Background()).Where(dao.QianqiGuangfubanIdsZhijia.Columns().FangzhenId, simulationID).
|
||
Where(dao.QianqiGuangfubanIdsZhijia.Columns().Name+" LIKE ?", namePattern).
|
||
Scan(&updatedPoints); err == nil && len(updatedPoints) > 0 {
|
||
realGeoPoint[simulationID][pointKey] = updatedPoints
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新数据库状态
|
||
for _, geoPoints := range realGeoPoint {
|
||
for key, pointList := range geoPoints {
|
||
// 取出所有的ID
|
||
primaryIDs := lo.Map(pointList, func(v entity.QianqiGuangfubanIdsZhijia, _ int) int {
|
||
return v.Id
|
||
})
|
||
|
||
g.Try(context.Background(), func(ctx context.Context) {
|
||
// 根据主键修改为已完成
|
||
// 修改组串的状态
|
||
// G01.12.18.1 -- G01.12.18.2 -- G01.12.18.3
|
||
_, err := dao.QianqiGuangfubanIdsZhijia.Ctx(ctx).WhereIn(dao.QianqiGuangfubanIdsZhijia.Columns().Id, primaryIDs).
|
||
Data(g.Map{
|
||
dao.QianqiGuangfubanIdsZhijia.Columns().Status: 2,
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err)
|
||
|
||
fangzhenID := pointList[0].FangzhenId
|
||
|
||
// 根据名字从 pv_module 中获取主键id
|
||
result, err := dao.PvModule.Ctx(ctx).Fields("id").Where(dao.PvModule.Columns().Name, key).
|
||
Where(dao.PvModule.Columns().FangzhenId, fangzhenID).
|
||
Where(dao.PvModule.Columns().Type, t).One()
|
||
liberr.ErrIsNil(ctx, err)
|
||
|
||
// 获取主键ID
|
||
keyID := result.Map()["id"].(int)
|
||
dao.ManageTaskResult.Ctx(ctx).Data(do.ManageTaskResult{
|
||
TaskId: id,
|
||
PvId: keyID,
|
||
}).Insert() // 记录
|
||
|
||
// 更新 PVModule 表中的状态
|
||
// G01.12.18
|
||
lastID, err := dao.PvModule.Ctx(ctx).WhereIn(dao.PvModule.Columns().Id, keyID).
|
||
Where(dao.PvModule.Columns().FangzhenId, fangzhenID).
|
||
Where(dao.PvModule.Columns().Type, t).
|
||
Data(g.Map{
|
||
dao.PvModule.Columns().Status: 2,
|
||
}).Update()
|
||
|
||
// 获取影响行数并更新 work_status 表
|
||
rows, err := lastID.RowsAffected()
|
||
liberr.ErrIsNil(ctx, err)
|
||
if rows > 0 {
|
||
_, err := dao.WorkStatus.Ctx(ctx).Where(dao.WorkStatus.Columns().FangzhenId, fangzhenID).
|
||
Where(dao.WorkStatus.Columns().Type, t).
|
||
Data(g.Map{
|
||
dao.WorkStatus.Columns().Finished: gdb.Raw(
|
||
"finished + 1",
|
||
),
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err)
|
||
}
|
||
|
||
// 根据 类型和方阵ID 获取对应的 work_id
|
||
dd := entity.WorkStatus{}
|
||
err = dao.WorkStatus.Ctx(ctx).Fields("work_id").Where(dao.WorkStatus.Columns().FangzhenId, fangzhenID).
|
||
Where(dao.WorkStatus.Columns().Type, t).Scan(&dd)
|
||
liberr.ErrIsNil(ctx, err)
|
||
|
||
work_id := dd.WorkId
|
||
err = UpdateWorkSchedule(work_id)
|
||
liberr.ErrIsNil(ctx, err)
|
||
})
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 处理匹配到的光伏板
|
||
func ProcessMatchedRectangles(matchedRectangles []int) error {
|
||
if len(matchedRectangles) == 0 {
|
||
return nil
|
||
}
|
||
|
||
return g.Try(context.Background(), func(ctx context.Context) {
|
||
timeNow := time.Now().Format("2006-01-02")
|
||
// 更新光伏板状态
|
||
_, err := dao.PvModule.Ctx(ctx).
|
||
WhereIn(dao.PvModule.Columns().Id, matchedRectangles).
|
||
Data(g.Map{
|
||
dao.PvModule.Columns().Status: 3,
|
||
dao.PvModule.Columns().DoneTime: timeNow,
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err, "更新光伏板状态失败")
|
||
|
||
// 获取所有的 work_id
|
||
pvModules := []entity.PvModule{}
|
||
err = dao.PvModule.Ctx(ctx).Fields("work_id").WhereIn("id", matchedRectangles).Scan(&pvModules)
|
||
liberr.ErrIsNil(ctx, err, "查询 pv_module 表失败")
|
||
|
||
// 提取所有的 work_id
|
||
workIds := lo.Map(pvModules, func(module entity.PvModule, _ int) string {
|
||
return module.WorkId
|
||
})
|
||
|
||
// 查询 work_status 表
|
||
var workStatus []entity.WorkStatus
|
||
err = dao.WorkStatus.Ctx(ctx).WhereIn(dao.WorkStatus.Columns().WorkId, workIds).Scan(&workStatus)
|
||
liberr.ErrIsNil(ctx, err, "查询 work_status 表失败")
|
||
|
||
statusMap := make(map[string]entity.WorkStatus)
|
||
for _, status := range workStatus {
|
||
statusMap[status.WorkId] = status
|
||
}
|
||
|
||
// 遍历 pv_module 表,根据 work_id 更新 work_status 表
|
||
for _, pvModule := range pvModules {
|
||
if _, ok := statusMap[pvModule.WorkId]; !ok {
|
||
continue
|
||
}
|
||
|
||
// 2. 更新计划表 work_schdule 表中计划
|
||
// timeNow := time.Now().Format("2006-01-02")
|
||
|
||
// 根据 work_id 查询所有的计划
|
||
var workSchedules []entity.WorkSchedule
|
||
err = dao.WorkSchedule.Ctx(ctx).Where(dao.WorkSchedule.Columns().WorkId, pvModule.WorkId).Scan(&workSchedules)
|
||
if len(workSchedules) == 0 {
|
||
liberr.ErrIsNil(ctx, errors.New("需要前往进度计划对应的方阵中,设置计划日期,随后再来实现进度复查功能!"))
|
||
return
|
||
}
|
||
liberr.ErrIsNil(ctx, err, "查询 work_schedule 表失败")
|
||
|
||
// 根据计划起始和结束时间,当天位于哪个计划中
|
||
for _, schedule := range workSchedules {
|
||
startAt := schedule.StartAt.Format("Y-m-d")
|
||
endAt := schedule.EndAt.Format("Y-m-d")
|
||
|
||
if timeNow >= startAt && timeNow <= endAt {
|
||
// 反序列化 Detail
|
||
var details []struct {
|
||
Date string `json:"date"`
|
||
PlanNum int `json:"planNum"`
|
||
FinishedNum int `json:"finishedNum"` // 手动填充的完成数量
|
||
AutoFill int `json:"autoFill"` // 是否自动填充
|
||
}
|
||
if err := json.Unmarshal([]byte(schedule.Detail), &details); err != nil {
|
||
continue
|
||
}
|
||
|
||
// 遍历 details,找到当天的计划
|
||
for i := range details {
|
||
if details[i].Date == timeNow {
|
||
details[i].AutoFill++ // 标记为 AI 识别
|
||
}
|
||
}
|
||
|
||
// 序列化 details
|
||
detailBytes, err := json.Marshal(details)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
// 更新 work_schedule 表
|
||
_, err = dao.WorkSchedule.Ctx(ctx).Where(dao.WorkSchedule.Columns().WorkId, schedule.WorkId).
|
||
Data(g.Map{
|
||
dao.WorkSchedule.Columns().Detail: string(detailBytes),
|
||
dao.WorkSchedule.Columns().FinishedNum: schedule.FinishedNum + 1,
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err, "更新 work_schedule 表失败")
|
||
|
||
var currentStatus entity.WorkStatus
|
||
err = dao.WorkStatus.Ctx(ctx).Where(dao.WorkStatus.Columns().WorkId, schedule.WorkId).Scan(¤tStatus)
|
||
liberr.ErrIsNil(ctx, err, "查询 work_status 表失败")
|
||
|
||
if currentStatus.Finished+1 <= currentStatus.Total {
|
||
finished := currentStatus.Finished + 1
|
||
|
||
_, err = dao.WorkStatus.Ctx(ctx).Where(dao.WorkStatus.Columns().WorkId, currentStatus.WorkId).
|
||
Where(dao.WorkStatus.Columns().Type, currentStatus.Type).
|
||
Data(g.Map{
|
||
dao.WorkStatus.Columns().Finished: finished,
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err, "更新 work_status 表失败")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 传入一个 workID 修改其对应的计划数据
|
||
func UpdateWorkSchedule(workID string) error {
|
||
return g.Try(context.Background(), func(ctx context.Context) {
|
||
// 查询 work_status 表
|
||
var workStatus []entity.WorkStatus
|
||
err := dao.WorkStatus.Ctx(ctx).Where(dao.WorkStatus.Columns().WorkId, workID).Scan(&workStatus)
|
||
liberr.ErrIsNil(ctx, err, "查询 work_status 表失败")
|
||
|
||
// 遍历 work_status 表,如果自增后的完成量小于等于总数,则将完成量加一
|
||
for _, status := range workStatus {
|
||
if status.Finished+1 <= status.Total {
|
||
finished := status.Finished + 1
|
||
|
||
_, err = dao.WorkStatus.Ctx(ctx).Where(dao.WorkStatus.Columns().WorkId, status.WorkId).
|
||
Data(g.Map{
|
||
dao.WorkStatus.Columns().Finished: finished,
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err, "更新 work_status 表失败")
|
||
}
|
||
|
||
// 2. 更新计划表 work_schdule 表中计划
|
||
timeNow := time.Now().Format("2006-01-02")
|
||
|
||
// 根据 work_id 查询所有的计划
|
||
var workSchedules []entity.WorkSchedule
|
||
err = dao.WorkSchedule.Ctx(ctx).Where(dao.WorkSchedule.Columns().WorkId, status.WorkId).Scan(&workSchedules)
|
||
liberr.ErrIsNil(ctx, err, "查询 work_schedule 表失败")
|
||
|
||
// 会出现多个计划的情况下,需要根据计划的起始和结束时间来确定具体的计划
|
||
for _, schedule := range workSchedules {
|
||
startAt := schedule.StartAt.Format("Y-m-d")
|
||
endAt := schedule.EndAt.Format("Y-m-d")
|
||
|
||
if timeNow >= startAt && timeNow <= endAt {
|
||
// 反序列化 Detail
|
||
var details []struct {
|
||
Date string `json:"date"`
|
||
PlanNum int `json:"planNum"`
|
||
FinishedNum int `json:"finishedNum"` // 手动填充的完成数量
|
||
AutoFill int `json:"autoFill"` // 是否自动填充
|
||
}
|
||
if err := json.Unmarshal([]byte(schedule.Detail), &details); err != nil {
|
||
continue
|
||
}
|
||
|
||
// 遍历 details,找到当天的计划
|
||
for i := range details {
|
||
if details[i].Date == timeNow {
|
||
// 为自动填充的数量加一
|
||
details[i].AutoFill++
|
||
}
|
||
}
|
||
|
||
// 序列化 details
|
||
detailBytes, err := json.Marshal(details)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
// 更新 work_schedule 表
|
||
_, err = dao.WorkSchedule.Ctx(ctx).Where(dao.WorkSchedule.Columns().WorkId, status.WorkId).
|
||
Where(dao.WorkSchedule.Columns().Id, schedule.Id).
|
||
Data(g.Map{
|
||
dao.WorkSchedule.Columns().Detail: string(detailBytes),
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err, "更新 work_schedule 表失败")
|
||
}
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// parseAIResult 解析识别的结果,提取出光伏板、支架和立柱的中心点
|
||
// 该函数解析的 JSON 格式如下:
|
||
//
|
||
// {
|
||
// "targets": [
|
||
// {
|
||
// "type": "pho", // 光伏板
|
||
// "size": [100, 100],
|
||
// "leftTopPoint": [10, 10]
|
||
// }
|
||
// ]
|
||
// }
|
||
func ParseAIResul2(fileContent string) *AIResult {
|
||
// 钻孔、桩基、支架、光伏板
|
||
var solarPanels, brackets, pillars, holes []XYPosition
|
||
|
||
gjson.Get(fileContent, "targets").ForEach(func(_, target gjson.Result) bool {
|
||
targetType := target.Get("type").String()
|
||
|
||
if targetType != solarPanel && targetType != bracket && targetType != pillar && targetType != drillingHole {
|
||
return true
|
||
}
|
||
|
||
size := lo.FilterMap(target.Get("size").Array(), func(size gjson.Result, _ int) (int, bool) {
|
||
return int(size.Int()), true
|
||
})
|
||
|
||
leftTopPoint := lo.FilterMap(target.Get("leftTopPoint").Array(), func(point gjson.Result, _ int) (int, bool) {
|
||
return int(point.Int()), true
|
||
})
|
||
|
||
// 获取中心点
|
||
centroidX := float64(leftTopPoint[0]) + float64(size[0])/2
|
||
centroidY := float64(leftTopPoint[1]) + float64(size[1])/2
|
||
position := XYPosition{X: centroidX, Y: centroidY}
|
||
|
||
switch targetType {
|
||
case solarPanel:
|
||
solarPanels = append(solarPanels, position)
|
||
case bracket:
|
||
brackets = append(brackets, position)
|
||
case pillar:
|
||
pillars = append(pillars, position)
|
||
case drillingHole:
|
||
holes = append(holes, position)
|
||
}
|
||
|
||
return true
|
||
})
|
||
|
||
marshal1, _ := json.Marshal(solarPanels)
|
||
marshal2, _ := json.Marshal(brackets)
|
||
marshal3, _ := json.Marshal(pillars)
|
||
marshal4, _ := json.Marshal(holes)
|
||
fmt.Println("光伏板的坐标点", string(marshal1))
|
||
fmt.Println("支架的坐标点", string(marshal2))
|
||
fmt.Println("立柱的坐标点", string(marshal3))
|
||
fmt.Println("钻孔的坐标点", string(marshal4))
|
||
|
||
return &AIResult{
|
||
SolarPanels: solarPanels,
|
||
Brackets: brackets,
|
||
Pillars: pillars,
|
||
Holes: holes,
|
||
}
|
||
}
|
||
|
||
// getSolarPanelCenters 获取数据库中光伏板的中心点
|
||
func getSolarPanelCenters(projectID string) ([]Point, error) {
|
||
// 获取所有的子项目
|
||
subProjectQuery := g.Model("sub_project").Fields("id").Where("project_id", projectID)
|
||
|
||
var pvModules []entity.PvModule
|
||
if err := dao.PvModule.Ctx(context.Background()).
|
||
WhereIn(dao.PvModule.Columns().SubProjectid, subProjectQuery).
|
||
Where(dao.PvModule.Columns().Type, 15).
|
||
WhereNot(dao.PvModule.Columns().Status, 2).
|
||
WhereNot(dao.PvModule.Columns().Status, 3).
|
||
Scan(&pvModules); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var points []Point
|
||
|
||
// tojson
|
||
dd, _ := json.Marshal(pvModules)
|
||
gjson.Parse(string(dd)).ForEach(func(_, record gjson.Result) bool {
|
||
record.Get("detail").ForEach(func(_, detail gjson.Result) bool {
|
||
id := record.Get("id").Int()
|
||
|
||
var longitudeAndLatitude []XYPosition
|
||
gjson.Get(detail.String(), "positions").ForEach(func(_, position gjson.Result) bool {
|
||
longitude := position.Get("lng").Float()
|
||
latitude := position.Get("lat").Float()
|
||
|
||
longitudeAndLatitude = append(longitudeAndLatitude, XYPosition{X: longitude, Y: latitude})
|
||
return true
|
||
})
|
||
|
||
points = append(points, Point{ID: int(id), Points: longitudeAndLatitude})
|
||
|
||
return true
|
||
})
|
||
|
||
return true
|
||
})
|
||
|
||
return points, nil
|
||
}
|
||
|
||
func getPillars(projectID string) map[string]map[string]GeoPoint {
|
||
ctx := context.Background()
|
||
|
||
// 获取所有的子项目
|
||
subProjectQuery := g.Model("sub_project").Fields("id").Where("project_id", projectID)
|
||
|
||
// 查询 qianqi_guangfuban_ids_zhuangdian 表
|
||
var zhuangdians []entity.QianqiGuangfubanIdsZhuangdian
|
||
if err := dao.QianqiGuangfubanIdsZhuangdian.Ctx(ctx).
|
||
Fields("id,fangzhen_id,detail,name").
|
||
WhereIn(dao.QianqiGuangfubanIdsZhuangdian.Columns().SubProjectid, subProjectQuery).
|
||
Scan(&zhuangdians); err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
// 第一层方阵ID, 第二层立柱名称, 第三层立柱信息
|
||
realGeoPoint := make(map[string]map[string]GeoPoint)
|
||
for _, zhuangdian := range zhuangdians {
|
||
fangzhenID := zhuangdian.FangzhenId
|
||
pillarName := zhuangdian.Name
|
||
|
||
var coordinates struct {
|
||
Position struct {
|
||
Lng float64 `json:"lng"`
|
||
Lat float64 `json:"lat"`
|
||
Alt float64 `json:"alt"`
|
||
}
|
||
}
|
||
if err := json.Unmarshal([]byte(zhuangdian.Detail), &coordinates); err != nil {
|
||
continue
|
||
}
|
||
|
||
if _, ok := realGeoPoint[fangzhenID]; !ok {
|
||
realGeoPoint[fangzhenID] = make(map[string]GeoPoint)
|
||
}
|
||
|
||
realGeoPoint[fangzhenID][pillarName] = GeoPoint{
|
||
ID: zhuangdian.Id,
|
||
Coordinates: Coordinates{
|
||
Lng: coordinates.Position.Lng,
|
||
Lat: coordinates.Position.Lat,
|
||
Alt: coordinates.Position.Alt,
|
||
},
|
||
}
|
||
}
|
||
|
||
return realGeoPoint
|
||
}
|
||
|
||
// UpdateTableData 更新表中的数据
|
||
func UpdateTableData(task_id int) error {
|
||
ctx := context.Background()
|
||
|
||
// 根据任务ID查询出所有的 PV Module ID
|
||
var pvlist []struct {
|
||
PvID int `orm:"pv_id"`
|
||
FangZhenID int `orm:"fangzhen_id"`
|
||
WorkID string `orm:"work_id"`
|
||
TypeID int `orm:"type"`
|
||
}
|
||
|
||
// 查询出基本信息
|
||
err := dao.PvModule.Ctx(ctx).As("pv").Fields("pv.id as pv_id", "pv.fangzhen_id", "pv.work_id", "pv.type").
|
||
LeftJoin("manage_task_result as mts", "pv.id = mts.pv_id").
|
||
Where("mts.task_id = ?", task_id).Scan(&pvlist)
|
||
if err != nil {
|
||
return fmt.Errorf("查询 PV Module 表失败: %w", err)
|
||
}
|
||
|
||
if len(pvlist) == 0 {
|
||
return nil
|
||
}
|
||
|
||
return g.Try(ctx, func(ctx context.Context) {
|
||
for _, v := range pvlist {
|
||
if v.TypeID == 15 { // 光伏板
|
||
if err := ProcessMatchedRectangles([]int{v.PvID}); err != nil {
|
||
return
|
||
}
|
||
continue
|
||
}
|
||
|
||
// 更新 PV Module 表
|
||
lastID, err := dao.PvModule.Ctx(ctx).Where(dao.PvModule.Columns().Id, v.PvID).
|
||
Where(dao.PvModule.Columns().FangzhenId, v.FangZhenID).
|
||
Where(dao.PvModule.Columns().Type, v.TypeID).
|
||
Data(g.Map{
|
||
dao.PvModule.Columns().Status: 2,
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err)
|
||
|
||
rows, err := lastID.RowsAffected()
|
||
liberr.ErrIsNil(ctx, err)
|
||
|
||
if rows > 0 {
|
||
// 更新 work_status 表
|
||
_, err := dao.WorkStatus.Ctx(ctx).Where(dao.WorkStatus.Columns().FangzhenId, v.FangZhenID).
|
||
Where(dao.WorkStatus.Columns().Type, v.TypeID).
|
||
Data(g.Map{
|
||
dao.WorkStatus.Columns().Finished: gdb.Raw(
|
||
"finished + 1",
|
||
),
|
||
}).Update()
|
||
liberr.ErrIsNil(ctx, err)
|
||
|
||
UpdateWorkSchedule(v.WorkID)
|
||
}
|
||
}
|
||
})
|
||
}
|