feat: 推广 V3.5 实时氢耗并完善导出
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// mileage-correction writes a confirmed daily terminal odometer correction.
|
||||
// It is intentionally explicit: callers must provide the VIN, natural day and
|
||||
// confirmed cumulative mileage; it never derives values from a guess.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
|
||||
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/stats"
|
||||
)
|
||||
|
||||
func main() {
|
||||
vin := flag.String("vin", "", "VIN")
|
||||
date := flag.String("date", "", "natural day, yyyy-MM-dd")
|
||||
total := flag.Float64("total-km", 0, "confirmed cumulative mileage, km")
|
||||
flag.Parse()
|
||||
if strings.TrimSpace(*vin) == "" || strings.TrimSpace(*date) == "" || *total <= 0 {
|
||||
log.Fatal("vin, date and positive total-km are required")
|
||||
}
|
||||
dsn := strings.TrimSpace(os.Getenv("MYSQL_DSN"))
|
||||
if dsn == "" {
|
||||
log.Fatal("MYSQL_DSN is required")
|
||||
}
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
eventTime, err := time.ParseInLocation("2006-01-02 15:04:05", strings.TrimSpace(*date)+" 23:59:59", loc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
db, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
ctx := context.Background()
|
||||
if err := db.PingContext(ctx); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
candidate := stats.SourceMileageSample{
|
||||
VIN: strings.TrimSpace(*vin),
|
||||
StatDate: strings.TrimSpace(*date),
|
||||
Protocol: envelope.ProtocolGB32960,
|
||||
SourceKey: "GB32960:manual-correction@manual",
|
||||
SourceIP: "manual",
|
||||
SourceEndpoint: "manual-correction",
|
||||
FirstTotalKM: *total,
|
||||
LatestTotalKM: *total,
|
||||
DailyKM: 0,
|
||||
SampleCount: 1,
|
||||
FirstEventTime: eventTime,
|
||||
LatestEventTime: eventTime,
|
||||
QualityStatus: stats.QualityOK,
|
||||
QualityReason: "manual_correction_confirmed_total",
|
||||
}
|
||||
if err := stats.UpsertSourceMileage(ctx, db, candidate); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := stats.ProjectDailyMileage(ctx, db, candidate.VIN, candidate.StatDate, candidate.Protocol); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Keep both cumulative fields aligned. day_end_total_mileage_km is the API
|
||||
// field of record, while latest_total_mileage_km remains useful to internal
|
||||
// readers and must not retain the rejected reset value.
|
||||
if _, err := db.ExecContext(ctx, `
|
||||
UPDATE vehicle_daily_mileage
|
||||
SET latest_total_mileage_km=?, day_end_total_mileage_km=?
|
||||
WHERE vin=? AND stat_date=? AND protocol=?`,
|
||||
*total, *total, candidate.VIN, candidate.StatDate, string(candidate.Protocol)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var daily, latest, dayEnd float64
|
||||
if err := db.QueryRowContext(ctx, `SELECT daily_mileage_km,latest_total_mileage_km,day_end_total_mileage_km FROM vehicle_daily_mileage WHERE vin=? AND stat_date=? AND protocol=?`, candidate.VIN, candidate.StatDate, string(candidate.Protocol)).Scan(&daily, &latest, &dayEnd); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("corrected vin=%s date=%s protocol=%s daily_km=%.3f latest_total_km=%.3f day_end_total_km=%.3f\n", candidate.VIN, candidate.StatDate, candidate.Protocol, daily, latest, dayEnd)
|
||||
}
|
||||
@@ -82,6 +82,11 @@ func main() {
|
||||
} else {
|
||||
registry.SetGauge("vehicle_stat_hydrogen_capacity_cache_entries", nil, float64(loaded))
|
||||
}
|
||||
if loaded, err := writer.ReloadHydrogenRealtimeParameters(ctx); err != nil {
|
||||
logger.Warn("hydrogen V3.5 parameter cache load failed", "error", err)
|
||||
} else {
|
||||
registry.SetGauge("vehicle_stat_hydrogen_v35_parameter_cache_entries", nil, float64(loaded))
|
||||
}
|
||||
if cfg.NormalizePlatformSourcesOnStart {
|
||||
statDate := time.Now().In(cfg.Location).Format("2006-01-02")
|
||||
normalizeCtx, cancel := context.WithTimeout(ctx, cfg.NormalizePlatformSourcesTimeout)
|
||||
@@ -191,6 +196,13 @@ func syncHydrogenCapacities(ctx context.Context, logger interface {
|
||||
}
|
||||
registry.IncCounter("vehicle_stat_hydrogen_capacity_sync_total", metrics.Labels{"status": "ok"})
|
||||
registry.SetGauge("vehicle_stat_hydrogen_capacity_cache_entries", nil, float64(loaded))
|
||||
parameterCount, parameterErr := writer.ReloadHydrogenRealtimeParameters(syncCtx)
|
||||
if parameterErr != nil {
|
||||
registry.IncCounter("vehicle_stat_hydrogen_energy_parameter_sync_total", metrics.Labels{"status": "cache_error"})
|
||||
logger.Warn("hydrogen V3.5 parameter cache reload failed", "error", parameterErr)
|
||||
} else {
|
||||
registry.SetGauge("vehicle_stat_hydrogen_v35_parameter_cache_entries", nil, float64(parameterCount))
|
||||
}
|
||||
metrics.RecordLastActivity(registry, "vehicle_stat_hydrogen_capacity_last_sync_unix_seconds", nil)
|
||||
logger.Info("hydrogen tank capacities synchronized", "source_schema", cfg.HydrogenCapacitySourceSchema, "read", result.Read, "written", result.Written, "deactivated", result.Deactivated, "cache_entries", loaded)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user