R1 CAN Simulator

The R1 CAN Simulator can transmit simulated CAN signal values using a java panel or scripted input in a CSV format to an emulated Android device running on the same computer.

Page contents:

Overview
Download
Setup
Using the Simulator
Adjusting the CAN Panel Signals
Limitations
Additional Info

Overview

Your Android app can get the CAN values or listen for changes using the FCA Car Service CAN API.

The values for each CAN signal can be set using:

  1. An array of sliders
  2. A CSV file

Additional information relating to the CAN Simulator.


Download
Download Version Description
1.4 Updated to support CAE API v1.3.
1.3 Updated to allow CAN values to be input via a CSV and support R12.5 permissions.
1.2 Updated to support CAE API v1.2.


Setup

In order to connect to the simulator from your app you will need to import several modules and assets to your project. Follow the steps below:

  1. Download the CAN_Simulator.zip package.
  2. Open your Android Studio project and create an assets folder at “\app\src\main\assets”.
    1. Copy the three files from the “\Imports\Assets” folder of the CAN_Simulator package to the assets folder you created.
      1. KonaEmulator.properties
      2. Sensor_properties_global_20170127.json
      3. SensorContainer.properties



  1. Import each of the modules below from the “\Imports\Modules” folder of the CAN_Simulator package to your project by navigating to “File > New > Import Module”.
    1. android
    2. bluecove-2.1.0
    3. Emulator
    4. LoaderManagerImpl
    5. LoaderManagerInterfaces
    6. LoaderManagerStub
    7. SensorContainer
    8. SensorPropertiesParser
    9. Stringtree-json-2.0.9



Note: If your app is already configured to use the LoaderManager then it likely already has the LoaderManagerInterfaces and LoaderManagerStub modules, so you may not need to import them. You will need to import the LoaderManagerImpl module because it contains implementations of the managers that function with the simulator.

Note: During the module import process you might be warned that the module is missing dependencies. This is fine and will be resolved once all modules are imported.


  1. Launch your Android Virtual Device and click the settings cog. Navigate to “Settings > Proxy” and configure the proxy as seen below.
    1. Manual proxy configuration
      1. Host name: 10.0.2.2
      2. Port number: 2004



Note: It is recommended that you refer to the included “LoaderManagerSimulatorTestApp” to help understand the code for steps 5 – 8. The TestLoader.java Activity in the test app contains code that is cleanly broken into several button presses and onChange callbacks.


  1. Add the code to your app that is required to connect to the simulator.
    1. Add the code from the connectToSimulator method to your app so you can create the connection to the simulator. This code can go in a button press as shown in the example, or you could put it in the onCreate method of an Activity if you want it to run at launch.
      1. There is currently no way to disconnect/reconnect to the simulator in the same session. If you connect to the simulator, close the simulator, and want to reconnect, you will have to restart your app for this code to work again.
      2. This code will not work if the assets folder was not configured correctly in step 2.

/**
* Connect the emulated app to the simulator on the PC.
*
* @param view
*/
public void connectToSimulator(View view) {
    Properties propertiesKona = new Properties();
    AssetManager assetManager = this.getAssets();

    InputStream inputStream = null;
    try {
        inputStream = assetManager.open("KonaEmulator.properties");
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        propertiesKona.load(inputStream);
    } catch (IOException e) {
        e.printStackTrace();
    }

    Properties propertiesSensor = new Properties();
    assetManager = this.getAssets();
    inputStream = null;
    try {
        inputStream = assetManager.open("SensorContainer.properties");
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        propertiesSensor.load(inputStream);
    } catch (IOException e) {
        e.printStackTrace();
    }

    Properties merged = new Properties();
    merged.putAll(propertiesKona);
    merged.putAll(propertiesSensor);

    String sensorPropertyJson = null;
    try {
        InputStream is = getAssets().open("sensor_properties_global_20170127.json");
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();
        sensorPropertyJson = new String(buffer, "UTF-8");
    } catch (IOException ex) {
        ex.printStackTrace();
    }

    char[] charArray;
    charArray = sensorPropertyJson.toCharArray();

    EmulatorProperties.getInstance().setPropertiesFile(propertiesKona);
    ContainerProperties.getInstance().setPropertiesFile(propertiesSensor);
    SensorsObjectFactory.getInstance().setCharBuffer(charArray);
}
        

  1. Add code to load the managers you need, and implement their callbacks in the class.

