This commit is contained in:
che
2026-07-12 20:42:21 +05:00
parent b815cde04c
commit e3490e5d2e
23 changed files with 1568 additions and 538 deletions
+117 -20
View File
@@ -1,12 +1,14 @@
package analize
import (
"errors"
"fmt"
"math"
"net/http"
"path/filepath"
"strconv"
"strings"
"control/control_case"
"github.com/che4web/go4rest"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
@@ -32,10 +34,19 @@ type AnalizeSeriesPoint struct {
PsiMax float64 `json:"psi_max"`
}
type AnalizeSeriesGroup struct {
GroupValue float64 `json:"group_value"`
GroupLabel string `json:"group_label"`
Points []AnalizeSeriesPoint `json:"points"`
}
type AnalizeSeriesResponse struct {
Parameter string `json:"parameter"`
Label string `json:"label"`
Points []AnalizeSeriesPoint `json:"points"`
Parameter string `json:"parameter"`
Label string `json:"label"`
GroupParameter string `json:"group_parameter,omitempty"`
GroupLabel string `json:"group_label,omitempty"`
Points []AnalizeSeriesPoint `json:"points,omitempty"`
Groups []AnalizeSeriesGroup `json:"groups,omitempty"`
}
var analizeParameterColumns = map[string]struct {
@@ -45,7 +56,8 @@ var analizeParameterColumns = map[string]struct {
"Rel": {Column: "rel", Label: "Rel"},
"RelC": {Column: "rel_c", Label: "RelC"},
"Le": {Column: "le", Label: "Le"},
"Pr": {Column: "pr", Label: "Pr"},
"Pr": {Column: "sc", Label: "Sc"},
"Sc": {Column: "sc", Label: "Sc"},
"Pe": {Column: "pe", Label: "Pe"},
"Ma": {Column: "ma", Label: "Ma"},
"Time": {Column: "time", Label: "Time"},
@@ -59,6 +71,13 @@ func (c *AnalizeController) Series(ctx *gin.Context) {
return
}
groupParam := ctx.Query("group_parameter")
groupSelected, groupOK := analizeParameterColumns[groupParam]
if groupParam != "" && !groupOK {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid group parameter"})
return
}
filterParam := ctx.Query("filter_parameter")
filterSelected, filterOK := analizeParameterColumns[filterParam]
filterMin := ctx.Query("filter_min")
@@ -67,17 +86,19 @@ func (c *AnalizeController) Series(ctx *gin.Context) {
type row struct {
CaseID uint `gorm:"column:case_id"`
CaseName string `gorm:"column:case_name"`
GroupVal float64 `gorm:"column:group_val"`
X float64 `gorm:"column:x"`
Omega float64 `gorm:"column:omega"`
PsiMax float64 `gorm:"column:psi_max"`
}
groupExpr := groupExpr(groupParam, groupSelected.Column)
query := fmt.Sprintf(`
SELECT a.case_id, a.case_name, p.%s AS x, a.omega, a.psi_max
SELECT a.case_id, a.case_name, %s AS group_val, p.%s AS x, a.omega, a.psi_max
FROM analizes a
JOIN control_cases c ON c.id = a.case_id
JOIN params p ON p.id = c.params_id
`, selected.Column)
`, groupExpr, selected.Column)
where := make([]string, 0, 2)
args := make([]any, 0, 2)
if filterParam != "" {
@@ -97,7 +118,11 @@ func (c *AnalizeController) Series(ctx *gin.Context) {
if len(where) > 0 {
query += " WHERE " + strings.Join(where, " AND ")
}
query += fmt.Sprintf(" ORDER BY p.%s, a.id", selected.Column)
if groupParam != "" {
query += fmt.Sprintf(" ORDER BY ROUND(p.%s, 6), p.%s, a.id", groupSelected.Column, selected.Column)
} else {
query += fmt.Sprintf(" ORDER BY p.%s, a.id", selected.Column)
}
var rows []row
if err := c.db.Raw(query, args...).Scan(&rows).Error; err != nil {
@@ -106,23 +131,63 @@ func (c *AnalizeController) Series(ctx *gin.Context) {
}
points := make([]AnalizeSeriesPoint, 0, len(rows))
for _, r := range rows {
points = append(points, AnalizeSeriesPoint{
CaseID: r.CaseID,
CaseName: r.CaseName,
X: r.X,
Omega: r.Omega,
PsiMax: r.PsiMax,
})
groups := make([]AnalizeSeriesGroup, 0)
if groupParam == "" {
for _, r := range rows {
points = append(points, AnalizeSeriesPoint{
CaseID: r.CaseID,
CaseName: r.CaseName,
X: r.X,
Omega: r.Omega,
PsiMax: r.PsiMax,
})
}
} else {
groupIndex := map[string]int{}
for _, r := range rows {
groupValue := round6(r.GroupVal)
key := fmt.Sprintf("%.6f", groupValue)
idx, ok := groupIndex[key]
if !ok {
idx = len(groups)
groupIndex[key] = idx
groups = append(groups, AnalizeSeriesGroup{
GroupValue: groupValue,
GroupLabel: fmt.Sprintf("%s=%.6f", groupSelected.Label, groupValue),
Points: []AnalizeSeriesPoint{},
})
}
groups[idx].Points = append(groups[idx].Points, AnalizeSeriesPoint{
CaseID: r.CaseID,
CaseName: r.CaseName,
X: r.X,
Omega: r.Omega,
PsiMax: r.PsiMax,
})
}
}
ctx.JSON(http.StatusOK, AnalizeSeriesResponse{
Parameter: param,
Label: selected.Label,
Points: points,
Parameter: param,
Label: selected.Label,
GroupParameter: groupParam,
GroupLabel: groupSelected.Label,
Points: points,
Groups: groups,
})
}
func groupExpr(groupParam, groupColumn string) string {
if groupParam == "" {
return "0"
}
return fmt.Sprintf("ROUND(p.%s, 6)", groupColumn)
}
func round6(v float64) float64 {
return math.Round(v*1e6) / 1e6
}
func (c *AnalizeController) Recalculate(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
if err != nil {
@@ -136,7 +201,17 @@ func (c *AnalizeController) Recalculate(ctx *gin.Context) {
return
}
csvPath := filepath.Join(".", fmt.Sprintf("%d", item.CaseID), "foo.csv")
var controlCase control_case.ControlCase
if err := c.db.First(&controlCase, item.CaseID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
ctx.JSON(http.StatusNotFound, gin.H{"error": "control case not found"})
} else {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
return
}
csvPath := controlCase.FooCSVPath()
updated, err := AnalyzeCSV(c.db, item.CaseID, item.CaseName, csvPath)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
@@ -145,3 +220,25 @@ func (c *AnalizeController) Recalculate(ctx *gin.Context) {
ctx.JSON(http.StatusOK, updated)
}
func (c *AnalizeController) RecalculateControlCaseAnalysis(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
return
}
var controlCase control_case.ControlCase
if err := c.db.First(&controlCase, id).Error; err != nil {
ctx.JSON(http.StatusNotFound, gin.H{"error": "record not found"})
return
}
result, err := AnalyzeCSV(c.db, controlCase.ID, controlCase.Name, controlCase.FooCSVPath())
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, result)
}
+2
View File
@@ -13,4 +13,6 @@ func RegisterApp(r *gin.Engine, db *gorm.DB) {
go4rest.RegisterCRUDRoutes(r, "analize", controller)
r.GET("/api/analize/series", controller.Series)
r.POST("/api/analize/:id/recalculate", controller.Recalculate)
r.POST("/api/control_case/:id/recalculate-analysis", controller.RecalculateControlCaseAnalysis)
go RunAnalyzeWorker(db)
}
+28 -2
View File
@@ -8,11 +8,37 @@ import (
"os"
"strconv"
"strings"
"time"
"control/control_case"
"gonum.org/v1/gonum/dsp/fourier"
"gorm.io/gorm"
)
func RunAnalyzeWorker(db *gorm.DB) {
for {
var cases []control_case.ControlCase
if err := db.Where("status = ?", "R").Find(&cases).Error; err != nil {
fmt.Printf("failed to load cases for analysis: %v\n", err)
time.Sleep(10 * time.Second)
continue
}
for _, controlCase := range cases {
if _, err := AnalyzeCSV(db, controlCase.ID, controlCase.Name, controlCase.FooCSVPath()); err != nil {
fmt.Printf("failed to analyze case %d: %v\n", controlCase.ID, err)
continue
}
if err := db.Model(&control_case.ControlCase{}).Where("id = ?", controlCase.ID).Update("status", "D").Error; err != nil {
fmt.Printf("failed to mark case %d as done: %v\n", controlCase.ID, err)
}
}
time.Sleep(10 * time.Second)
}
}
func AnalyzeCSV(db *gorm.DB, caseID uint, caseName, csvPath string) (*Analize, error) {
file, err := os.Open(csvPath)
if err != nil {
@@ -136,8 +162,8 @@ func maxFloat(values []float64) float64 {
if len(values) == 0 {
return 0
}
max := values[0]
for _, v := range values[1:] {
max := values[len(values)-1]
for _, v := range values[len(values):] {
if v > max {
max = v
}