diff --git a/go.mod b/go.mod index 2ca5406b..c9887dbb 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,6 @@ require ( github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v28.2.2+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect diff --git a/go.sum b/go.sum index 088b13b9..b6d2b406 100644 --- a/go.sum +++ b/go.sum @@ -42,10 +42,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= diff --git a/pkg/migrations/00004_add_value_location.go b/pkg/migrations/00004_add_value_location.go new file mode 100644 index 00000000..fd1b79cd --- /dev/null +++ b/pkg/migrations/00004_add_value_location.go @@ -0,0 +1,45 @@ +package migrations + +import ( + "context" + "database/sql" + "runtime" + + "github.com/pressly/goose/v3" +) + +func init() { + _, filename, _, _ := runtime.Caller(0) + registerFunc := func() { + goose.AddNamedMigrationContext(filename, upAddValueLocation, downAddValueLocation) + } + registerFuncs = append(registerFuncs, registerFunc) +} + +func upAddValueLocation(ctx context.Context, tx *sql.Tx) error { + // This code is executed when the migration is applied. + upStatements := []string{ + "ALTER TABLE signal ADD COLUMN value_location Tuple(latitude Float64, longitude Float64, hdop Float64) COMMENT 'Location value of the signal collected.'", + } + for _, upStatement := range upStatements { + _, err := tx.ExecContext(ctx, upStatement) + if err != nil { + return err + } + } + return nil +} + +func downAddValueLocation(ctx context.Context, tx *sql.Tx) error { + // This code is executed when the migration is rolled back. + downStatements := []string{ + "ALTER TABLE signal DROP COLUMN value_location", + } + for _, downStatement := range downStatements { + _, err := tx.ExecContext(ctx, downStatement) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/migrations/migrations_test.go b/pkg/migrations/migrations_test.go index f43fe8d1..bf2b4886 100644 --- a/pkg/migrations/migrations_test.go +++ b/pkg/migrations/migrations_test.go @@ -44,6 +44,7 @@ func TestSignalMigration(t *testing.T) { {Name: vss.CloudEventIDCol, Type: "String", Comment: "Id of the Cloud Event that this signal was extracted from."}, {Name: vss.ValueNumberCol, Type: "Float64", Comment: "float64 value of the signal collected."}, {Name: vss.ValueStringCol, Type: "String", Comment: "string value of the signal collected."}, + {Name: vss.ValueLocationCol, Type: "Tuple(latitude Float64, longitude Float64, hdop Float64)", Comment: "Location value of the signal collected."}, } // Check if the actual columns match the expected columns diff --git a/pkg/tesla/telemetry/convert_test.go b/pkg/tesla/telemetry/convert_test.go index b3256cca..ed899071 100644 --- a/pkg/tesla/telemetry/convert_test.go +++ b/pkg/tesla/telemetry/convert_test.go @@ -1,6 +1,7 @@ package telemetry import ( + "math" "testing" "time" @@ -131,6 +132,7 @@ func TestConvertTyped(t *testing.T) { } expectedSignals := []vss.Signal{ + {TokenID: 7, Timestamp: ts, Name: "currentLocation", ValueLocation: vss.Location{Latitude: 30.267222, Longitude: -97.743056, HDOP: math.NaN()}, Source: teslaConnection}, {TokenID: 7, Timestamp: ts, Name: "currentLocationLatitude", ValueNumber: 30.267222, Source: teslaConnection}, {TokenID: 7, Timestamp: ts, Name: "currentLocationLongitude", ValueNumber: -97.743056, Source: teslaConnection}, {TokenID: 7, Timestamp: ts, Name: "powertrainTractionBatteryCurrentPower", ValueNumber: 5700.000084936619, Source: teslaConnection}, diff --git a/pkg/tesla/telemetry/outer_convert_funcs_gen.go b/pkg/tesla/telemetry/outer_convert_funcs_gen.go index f4f51541..6d155101 100644 --- a/pkg/tesla/telemetry/outer_convert_funcs_gen.go +++ b/pkg/tesla/telemetry/outer_convert_funcs_gen.go @@ -3,6 +3,7 @@ package telemetry import ( "fmt" + "math" "github.com/DIMO-Network/model-garage/pkg/tesla/telemetry/parse" "github.com/DIMO-Network/model-garage/pkg/tesla/telemetry/unit" @@ -53,6 +54,18 @@ func ProcessPayload(payload *protos.Payload, tokenID uint32, source string) ([]v sig.SetValue(res) out = append(out, sig) } + sig := vss.Signal{ + TokenID: tokenID, + Name: "currentLocation", + Timestamp: ts, + Source: source, + } + sig.SetValue(vss.Location{ + Latitude: tvf.Latitude, + Longitude: tvf.Longitude, + HDOP: math.NaN(), + }) + out = append(out, sig) case protos.Field_DetailedChargeState: var tvf protos.DetailedChargeStateValue switch tv := d.GetValue().Value.(type) { diff --git a/pkg/vss/signal.go b/pkg/vss/signal.go index ae8bde06..a0e90dd9 100644 --- a/pkg/vss/signal.go +++ b/pkg/vss/signal.go @@ -25,6 +25,8 @@ const ( ValueNumberCol = "value_number" // ValueStringCol is the name of the value_string column in Clickhouse. ValueStringCol = "value_string" + // ValueLocationCol is the name of the value_location column in Clickhouse. + ValueLocationCol = "value_location" ) // Signal represents a single signal collected from a device. @@ -45,6 +47,9 @@ type Signal struct { // ValueString is the value of the signal collected. ValueString string `ch:"value_string" json:"valueString"` + // ValueLocation is the value of the signal collected. + ValueLocation Location `ch:"value_location" json:"valueLocation"` + // Source is the source of the signal collected. Source string `ch:"source" json:"source"` @@ -55,6 +60,14 @@ type Signal struct { CloudEventID string `ch:"cloud_event_id" json:"cloudEventId"` } +// Location represents a point on the earth in WSG-84 coordinates, +// optionally with a Horizontal Dilution of Position (HDOP) value. +type Location struct { + Latitude float64 `ch:"latitude"` + Longitude float64 `ch:"longitude"` + HDOP float64 `ch:"hdop"` +} + // SetValue dynamically set the appropriate value field based on the type of the value. func (s *Signal) SetValue(val any) { switch typedVal := val.(type) { @@ -62,6 +75,8 @@ func (s *Signal) SetValue(val any) { s.ValueNumber = typedVal case string: s.ValueString = typedVal + case Location: + s.ValueLocation = typedVal default: s.ValueString = fmt.Sprintf("%v", val) } @@ -79,6 +94,7 @@ func SignalToSlice(obj Signal) []any { obj.CloudEventID, obj.ValueNumber, obj.ValueString, + obj.ValueLocation, } } @@ -93,5 +109,6 @@ func SignalColNames() []string { CloudEventIDCol, ValueNumberCol, ValueStringCol, + ValueLocationCol, } }