This is the developer journey for the playground. Here, you will learn how to create a basic prototype, how to customize it, and how to create re-usable widgets - including new UX elements and new data sources.

For a general introduction of, please click here. For the documentation of the key architectural concepts of the playground, please click architectural here

Creating a simple prototype with re-usable widgets

Creating a new Prototype

Model selection screen

Developing your own Prototype starts with selecting a Model you want to develop the prototype for. Note that you can only develop Prototypes for Models which you were granted Contributor rights.

Prototype Catalogue 2.png

If you have the appropriate rights the "New Prototype" button will be available in the Prototype catalogue of the Model.

Create Prototype dialogue.png

Klicking on this button will spawn a dialogue that enables a developer to enter a name and some basic info for the Customer Journey the name field is required but the rest can be left blank.

New Prototype in List 2.png

Your new Prototype will now appear in the overview. If the list is very large you can also enter the name of your Prototype in the search bar on top of the list.

The Journey Tab

The "Journey" Tab of the Prototype contains the projected Customer Journey as well as some meta information. Here you can also Prototype is administrated . Here you can also rename the Prototype, add an image to represent the Prototype and delete the Prototype (if you have the rights to do so).

New Prototype Journey marked.png

Editable items in the Journey will have have a button with a pencil symbol for authorized Users (marked in red in the image above).

With the top left edit button you can add switch the image of the Prototype. Klicking on that button will open a menu to select an image from available media. please refer to the section Adding Media for more details.

With the top right button you can change the name of the prototype and the basic info like stakeholders and problems the Prototype is supposed to solve.

Below those parameters you find the parameter "Tags". These are used to filter Prototypes and are also displayed on the list of Prototypes of a Model. You can remove Tags by clicking on the 'x' next to the Tag and add a Tag by clicking on "Add New" and selecting the Tag category and then the Tag in the two consecutively appearing dropdowns and then clicking the checkmark to the right to confirm your selection.

Editing the Customer Journey

The customer Journey is a table intended to describe the customers journey in steps(column headings) with custom parameters in rows. The columns and row headings and their number can be configured freely.

Editing customer journey 2.png

When clicking on the "Edit" button next to the Customer Journey you first start out with a table with three columns and three rows.

Editing Column header name.png

clicking on any row or column header text makes that text editable. Same goes for table cells.

Add Row Button.pngAdd column button.png

The "Add Row" and "Add Column" buttons let you add rows or columns respectively.

Delete column.pngDelete row button.png

You can delete entire rows or columns if you don't need them by hovering over the respective row or column header and clicking the red "delete" button that appears.

Resize cell.png

If you want to change the height of a row you can by dragging the two lines in the bottom right corner of each cell of that row.

The Code Tab

This is the area where you will actually work on the prototype itself. It is split into three sections:

The code editor:

The Code Editor.png

The left section is the code editor. This is where you write the actual code to the prototype. Prototype code is written in Python. The code will be compiled and run locally in your browser. so executions are independent from one another. Everyone who has access to the Prototype can run this code and (locally) modify it. If you have the approrpiate rights as a contributor for the Prototype then your changes are automatically saved. In order to run the code you have to switch to the the Dashboard. You can write regular Python code here but bear in mind that some io functions and some other basic things might not be supported. More details on Prototype code development in the following section Working with Prototype code

The API catalogue:

The API Catalogoue.png

In the top right section you find the API catalogue. This is a small version of the API catalogue of the current Model. Here you can look up API nodes you might want to use. It features a search and filter function and clicking on a node will copy it to your clipboard. Clicking on the "Used APIs" Tab will show you which API nodes are already in use in your code.

The dashboard configuration: Clicking on the third tab above the API catalogue will reveal the dashboard configuration. This is used to place Widgets on the dashboard.

		"boxes": [1],
		"plugin": <plugin 1 name>,
		"widget": <plugin 1 widget 1 name>
		"boxes": [3,4],
		"plugin": <plugin 1 name>,
		"widget": <plugin 1 widget 2 name>
		"boxes": [5,10],
		"plugin": <plugin 2 name>,
		"widget": <plugin 2 widget 1 name>

