feat: expand vehicle data platform capabilities
This commit is contained in:
149
go/vehicle-gateway/internal/stats/hydrogen_capacity.go
Normal file
149
go/vehicle-gateway/internal/stats/hydrogen_capacity.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const HydrogenTankCapacityTableSQL = `CREATE TABLE IF NOT EXISTS vehicle_hydrogen_tank_capacity (
|
||||
vin VARCHAR(64) NOT NULL,
|
||||
source_vehicle_id BIGINT NOT NULL,
|
||||
source_model_id BIGINT NULL,
|
||||
plate_number VARCHAR(50) NOT NULL DEFAULT '',
|
||||
brand_name VARCHAR(500) NOT NULL DEFAULT '',
|
||||
model_name VARCHAR(500) NOT NULL DEFAULT '',
|
||||
tank_capacity_l DECIMAL(12,2) NOT NULL,
|
||||
active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
source_updated_at DATETIME NULL,
|
||||
synced_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (vin),
|
||||
KEY idx_hydrogen_capacity_model (source_model_id),
|
||||
KEY idx_hydrogen_capacity_active (active,vin)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`
|
||||
|
||||
type HydrogenCapacitySyncResult struct {
|
||||
Read int
|
||||
Written int
|
||||
Deactivated int64
|
||||
}
|
||||
|
||||
type hydrogenCapacityRow struct {
|
||||
VIN string
|
||||
SourceVehicleID int64
|
||||
SourceModelID sql.NullInt64
|
||||
Plate string
|
||||
Brand string
|
||||
Model string
|
||||
CapacityLiters float64
|
||||
SourceUpdatedAt sql.NullTime
|
||||
}
|
||||
|
||||
var mysqlIdentifier = regexp.MustCompile(`^[A-Za-z0-9_]+$`)
|
||||
|
||||
func SyncHydrogenTankCapacities(ctx context.Context, db *sql.DB, sourceSchema string) (HydrogenCapacitySyncResult, error) {
|
||||
result := HydrogenCapacitySyncResult{}
|
||||
if db == nil {
|
||||
return result, fmt.Errorf("mysql database is required")
|
||||
}
|
||||
sourceSchema = strings.TrimSpace(sourceSchema)
|
||||
if !mysqlIdentifier.MatchString(sourceSchema) {
|
||||
return result, fmt.Errorf("invalid asset schema %q", sourceSchema)
|
||||
}
|
||||
query := fmt.Sprintf(`SELECT vi.id,vi.vehicle_model_id,UPPER(TRIM(vi.vin)),COALESCE(vi.plate_number,''),
|
||||
COALESCE(vm.brand,''),COALESCE(vm.model,''),vm.tank_capacity,
|
||||
CASE
|
||||
WHEN vi.update_time IS NULL THEN vm.update_time
|
||||
WHEN vm.update_time IS NULL THEN vi.update_time
|
||||
ELSE GREATEST(vi.update_time,vm.update_time)
|
||||
END
|
||||
FROM %s.vehicle_info vi
|
||||
JOIN %s.vehicle_model vm ON vm.id=vi.vehicle_model_id
|
||||
WHERE COALESCE(vi.del_flag,'0')='0' AND COALESCE(vm.del_flag,'0')='0'
|
||||
AND LENGTH(TRIM(vi.vin))=17 AND vm.tank_capacity>0`, sourceSchema, sourceSchema)
|
||||
rows, err := db.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("read asset hydrogen capacities: %w", err)
|
||||
}
|
||||
byVIN := map[string]hydrogenCapacityRow{}
|
||||
for rows.Next() {
|
||||
var row hydrogenCapacityRow
|
||||
if err := rows.Scan(&row.SourceVehicleID, &row.SourceModelID, &row.VIN, &row.Plate, &row.Brand, &row.Model, &row.CapacityLiters, &row.SourceUpdatedAt); err != nil {
|
||||
rows.Close()
|
||||
return result, err
|
||||
}
|
||||
result.Read++
|
||||
if row.CapacityLiters <= 0 || row.CapacityLiters > 10000 {
|
||||
continue
|
||||
}
|
||||
if previous, exists := byVIN[row.VIN]; !exists || newerCapacityRow(row, previous) {
|
||||
byVIN[row.VIN] = row
|
||||
}
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return result, err
|
||||
}
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
_, err = tx.ExecContext(ctx, `UPDATE vehicle_hydrogen_tank_capacity SET active=0 WHERE active=1`)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
const upsert = `INSERT INTO vehicle_hydrogen_tank_capacity(
|
||||
vin,source_vehicle_id,source_model_id,plate_number,brand_name,model_name,tank_capacity_l,active,source_updated_at,synced_at
|
||||
) VALUES(?,?,?,?,?,?,?,1,?,NOW(3))
|
||||
ON DUPLICATE KEY UPDATE source_vehicle_id=VALUES(source_vehicle_id),source_model_id=VALUES(source_model_id),
|
||||
plate_number=VALUES(plate_number),brand_name=VALUES(brand_name),model_name=VALUES(model_name),
|
||||
tank_capacity_l=VALUES(tank_capacity_l),active=1,source_updated_at=VALUES(source_updated_at),synced_at=NOW(3)`
|
||||
for _, row := range byVIN {
|
||||
if _, err := tx.ExecContext(ctx, upsert, row.VIN, row.SourceVehicleID, row.SourceModelID, row.Plate, row.Brand, row.Model, row.CapacityLiters, row.SourceUpdatedAt); err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Written++
|
||||
}
|
||||
if err := tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM vehicle_hydrogen_tank_capacity WHERE active=0`).Scan(&result.Deactivated); err != nil {
|
||||
return result, err
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func newerCapacityRow(left, right hydrogenCapacityRow) bool {
|
||||
if left.SourceUpdatedAt.Valid != right.SourceUpdatedAt.Valid {
|
||||
return left.SourceUpdatedAt.Valid
|
||||
}
|
||||
if left.SourceUpdatedAt.Valid && !left.SourceUpdatedAt.Time.Equal(right.SourceUpdatedAt.Time) {
|
||||
return left.SourceUpdatedAt.Time.After(right.SourceUpdatedAt.Time)
|
||||
}
|
||||
return left.SourceVehicleID > right.SourceVehicleID
|
||||
}
|
||||
|
||||
func LoadHydrogenTankCapacities(ctx context.Context, query Queryer) (map[string]float64, error) {
|
||||
if query == nil {
|
||||
return nil, fmt.Errorf("mysql queryer is required")
|
||||
}
|
||||
rows, err := query.QueryContext(ctx, `SELECT UPPER(TRIM(vin)),tank_capacity_l FROM vehicle_hydrogen_tank_capacity WHERE active=1 AND tank_capacity_l>0`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
capacities := map[string]float64{}
|
||||
for rows.Next() {
|
||||
var vin string
|
||||
var capacity float64
|
||||
if err := rows.Scan(&vin, &capacity); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vin) == 17 && capacity > 0 && capacity <= 10000 {
|
||||
capacities[vin] = capacity
|
||||
}
|
||||
}
|
||||
return capacities, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user