CSBL - Library functions for dashboard interaction

 

 

9. Library functions for dashboard interaction

It is assumed that you need to create a system of interfaces (dashboard) that allow the following interaction:

Following an event launched by a widget in dashboard A, dashboard B opens and is given the parameters necessary for a widget in dashboard B to retrieve data and display it.

For example, we could have a menu dashboard, with a table that will list certain devices (e.g., vehicles, batteries, users, etc.) and a card dashboard in which a set of widgets (time trends, bar plots, pie charts, etc.) ) will show the data collected for a specific device – see figure.

By clicking on a row of the table in the dashboard menu, the dashboard card will open and each widget will independently retrieve the data to be displayed.

To allow this interaction, the following two library functions have been implemented:

Note: Functions are automatically included in widgets, except for externalContent. In that case you need to explicitly specify the source js file to include, e.g.,

<script type='text/javascript' src='https://www.snap4city.org/dashboardSmartCity/js/widgetsCommonFunctions.js'>

</script>

taking care to specify the absolute path. The above included Javascript file contains a CSBL function library, including pre-defined functions of the Visual CSBL Editor, as well as the pre-defined functions that are used in the following examples:

 

9.1 Function: openNewDashboard

The function, to be specified in the CK Editor of the More Option panel of a widget, has the following definition

function openNewDashboard(url, target)

Input:

Outputs: none
Effect: Opens a new page using the specified url and target

9.2 Function: getParams

The function, to be specified in the CK Editor of the More Option panel of a widget, has the following definition