The code above is a template to create a valid Dashboard configuration. The config is a JSON list of objects each defining the placement of a certain widget. Since Widgets are bound to Plugins you have to name the Plugin as it is named in the Plugin list and the name of the Widget as defined inside that Plugin. The names must be written in quotes without the angle brackets. The "boxes" parameter defines which box(es) the Widget will occupy on the gridview of the Dashboard. It is a comma separated list. You can use any boxes in combination or alone as long as they form a rectangular shape.

Working with Prototype code

As stated above you can technically write any Python code with limitations. A lot of the ressources a Python implementation expects are emulated but not everything might work. Also right now there is no way to import external libraries. This is due to how the code is handled. A complete list of supported packages as well as packages specific to browser based Python can be found here.

When developing your Prototype you mostly interact with two things: The VSS API and the Plugins.

Access to VSS APIs: This is achieved via the Vehicle class of the Playground native sdv_model package.

from sdv_model import Vehicle
vehicle = Vehicle()

The code above demonstrates the Vehicle class being imported and instantiated. A newly created Prototype will conveniently start out with this in the code. Here and in further examples we are using lowercase vehicle as a variable name but you can use any name you like. Access to API nodes works like accessing an object. You can read values from API nodes for sensors:

await vehicle.Acceleration.Longitudinal.get()

You can set values to API nodes for actuators: Note: Values can be integers, decimal numbers, booleans or attributes from VSS depending on the actuator.

await vehicle.Body.Mirrors.Right.Tilt.set(45)

You can subscribe to value changes:

await vehicle.Speed.subscribe(run_simulation)

Please note that all API nodes must be called with 'await' as they are asynchronous and return a promise. Please also note that the clipboard function of the small VSS catalogue writes 'Vehicle' with an uppercase 'V' so if you are working with the instance like we do in the example you will have to change it to a lowercase one. The Playground provides a barbones simulation meaning that .get() calls will always return a default value, .set() calls will accept values and .subscribe() calls will call the callback. There is however no effect on any background process and values of different API nodes don't corelate. This is just to have callable working API nodes for basic testing of your Prototype. For an actual simulation of any kind you will need to use Plugins

Interacting with Plugins directly: There are some situations where you'd might want to call functions of a Plugin directly. Since Plugins control Widgets the most likely Scenario is that you have a Widget you want to directly manipulate from your Prototypes code. For this you need the built package 'plugins':

import plugins

With this imported you can get access to any Plugin by name and call any functions it makes available (A newly created Prototype conveniently starts out with this already in the code):

kinetosis_plugin = plugins.get_plugin("Anti-Kinetosis")

Creating and using Plugins

Plugins are bound to a Model so any Prototype that belongs to the same Model will have access to the same Plugins. With enough planning Plugins and their Widgets can be made highly reusable. But since their simulations often are very specific to a certain Prototype it is very likely you will have to write your own. Regardless of that for any Prototype all available Plugins will always be loaded and made available.

Adding a new Plugin

Selecting Plugins Section.png

When you have opened a Model or Prototype the hamburger menu in the navigation bar will have an entry Plugins. Clicking on that will lead to the list of available Plugins to that Model.

Create Plugin 2.png

In the following screen you can modify existing Plugins. At the top of the list you find the option "New Plugin". If you click on that a panel will open. You have to give a Name for your plugin. This will be the reference in the code. Note: You can only choose names without whitespaces. The description is optional. The "JS Code" field must be filled with a link to the Plugin. There are multiple options how to create and host a Plugin.

Hosting a Plugin

The requirements for hosted Plugins are that the Playground needs direct access to the raw file via URL. Also the hosting MUST be https. There are a few options for this we recommend:

Option1: Using our code editor:

Plugin Editor .png leads to the Plugin Editor. Here you can upload new media files to use in your Project as well as write and host code. Specifically for custom Plugins.

