EU Data Act: identify ID.3 cruising range by data point key (#30851)
This commit is contained in:
parent
3842c3da52
commit
bcd75510d0
3 changed files with 49 additions and 10 deletions
|
|
@ -128,3 +128,16 @@ func TestMergeDeliveryOrder(t *testing.T) {
|
|||
|
||||
assert.Equal(t, "75", data[FieldSoc].Value, "the newest delivered SoC wins")
|
||||
}
|
||||
|
||||
// TestPoints guards that a data point with a generic field name ("value") is
|
||||
// indexed by its unique key while the name stays indexed (and thus logged).
|
||||
func TestPoints(t *testing.T) {
|
||||
data := points([]dataPoint{
|
||||
{Key: KeyRangeID3, DataFieldName: "value", Value: "317"},
|
||||
{DataFieldName: FieldOdometer, Value: "22164"},
|
||||
})
|
||||
|
||||
assert.Equal(t, "317", data[KeyRangeID3].Value, "ID.3 range indexed by key")
|
||||
assert.Equal(t, "317", data["value"].Value, "field name remains indexed")
|
||||
assert.Equal(t, "22164", data[FieldOdometer].Value, "named field indexed by name")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ func (v *Provider) Range() (int64, error) {
|
|||
return 0, err
|
||||
}
|
||||
|
||||
if p := lookup(data, FieldRangeSecondary, FieldRangePrimary, FieldRangeCombined); p != nil {
|
||||
if p := lookup(data, FieldRangeSecondary, FieldRangePrimary, FieldRangeCombined, KeyRangeID3); p != nil {
|
||||
f, err := strconv.ParseFloat(p.Value, 64)
|
||||
return int64(f), err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,8 +81,10 @@ type dataset struct {
|
|||
CreatedOn time.Time `json:"createdOn"`
|
||||
}
|
||||
|
||||
// dataPoint is a single data point as delivered in the dataset JSON document
|
||||
// dataPoint is a single data point as delivered in the dataset JSON document.
|
||||
// Key is the data point's unique GUID, used when DataFieldName is generic (e.g. "value").
|
||||
type dataPoint struct {
|
||||
Key string `json:"key"`
|
||||
DataFieldName string `json:"dataFieldName"`
|
||||
Value string `json:"value"`
|
||||
TimestampUtc *time.Time `json:"timestampUtc"`
|
||||
|
|
@ -121,6 +123,7 @@ const (
|
|||
FieldRangeCombined = "cruising_range_combined"
|
||||
FieldRangePrimary = "cruising_range_primary_engine"
|
||||
FieldRangeSecondary = "cruising_range_secondary_engine"
|
||||
KeyRangeID3 = "0ca40e18-0564-3eda-bcc0-7aee9ef44f04" // VW ID.3 cruising range, delivered as "value"
|
||||
|
||||
// odo
|
||||
FieldOdometer = "mileage"
|
||||
|
|
@ -130,6 +133,12 @@ const (
|
|||
FieldRemainingTime = "remaining_charging_time"
|
||||
)
|
||||
|
||||
// knownKeys lists data point GUIDs that are indexed by their key instead of the
|
||||
// generic, non-unique DataFieldName they are delivered with
|
||||
var knownKeys = map[string]struct{}{
|
||||
KeyRangeID3: {},
|
||||
}
|
||||
|
||||
// contentDatasets returns the datasets that actually carry content, with their
|
||||
// delivery time parsed into Timestamp and sorted from oldest to newest. The
|
||||
// portal emits "..._no_content_found.zip" placeholders while the vehicle is
|
||||
|
|
@ -192,9 +201,26 @@ func parseDataset(log *log.Logger, b []byte) (map[string]point, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
res := make(map[string]point, len(ds.Data))
|
||||
for _, p := range ds.Data {
|
||||
if p.DataFieldName == "" || p.Value == "" {
|
||||
return points(ds.Data), nil
|
||||
}
|
||||
|
||||
// points indexes data points by field name (newest timestamp wins), and known
|
||||
// data points additionally by their unique key, as their name is not unique.
|
||||
func points(data []dataPoint) map[string]point {
|
||||
res := make(map[string]point, len(data))
|
||||
|
||||
set := func(name string, p point) {
|
||||
if name == "" {
|
||||
return
|
||||
}
|
||||
if cur, ok := res[name]; ok && cur.Timestamp.After(p.Timestamp) {
|
||||
return
|
||||
}
|
||||
res[name] = p
|
||||
}
|
||||
|
||||
for _, p := range data {
|
||||
if p.Value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -202,13 +228,13 @@ func parseDataset(log *log.Logger, b []byte) (map[string]point, error) {
|
|||
if p.TimestampUtc != nil {
|
||||
ts = *p.TimestampUtc
|
||||
}
|
||||
pt := point{Value: p.Value, Timestamp: ts}
|
||||
|
||||
if cur, ok := res[p.DataFieldName]; ok && cur.Timestamp.After(ts) {
|
||||
continue
|
||||
set(p.DataFieldName, pt)
|
||||
if _, ok := knownKeys[p.Key]; ok {
|
||||
set(p.Key, pt)
|
||||
}
|
||||
|
||||
res[p.DataFieldName] = point{Value: p.Value, Timestamp: ts}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue