Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulator option added to feederv2 template. #7

Merged
merged 3 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.