Skip to content

Commit

Permalink
Merge pull request #7 from UlfBj/master
Browse files Browse the repository at this point in the history
Simulator option added to feederv2 template.
  • Loading branch information
UlfBj authored Mar 19, 2024
2 parents 55aafb3 + 94a67ad commit b7d7ce8
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 3 deletions.
79 changes: 76 additions & 3 deletions feeder/feeder-template/feederv2/feederv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ type DomainData struct {
Value string
}

type DataItem struct {
Path string `json:"path"`
Dp []DpItem `json:"dp"`
}

type DpItem struct {
Ts string `json:"ts"`
Value string `json:"value"`
}

var tripData []DataItem
var simulatedSource string

type FeederMap struct {
MapIndex uint16
Name string
Expand Down Expand Up @@ -247,6 +260,7 @@ func initVehicleInterfaceMgr(fMap []FeederMap, inputChan chan DomainData, output
var simCtx simulateDataCtx
simCtx.RandomSim = true
simCtx.Fmap = fMap
dpIndex := 0
for {
select {
case outData := <-outputChan:
Expand All @@ -257,8 +271,17 @@ func initVehicleInterfaceMgr(fMap []FeederMap, inputChan chan DomainData, output
simCtx.Iteration = 0

default:
time.Sleep(3 * time.Second) // not to overload input channel
inputChan <- simulateInput(&simCtx) // simulating signals read from the vehicle interface
if simulatedSource == "internal" {
time.Sleep(3 * time.Second) // not to overload input channel
inputChan <- simulateInput(&simCtx) // simulating signals read from the vehicle interface
} else {
time.Sleep(1 * time.Second) // set to the tripdata "time base"
dataPoint := getSimulatedDataPoints(dpIndex)
for i := 0; i < len(dataPoint); i++ {
inputChan <- dataPoint[i]
}
dpIndex = incDpIndex(dpIndex)
}
}
}
}
Expand Down Expand Up @@ -308,6 +331,41 @@ func getRandomVssfMapIndex(fMap []FeederMap) int {
return signalIndex
}

func readSimulatedData(fname string) []DataItem {
if !fileExists(fname) {
utils.Error.Printf("readSimulatedData: The file %s does not exist.", fname)
return nil
}
data, err := os.ReadFile(fname)
if err != nil {
utils.Error.Printf("readSimulatedData:Error reading %s: %s", fname, err)
return nil
}
err = json.Unmarshal([]byte(data), &tripData)
if err != nil {
utils.Error.Printf("readSimulatedData:Error unmarshal json=%s", err)
return nil
}
return tripData
}

func getSimulatedDataPoints(dpIndex int) []DomainData {
dataPoint := make([]DomainData, len(tripData))
for i := 0 ; i < len(tripData); i++ {
dataPoint[i].Name = tripData[i].Path
dataPoint[i].Value = tripData[i].Dp[dpIndex].Value
}
return dataPoint
}

func incDpIndex(index int) int {
index++
if index == len(tripData[0].Dp) {
return 0
}
return index
}

func convertDomainData(north2SouthConv bool, inData DomainData, feederMap []FeederMap) DomainData {
var outData DomainData
matchIndex := sort.Search(len(feederMap), func(i int) bool { return feederMap[i].Name >= inData.Name })
Expand Down Expand Up @@ -395,6 +453,8 @@ func main() {
Required: false,
Help: "changes log output level",
Default: "info"})
simSource := parser.Selector("i", "simsource", []string{"vssjson", "internal"}, &argparse.Options{Required: false,
Help: "Simulator source must be either vssjson, or internal", Default: "internal"}) // "vehiclejson" could be added for non-converted simulator data
stateDB := parser.Selector("d", "statestorage", []string{"sqlite", "redis", "memcache", "none"}, &argparse.Options{Required: false,
Help: "Statestorage must be either sqlite, redis, memcache, or none", Default: "redis"})
dbFile := parser.String("f", "dbfile", &argparse.Options{
Expand All @@ -407,6 +467,7 @@ func main() {
utils.Error.Print(parser.Usage(err))
}
stateDbType = *stateDB
simulatedSource = *simSource

utils.InitLog("feeder-log.txt", "./logs", *logFile, *logLevel)

Expand Down Expand Up @@ -463,6 +524,13 @@ func main() {

utils.Info.Printf("Initializing the feeder for mapping file %s.", *mapFile)
feederMap := readFeederMap(*mapFile)
if simulatedSource != "internal" {
tripData = readSimulatedData("tripdata.json")
if len(tripData) == 0 {
utils.Error.Printf("Tripdata file not found.")
os.Exit(1)
}
}
scalingDataList = readscalingDataList(*sclDataFile)
go initVSSInterfaceMgr(vssInputChan, vssOutputChan)
go initVehicleInterfaceMgr(feederMap, vehicleInputChan, vehicleOutputChan)
Expand All @@ -472,7 +540,12 @@ func main() {
case vssInData := <-vssInputChan:
vehicleOutputChan <- convertDomainData(true, vssInData, feederMap)
case vehicleInData := <-vehicleInputChan:
vssOutputChan <- convertDomainData(false, vehicleInData, feederMap)
if simulatedSource != "vssjson" {
vssOutputChan <- convertDomainData(false, vehicleInData, feederMap)
} else {
//utils.Info.Printf("simulatedDataPoints:Path=%s, Value=%s", vehicleInData.Name, vehicleInData.Value)
vssOutputChan <- vehicleInData // conversion not needed
}
}
}
}
9 changes: 9 additions & 0 deletions feeder/feeder-template/feederv2/tripdata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[

{ "path": "Vehicle.TraveledDistance", "dp": [ { "ts": "2020-04-15T13:37:00Z", "value": "1000.0" }, { "ts": "2020-04-15T13:37:05Z", "value": "1000.0" }, { "ts": "2020-04-15T13:37:10Z", "value": "1001.0" }, { "ts": "2020-04-15T13:37:15Z ", "value": "1001.0" } ]},

{ "path": "Vehicle.CurrentLocation.Longitude", "dp": [ { "ts": "2020-04-15T13:37:00Z", "value": "56.02024719364729" }, { "ts": "2020-04-15T13:37:00Z", "value": "56.02233748493814" }, { "ts": "2020-04-15T13:37:00Z", "value": "56.02565421151008" }, { "ts": "2020-04-15T13:37:05Z", "value": "56.02823595803967" } ]},

{ "path": " Vehicle.CurrentLocation.Latitude " , "dp": [ { "ts": "2020-04-15T13:37:00Z", "value": "12.599927977070497" }, { "ts": "2020-04-15T13:37:00Z", "value": "12.601058355542794" }, { "ts": "2020-04-15T13:37:00Z", "value": "12.602554268256588" }, { "ts": "2020-04-15T13:37:05Z", "value": "12.603616368784676" } ]}

]
10 changes: 10 additions & 0 deletions tutorial/content/feeder/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,13 @@ where the Vehicle interface is implemented to connect to a RemotiveLabs broker.

There is also an External Vehicle Interface Client [EVIC](https://github.com/covesa/vissr/tree/master/feeder/feeder-evic)
feeder that enables the interface client to be implemented in a separate executable.

## Simulated vehicle data sources
The feederv2 template contains two different simulation mechanisms that are selected via the command line configuration parameters.

The one configured by "internal" uses the conversion instructions data as input for which signals to simulate, which are then randomly selected and set to random values.

The other configured by "vssjson" tries to read the file "tripdata.json", which must have a format as found in the example file. s seen in that file it contains an array of signal path names, and for each signal it contains an array of datapoints, i.e. timestamps and values. The data points are replayed at a constant frequency of 1 Hz. To change the frequency the time.Sleep input in the code must be changed and recompiled. This sets the rquirement on the data point arrays that their values must have been captured at this frequency, or recalculated to this frequency. Each data point array must have the same length. The simulator waps around and starts again from the beginning after reaching the end.

The signal names must be VSS paths as they are not processed by the conversion engine.
Extending the model to instead expect "vehicle domain signals" (like CAN signal data) should be a simple coding exercise for anyone preferring that.

0 comments on commit b7d7ce8

Please sign in to comment.