function getParams(isIFrame = false){

Input:

  • isIFrame (default false): specifies whether the widget implementing the function is loaded in the dashboard as an I-Frame (e.g., externalContent) or not.

Outputs:

  • A JSON string containing the GET parameters retrieved from the url of the same dashboard

Example interaction
Supposing to have

By clicking on the button in dashboard A, dashboard B will open and the pie-chart will retrieve the data of a specified device and then display them

Code for actuator (e.g. impulse button)

function execute() {
   let suri = 'http://www.disit.org/km4city/resource/iot/orionUNIFI/DISIT/prettoTestBattery03';
   let device = 'prettoBatteryTest03';
   let url = 'https://www.snap4city.org/dashboardSmartCity/view/index.php?iddasboard=MzY3NA==&suri=' + suri + '&device=' + device;
   let target = '_blank';
   openNewDashboard(url, target);
}

function execute() {
    let suri = 'http://www.disit.org/km4city/resource/iot/orionUNIFI/DISIT/METRO1030';
    let model = 'metrotrafficsensor';

    let device = 'METRO1030';
    let url = 'https://www.snap4city.org/dashboardSmartCity/view/index.php?iddasboard=NDIzMA==&suri=' +  suri + '&model=' + model + '&device=' +device;
    let target = '_blank';

    openNewDashboard(url, target);
}

Note: openNewDashboard must be included in the execute function as it must be invoked when an event is triggered by the actuating widget (e.g. the click on the button, or the selection of a row in the widgetDeviceTable, etc.)

When the button on dashboard A is clicked, dashboard B will open with the following url

https://www.snap4city.org/dashboardSmartCity/view/index.php?iddasboard=NDIzMA==&suri=http://www.disit.org/km4city/resour...

Then, on dashboard B, a pie-chart will run the following code to fetch the data of the selected device and then display it.

Receiver code (e.g. pie-chart)

let params = JSON.parse(getParams());
if(params.suri && params.device)  {
    let suri = params.suri;
    let device = params.device;
    let passedData= [
        {
            "metricId": 'http://www.disit.org/km4city/resource/iot/orionUNIFI/DISIT/' + device,
            "metricHighLevelType": 'IoT Device Variable',
            "metricName": device,
            "metricType":'vehicleFlow',
            "serviceUri": suri,
        },
        {
            "metricId": 'http://www.disit.org/km4city/resource/iot/orionUNIFI/DISIT/' + device,
            "metricHighLevelType": 'IoT Device Variable',
            "metricName": device,
            "metricType": 'averageSpeed',
            "serviceUri": suri,
        },
        {
            "metricId": 'http://www.disit.org/km4city/resource/iot/orionUNIFI/DISIT/' + device,
            "metricHighLevelType": 'IoT Device Variable',
            "metricName": device,
            "metricType": 'concentration',
            "serviceUri": suri,
        },
    ];
    setTimeout(function() {
        $('body').trigger({
            type: "showPieChartFromExternalContent_w_AggregationSeries_4230_widgetPieChart39612",
            eventGenerator: $(this),
            targetWidget: "w_AggregationSeries_4230_widgetPieChart39612",
            color1: "#e8a023",
            color2: "#9c6b17",
            widgetTitle: "Traffic Device Data",
            passedData: passedData
        })
    }, 750);    
}

Note: to allow the widget to load data, you need to impose a delay on $('body').trigger() using for example setTimeout(). Otherwise the widget will hang and fail to load the data.

9.3 Function: composeSURI(baseUrl, parameterName)

This function is included in the CSBL pre-defined function library, so it can be called directly in the JavaScript code in the CK editor. This function composes the service URI of a virtual device/entity by the concatenation of a baseURL, <BASE_SERVICEURI_ULR>  (e.g.: "http://www.disit.org/km4city/resource/iot/orionUNIFI/DISIT/"), and a string, <PARAMETER_NAME>, retrieved as a GET parameter from the dashboard URL.

9.4 Function: fetchAjax

Function: var response = fetchAjax(<API_URL>, <DATA_OBJ>, <METHOD>, <DATA_TYPE>, <ASYNC>, <TIMEOUT_VAL>)

This function is included in a global JavaScript library, so it can be called directly in the JavaScript code in the CK editor. This function makes an ajax call passing the input parameters as in the following:

$.ajax({
        url: <API_URL>,
        data: <DATA_OBJ>,
        type: <METHOD>,     // e.g. “GET”
        dataType: <DATA_TYPE>,     // 'json'
        async: true,            // asyncFlag
        timeout: timeoutVal
    })

It can be useful, for instance, to retrieve IoT device/entities data calling the Sanp4City Smart City API.

For example:

var response = fetchAjax("var apiUrl = "../controllers/superservicemapProxy.php/api/v1/?serviceUri= http://www.disit.org/km4city/resource/iot/orionUNIFI/DISIT/METRO1030", null, "GET", 'json', true, 0);

The response data (responseData) is returned as in the following example:

response.done(function(responseData) {
             if (responseData.error == null && responseData.failure == null) {             

                    // ...Do your CSBL...                   

             } else {
                    console.log("Error in retrieving data.");
                    console.log(JSON.stringify(responseData));
             }
       });

9.5 Function: triggerMetricsForTrends

Function: triggerMetricsForTrends (<WIDGET_ID>, <LISTENER_NAME>, <JSON_DATA>, <SELECTED_METRICS>, <BASE_KB_URL>, <LEGEND_ENTITY_NAME>, <TIME_RANGE>, <LEGEND_LABELS>)

This function is included in the CSBL pre-defined function library, so it can be called directly in the JavaScript code in the CK editor. This function prepares the required data for triggering the visualization in the target widget (having id <WIDGET_ID>), and triggered the prepared data by calling the widget specific listener (represented by <LISTENER_NAME>). The data preparation is applied to a json object (<JSON_DATA>) which is typically IoT device/entity data retrieved by calling the Snap4City API (for instance, through the above described fetchAjax function). To this aim, the base URL for querying the specific knowledge base has to be passed as a parameter (<BASE_KB_URL>, e.g.: https://servicemap.disit.org/WebAppGrafo/api/v1/?serviceUri=). It is possible to select a subset of the IoT device/entity metrics to be sent for visualization, specifying their name in the <SELECTED_METRICS> array of strings. It this parameter is null or undefined, all the IoT device/entity metrics values will be displayed.

Other parameters (optional, not required):

<LEGEND_ENTITY_NAME>: array of strings specifying the name of the devices to be represented in widgetCurvedLineSeries legend.

<LEGEND_LABELS>: array of strings specifying custom strings to be represented as metric labels in widgetCurvedLineSeries legend. It must follow the same oreder of the metrics in the <SELECTED_METRICS>, if present.

<TIME_RANGE>: specify the visualization time-range, useful for widgetTimeTrendCompare. It can be one of the following: "4/HOUR", "1/DAY", "7/DAY", "30/DAY", "180/DAY", "365/DAY".

9.6 Example

The following example use the above defined functions composeSURI, fetchAjax and triggerMetricsForTrends. The following JavaScript code is not included in the typical execute() function, thus showing another possible use of the CSBL, for instance allowing the execution of JavaScript code directly on dashboard loading. In this case, the following code can be placed in any of the widgets described in section 6 in order to dynamically show data from any virtual device/entity automatically on dashboard load, provided that the device name is passed as a GET parameter in the dashboard (e.g. using the "entityId" GET parameter: https://www.snap4city.org/dashboardSmartCity/view/Gea-Night.php?iddasboard=Mzk4Mg==&entityId=building2_33B). This is useful when you want to use a single dashboard to show data from many different entities by simply passing the device or entity name as a GET parameter in the dashboard URL.

var baseUrl = <BASE_SERVICEURI_ULR>;
var serviceUri = composeSURI(baseUrl, <PARAMETER_NAME>);
if (serviceUri != null) {
       var apiUrl = "../controllers/superservicemapProxy.php/api/v1/?serviceUri=" + serviceUri;
       var getSmartCityAPIData = fetchAjax(apiUrl, null, "GET", 'json', true, 0);
       getSmartCityAPIData.done(function(jsonData) {
             if (jsonData.error == null && jsonData.failure == null) {
                    var selectedMetrics = <SELECTED_METRICS>;
                    var legendEntityName = serviceUri.split("_")[1];
                    var widgetId = <WIDGET_ID>;
                    var baseKbUrl = "https://servicemap.disit.org/WebAppGrafo/api/v1/?serviceUri=";
                    var listenerName = <LISTENER_NAME> + widgetId;
                    triggerMetricsForTrends(widgetId, listenerName, jsonData, selectedMetrics, baseKbUrl, legendEntityName, <TIME_RANGE>, <LEGEND_LABELS>);
             } else {
                    console.log("Error in retrieving data from ServiceMap.");
                    console.log(JSON.stringify(geoJsonData));
             }
       });
}

<BASE_SERVICEURI_ULR>: it is the base URL in the service URI representing the building virtual device (e.g.: http://www.disit.org/km4city/resource/iot/orionUNIFI/DISIT/);

<PARAMETER_SAME>

<SELECTED_METRICS >: array of strings representing the name of the desired metrics of the virtual device to be shown in the widget having id <WIDGET_ID>;

<LISTENER_NAME>: name of the specific widget listener (depending on the <WIDGET_ID>, see seciont 5 for details, e.g.: "showBarSeriesFromExternalContent_" for widgetBarSeries);

NOTE: When building such a scenario, as described in the above example, it is convenient to set "Show CSBL Content on Load": "no" in the widget <WIDGET_ID> more options tab, in order to avoid possible overlapping effects if this widget has default metrics to be shown (for instance, when the dashboard is loaded without entityId parameter in the URL).