New Cool Plugin.png

On The Landing Page of the Editor you can click "Create New Plugin", give your new Plugin a name in the Editor view that just opened and click on "save". After That a "Deploy url" will appear that you can copy and insert into the "JS Code" field when adding a new Plugin on the Playground. There is also a shortcut: Below the "JS Code" field is the option "Create JS code file using our editor". Clicking this will fill the JS Code Field with a generated URL after which the option changes to: "Open JS code editor"

Open JS code Editor.png

You can now click the "create" Button to register your new Plugin and make it available to Prototypes. Clicking the option "Open JS code editor" will lead you to the Plugin Editor with your newly created Plugin already open.

Any changes you make in the Plugin Editor will immediately take effect after saving. If you are already working with the Plugin in a Prototype you might have to reload the Prototype for the changes to take effect.

Option2: Hosting your Plugin locally for faster Plugin testing:

In this example we are using the Live Server plugin for VSCode but many IDEs support similar Plugins and you can of course also just use any local Webserver you like.

  1. Create a new project with VS Code
  2. Inside VS Code, install the Live Server Plugin
  3. Write a <plugin_name>.js file
  4. Start Live Server plugin, you will get a URL like: http://localhost:5500/<plugin_name>.js
  5. Go to Playground, add a plugin with the above URL
  6. Test your Plugin in a Prototype
  7. If the plugin is not working correctly, edit the file in Vscode, save, and reload the Page. Note: You will have to reload the entire page, switching between tabs on the Playground doesn't reload Plugins.

Option3: Hosting your Plugin via Github:

Hosting a Plugin via Github.png

Working with Plugin code

