初始
This commit is contained in:
88
third/solaranalyzer/analyzer.go
Normal file
88
third/solaranalyzer/analyzer.go
Normal file
@ -0,0 +1,88 @@
|
||||
package solaranalyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
const (
|
||||
drillingHole = "hole" // 钻孔
|
||||
pillar = "pile" // 桩基
|
||||
bracket = "shelves" // 支架
|
||||
solarPanel = "pho" // 光伏板
|
||||
)
|
||||
|
||||
type Point struct {
|
||||
Points []XYPosition // 光伏板的坐标点
|
||||
ID int // pv_module 表中的自增ID
|
||||
}
|
||||
|
||||
type XYPosition struct {
|
||||
X float64 `json:"x"`
|
||||
Y float64 `json:"y"`
|
||||
}
|
||||
|
||||
// getSolarPanelRanges 解析光伏板 JSON
|
||||
func getSolarPanelRanges(file string) []Point {
|
||||
var points []Point
|
||||
|
||||
gjson.Parse(file).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
|
||||
}
|
||||
|
||||
// convertToLongitudeAndLatitude 根据 tif 文件将坐标转换为经纬度
|
||||
func convertToLongitudeAndLatitude(tifFilePath string, coordinatePoints []XYPosition) []XYPosition {
|
||||
positionsJSON, err := json.Marshal(coordinatePoints)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
convertPath, err := filepath.Abs("./convert")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
output, err := exec.Command(convertPath, filepath.Clean(tifFilePath), string(positionsJSON)).CombinedOutput()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outputLines := strings.Split(string(output), "\n")
|
||||
lastLine := outputLines[len(outputLines)-2] // 获取最后一行
|
||||
|
||||
// 解析二进制返回的 JSON, 并将其转换为 XYPosition 结构
|
||||
var longitudeAndLatitude []XYPosition
|
||||
gjson.Get(lastLine, "data").ForEach(func(_, position gjson.Result) bool {
|
||||
longitudeAndLatitude = append(longitudeAndLatitude, XYPosition{
|
||||
X: position.Get("y").Float(),
|
||||
Y: position.Get("x").Float(),
|
||||
})
|
||||
return true
|
||||
})
|
||||
|
||||
return longitudeAndLatitude
|
||||
}
|
57
third/solaranalyzer/bracket.go
Normal file
57
third/solaranalyzer/bracket.go
Normal file
@ -0,0 +1,57 @@
|
||||
package solaranalyzer
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/tomchavakis/geojson/geometry"
|
||||
)
|
||||
|
||||
const (
|
||||
earthRadius = 6371000 // 地球半径(米)
|
||||
|
||||
radiusMeters = 0.07 // 半径(7厘米)
|
||||
|
||||
numPoints = 8 // 点的数量
|
||||
)
|
||||
|
||||
type GeoPoint struct {
|
||||
ID int
|
||||
Coordinates Coordinates
|
||||
}
|
||||
|
||||
type Coordinates struct {
|
||||
Lng float64
|
||||
Lat float64
|
||||
Alt float64
|
||||
}
|
||||
|
||||
// ExpandToCircle 用于桩点和支架
|
||||
// 以一个点为中心,扩展出一个圆形
|
||||
func (p Coordinates) ExpandToCircle() geometry.Polygon {
|
||||
circle := make([]Coordinates, numPoints)
|
||||
|
||||
for i := 0; i < numPoints; i++ {
|
||||
angle := 2 * math.Pi * float64(i) / float64(numPoints)
|
||||
|
||||
// 计算偏移量(米)
|
||||
dx := radiusMeters * math.Cos(angle)
|
||||
dy := radiusMeters * math.Sin(angle)
|
||||
|
||||
// 将米转换为经纬度
|
||||
dLng := dx / (earthRadius * math.Cos(p.Lat*math.Pi/180)) * 180 / math.Pi
|
||||
dLat := dy / earthRadius * 180 / math.Pi
|
||||
|
||||
circle[i] = Coordinates{
|
||||
Lng: p.Lng + dLng,
|
||||
Lat: p.Lat + dLat,
|
||||
Alt: p.Alt,
|
||||
}
|
||||
}
|
||||
|
||||
var coords []geometry.Point
|
||||
for _, point := range circle {
|
||||
coords = append(coords, geometry.Point{Lat: point.Lat, Lng: point.Lng})
|
||||
}
|
||||
|
||||
return geometry.Polygon{Coordinates: []geometry.LineString{{Coordinates: coords}}}
|
||||
}
|
695
third/solaranalyzer/sparta.go
Normal file
695
third/solaranalyzer/sparta.go
Normal file
@ -0,0 +1,695 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user