public class TestLoader extends AppCompatActivity implements ILoader.ILoaderCallback, IVehicleHVACManager.FcaIVehicleHVACManagerCallback,
IVehicleStatusManager.FcaIVehicleStatusManagerCallback {
        

/**
* Load the managers containing the required LIDs.
*
* @param view
*/
public void loadManagers(View view) {
    final ManagerLoader managerLoader = new ManagerLoader();

    managerLoader.getManager(IVehicleHVACManager.class.getName(), this, getApplicationContext());
    managerLoader.getManager(IVehicleStatusManager.class.getName(), this, getApplicationContext());
}
        

  1. Add the get / register / unregister API calls that you require. In the test app, each of these actions is grouped to its own button press for simplicity.

/**
* Refresh values via get methods.
*
* @param view
*/
public void getValues(View view) {
    final ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(() -> {
        try {
            textViewBattery.setText("Battery: " + this.mVehicleStatusManager.getBattery());
            textViewSpeed.setText("Speedometer: " + this.mVehicleStatusManager.getSpeedometer());
            textViewTemp.setText("FT_DRV_ATC_TEMP: " + this.mVehicleHVACManager.getFT_DRV_ATC_TEMP());
        } catch (final PropertyNotSupported e) {
            System.out.println("Property not supported");
        }
    });
}

/**
* Register the listeners.
*
* @param view
*/
public void registerListeners(View view) {
    //Register VehicleHVACManager listeners
    if (IVehicleHVACManager.version.equalsIgnoreCase(this.mVehicleHVACManager.getManagerVersion())) {

        final ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(() -> {
            try {
                this.mVehicleHVACManager.registerFT_DRV_ATC_TEMPChange(this);
            } catch (final PropertyNotSupported e) {
                System.out.println("Failed to register listener for VehicleHVACManager. Property not supported.");
            }
        });

    } else {
        System.out.println("Manager version " + this.mVehicleHVACManager.getManagerVersion()
                + " and interface version " + IVehicleHVACManager.version + " are not the same");
    }

    //Register VehicleStatusManager listeners
    if (IVehicleStatusManager.version.equalsIgnoreCase(this.mVehicleStatusManager.getManagerVersion())) {

        final ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(() -> {
            try {
                this.mVehicleStatusManager.registerBatteryChange(this);
                this.mVehicleStatusManager.registerSpeedometerChange(this);
            } catch (final PropertyNotSupported e) {
                System.out.println("Failed to register listener for VehicleStatusManager. Property not supported.");
            }
        });

    } else {
        System.out.println("Manager version " + this.mVehicleStatusManager.getManagerVersion()
                + " and interface version " + IVehicleHVACManager.version + " are not the same");
    }
}

/**
* Unregisters the listeners.
*
* @param view
*/
public void unregisterListeners(View view) {
    this.mVehicleStatusManager.unregisterBatteryChange(this);
    this.mVehicleStatusManager.unregisterSpeedometerChange(this);
    this.mVehicleHVACManager.unregisterFT_DRV_ATC_TEMPChange(this);
}
        

  1. Add onManagerReady, onManagerFailure, and any relevant listener callbacks.

@Override
public void onManagerReady(final String managerName, final Object o) {
    if (managerName.equals(IVehicleHVACManager.class.getName())) {
        this.mVehicleHVACManager = (IVehicleHVACManager) o;
    } else if (managerName.equals(IVehicleStatusManager.class.getName())) {
        this.mVehicleStatusManager = (IVehicleStatusManager) o;
    }
}

@Override
public void onManagerFailure(final String managerName) {
    System.out.println("Manager failure: " + managerName);
}

@Override
public void onFT_DRV_ATC_TEMPChange(final int value) {
    textViewTemp.setText("FT_DRV_ATC_TEMP: " + value);
}

@Override
public void onSpeedometerChange(final float value) {
    textViewSpeed.setText("Speedometer: " + value);
}

@Override
public void onBatteryChange(final float value) {
    textViewBattery.setText("Battery: " + value);
}
        


Using the Simulator

  1. Open the “\CANSimulator\simulator” folder in the CAN_Simulator package and open a command prompt to that directory.
  2. Enter “java –jar CANSimulator.jar” into the command prompt for the CAN Panel or “java –jar CANSimulator.jar TestCANSimulation.csv” for the CSV input file.



  1. A CAN panel should now have appeared with the command prompt indicating that it is “Waiting for a connection from Xlet on port 2004…”



  1. At this point you are ready to call the simulation connection code in your app as detailed in step 5 of the Setup section. If you put the connection code in an onCreate method, now would be the time to launch the Activity where you included the code.
  2. Once the connection is made and the managers are loaded, you should now be able to drag the sliders around on the CAN panel for the LIDs you care about and fetch their values with get calls in your app, or see live updates in your app by registering a listener.


Adjusting the CAN Panel Signals

The signals displayed on the CAN panel are based on the sensor_properties_global_20170127.json file found in “\CANSimulator\simulator\properties” of the CAN_Simulator package. You can remove signals you don’t need from the file so it’s easier to find the signals you need on the panel. To remove a signal, remove its entry from the “sensor” array. The signal name is the same as the “componentname” for each sensor. For example, if you did not want to see the “A_EV_Miles” and “A_Hybrid_Miles” signals on the panel, you would delete the following code from the sensor_properties file.


{
    "type": "range",
    "componentname": "A_EV_Miles",
    "ppsSource": "can/xlet",
    "channelNos": "1",
    "ppsattributes": "A_EV_Miles",
    "dataTypes": "TYPE_DOUBLE",
    "minabsranges": "0",
    "maxabsranges": "9999.9",
    "resoluions": "0.1",
    "unit": "Mile"
},
{
    "type": "range",
    "componentname": "A_Hybrid_Miles",
    "ppsSource": "can/xlet",
    "channelNos": "1",
    "ppsattributes": "A_Hybrid_Miles",
    "dataTypes": "TYPE_DOUBLE",
    "minabsranges": "0",
    "maxabsranges": "9999.9",
    "resoluions": "0.1",
    "unit": "Mile"
},    
        

Once the file has been updated, save it and relaunch the simulator to see the changes take effect.



Limitations



Additional Info



Updated: 1/31/2020, Android is a trademark of Google LLC