const plugin = ({widgets, simulator, vehicle}) => {
  //Your Plugin code here

The example above shows the bare minimum any Plugin needs to contain. A Plugin is a function that gets passed three parameters by the Playground: 'widgets', 'simulator', 'vehicle'.

Registering Widgets: The 'widget' object provides a 'register' function that requires a Widget name by which the Widget will be referred in the Playground and a function that accepts a "box" parameter with an 'inject' method. That Box is the outline we define with the 'box' Parameter in the Dashboard Configuration.

widgets.register(<widget name>, widgetActivationFunction)

function widgetActivationFunction (box) {
  box.injecNode(<html code>);

The example above shows the basics needed to register a Widget. Widgets are just HTML Code that can be modified by and interact with the Plugin code with all the possibilities of the DOM environment. Registering a Widget means making it available to be used on the Dashboard. The widget object provides a "register" method that expects two parameters: The name of the widget as a string and "Widget activation function". The Widget activation function accepts a box parameter. The box is an area of variable size the Widget will reside in. the box is what we define with the "boxes" parameter in the Dashboard configuration. Inside the Widget activation function we use the "injectNode" method of the box to add HTML to that box. The "injectNode" method expects a string containing HTML as a parameter. This can be any valid HTML code. Obviously the possibilities here are "endless" but of course we don't want to always build everything from scratch, especially for simple visualizations.

That is why the Playground has a set of Reusables these are templates that can be imported to the Plugin and then used to build your own Widget on top. The reason they can't just be used out of the box is that we need the Plugin to connect them to whichever value or process in the simulation we want to visualize. A Reusable is a function that accepts different parameters for its configuration and returns a Widget activation function to be passed to the register function of the widget object. A list of available Reusables and how they are used can be found here

import SignalPills from "https://playground-plugins.netlify.appU/reusable/SignalPills.js"

const plugin = ({widgets, simulator, vehicle}) => {
  widgets.register("testwidget", SignalPills([{signal: "Vehicle.Body.Windshield.Rear.Wiping.Intensity"}], vehicle))

export default plugin;

The example above is using the 'SignalPills' Reusable. We are still using the 'widgets.register()' function and passing our own Widget name but in place of the Widget activation function we call the Reusable with some parameters which returns its own Widget activation function. The 'SignalPills' Reusable requires a list of JSON Objects, that is because it can display up to three values. Each Object defines a "signal" or API node whose value it will show. The other Parameters in the object are for styling and metainformation (please refer to the guide on Reusables mentioned above). As a last parameter the Reusable requires the 'vehicle' object passed to the Plugin function.

And this is how the SignalPills Widget would look like in our example:

SignalPills on Dashboard.png

Accessing VSS Values inside a Plugin: In the section before we built our own basic Plugin and we tested a Reusable that was able to display the value of an API node. Now what if we want to do that in our own Widget? This is where the 'vehicle' object passed to the Plugn function comes in and this is also why the Reusable required it. The 'vehicle' object makes values of API (especially those modified by a Prototype) available to the Plugin. Access works as follows:

const value = await cehicle["Body.Windshield.Rear.Wiping.Intensity"].get()

The example above loads the current value of "vehicle.Body.Windshield.Rear.Wiping.Intensity" into a variable. Note: we are omitting the "vehicle" in front of the API node when using the vehicle object. The vehicle object is a map giving access to all available API nodes.

Changing the behavior of API Nodes in Prototype Code: We talked about that for many cases we might need a more sphisticated simulation and this is where we use the 'simulator' object passed to the Plugin function for. This can be used to override the behavior of any API node. The Simulator is a function that requires an exact API node as a string Note: with "Vehicle" in capital letters. Furthermore it requires the type of function we want to override (get / set) and lastly an async function to model the required behavior. The function has to be async because API nodes need to return a promise.

simulator("Vehicle.OBD.AmbientAirTemperature","get", async () => {
  return Math.floor(Math.random() * 1000);

simulator("Vehicle.Cabin.Seat.Row2.Pos1.Massage", "set", async (value) => {

The example above shows two template API nodes being overridden. The first one overrides the get() function of a Sensor and would make that sensor return a random value every time it is accessed. Of course we can also write a Function that uses a lookup table, returns static values or makes complex calculations based on a more complex simulation. The second example overrides the set() function of an Actuator. In this case we want to react to a change made by the Prototype code. Here we are passing the value to some function that will influence our simulation but we could do virtually anything with this value, save it, do calculations or use it to influence the values of some other API nodes with anothes Simulator.

Advanced: Creating custom widgets

widgets.register(<widget name>, widgetActivationFunction)

function widgetActivationFunction (box) {
  box.injecNode(<html code>);

Like mentioned in ... we can register Widgets using the "register" method of the "widget" object of the Plugin using a Widget name and a Widget activation function. While the latter can be generated by a reusable we can also write our own Widget activation function.

widgets.register("helloWidget", (box) => {
  const div = document.createElement("div");
  div.innerHTML = "Hello World";


The example above demonstrates how to register a simple hello world Widget. This will just display the text "Hello World" inside a div. In this example we wrote a basic Widget activation function inline of the register method. Inside we create a div and insert the "Hello World" text into it. Then we use the "injectNode" method of the "box" parameter to add our div to whatever box or group of boxes our Widget will be assigned to.

General functions

Adding Media

Opened Hamburger.png

If you click on the hamburger menu in the navigation bar (pictured above) a menu will fold out. If you click on the option "Media" you will be redirected to the overview of all available media files (images and videos). Here you can upload new media to be used in your projects with the "Upload Media" button on the top right of the screen.

Managing Tags

The hamburger menu described in the section above has an option "Tags" clicking on it will lead you to the Tag category overview:

Tag Category overview.png

Here you can either add a new tag category by clicking on "New Tag Category", entering a name and selecting a color in the unfolding menu. or you can add tags to existing categories by clicking on the category you want to modify. This will bring you to an overview of all Tags available to this category:

Tags of category.png

Tags are represented by boxes with headers containing the tags name and optionally a representative image. Clicking on the box with the dotted outline and plus symbol will let you add a tag by entering a Name and optionally a description in the unfolding menu. If you want to add an image to a tag you have to click it and select the image in the following screen.