POV - Portal View

Description

Accessing the rapidM2M REST API made easy

OVERVIEW

POV_Abstract:
POV → POrtal View
Portal View is called the site's viewable representation at the myDatanet backend.
The POV supports Embedded, Standalone and Root mode.
  • Embedded or Standalone - you can modify the site's appearence in the sitelist (list.vue) and details (details.vue | details.pug) representation.
  • Root - create your own Web Application which is hosted by the myDatanet backend.
  • The details representation appears when you click in the sitelist on the site's name.
    If you like to start building your own POV it's highly recommended to read the Vue.js documentation. So you will get a basic understanding about Vue.js.

    To change the Portal View's mode, go to the project settings page.
    Further readings → Embedded, Standalone and Root
    Embedded:
    With this mode the portal view is framed by the backend's portal.
    Within this frame there are several default libraries loaded.

    Content:

  • details.vue
  • list.vue

  • Provided libraries:

  • async - v2
  • bootstrap - v3.3.7
  • highcharts - v8.0.0
  • jquery - v2.2.4
  • moment - v2.18.1
  • vue - v2.6.10
  • Standalone:
    With this mode only the LIST-View is framed by the backend.
    The DETAILS-View is a fullscreen HTML page that is opened by the backend.

    Content:

  • details.pug
  • list.vue
  • Root:
    With this mode you can create your own Web Application.
    This application is hosted by the myDatanet backend and is reachable by a custom domain.
    To achive this, you need to create an dns entry that points to the public ip of the myDatanet server.
    The dns entry has to be an Address Mapping record (A Record) - also known as a DNS host record, stores a hostname and its corresponding IPv4 address.
    In this mode the List and the Details View can not be changed!
    Content:
  • index.pug
  • index.mjs
  • Example:
  • rootSample
  • list.vue:
    The list.vue file is used to add additional data to the site's list representation.
    It is available in both POV modes. Embedded and Standalone.
    If you start editing the List View, the content is displayed in the sitelist's APPLICATION view.
    This view can be switched at the top right of the sitelist.
    If the ~microino library is also added you can access the autogenerated DDE functions with this.DDE.inside the Vue.js context.
    Example:
    <template lang="pug"> .list-view h1 Hello World span Some funny counting: {{ cnt }} </template> <script> export default { name: "list-view", data() { return { cnt:0 } }, async created() { setInterval(() => {this.cnt++}, 1000); //increase the counter each second // --- only if ~microino is added --- //request some data with the autogenerated DDE functions: const container = await this.DDE.container_read(); //reads the data of the config container named "container" } } </script> <style lang="less" scoped> .list-view </style>
    details.vue:
    The details.vue is used to change the site's details (edit page) representation.
    The details.vue file is only available in Embedded mode!
    If you like to have the default representation see povDetailsDefaultSample.
    Example:
    <template lang="pug"> .details-view mdn-foldable(title="Funny counting", :expanded="true") h1 Hello World span Some funny counting: {{ cnt }} </template> <script> export default { name: "details-view", data() { return { cnt:0 } }, async created() { setInterval(() => {this.cnt++}, 1000); //increase the counter each second // --- only if ~microino is added --- //request some data with the autogenerated DDE functions: const container = await DDE.container_read(); //reads the data of the config container named "container" } } </script> <style lang="less" scoped> .details-view </style>
    details.pug:
    The details.pug file is used to change the site's details (edit page) representation.
    The details.pug is only available in Standalone mode!
    Example:
    standaloneDetailsSample
    index.pug:
    The index.pug file is the entry file of the Root mode.
    The index.pug is only available in Root mode!
    Example:
    rootSample

    Common

    images:
    Due to the fact that the files are bundled. Handling images is a bit different as usual.

    Usage with javascript recommended

    Example:
    <template lang="pug"> .vue-component //assign the image variable img(:src="myImage") </template> <script> //load the image as variable import loadedImage from "./images/myImage.png"; export default { name: "vue-component", data() { return { //assign the loaded image to the vue variable myImage: loadedImage } } } </script> <style lang="less" scoped> .vue-component { } </style>

    Usage with pug not recommendet

    Example:
    img(src="./images/myImage.png")

    Vue components

    Abstract:
    These components are only available in the Embedded Details View (details.vue)!
    mdn-foldable(

    The mdn-foldable component facilitates the organization of information into collapsible sections.

    title : string - specifies the title of the foldable section
    expanded : boolean - determines whether the section is initially expanded or collapsed
    tabs : string - enables the creation of subsections within the foldable area. This property requires a comma-separated list of titles (e.g., "Title1,Title2"). Each subsection's content is defined using the mdn-foldable-line component
    Example:
    mdn-foldable(title="My Foldable", :expanded="true") // Your content here
    mdn-foldable-line(

    The mdn-foldable-line component complements mdn-foldable, allowing the creation of multiple subsections within a foldable area.

    tabid : Number - specifies the index of the foldable subsection. It correlates with the tabs property of the mdn-foldable component
    Example:
    mdn-foldable(title="My Foldable", :expanded="true", :tabs="Basic,Alarm") mdn-foldable-line(:tabid='0') // Content for the Basic subsection mdn-foldable-line(:tabid='1') // Content for the Alarm subsection
    mdn-cfg-field-input(

    The mdn-cfg-field-input component enables viewing and editing of data for DDE-defined fields within POV.

    field : Object - specifies blueprint information of field
    width : [String,Number] - specifies the width of the input field
    readonly : Boolean - determines whether the field is read-only (default: false)
    Example:
    mdn-cfg-field-input(:field="blueprint.config0.status", v-model="data.config0.status")
    mdn-cfg-autoedit(

    The mdn-cfg-autoedit component is an enhanced version of mdn-cfg-field-input, offering versatility in data viewing and editing.

    field : Object - specifies blueprint information of field
    width : [String,Number] - specifies the width of the input field
    readonly : Boolean - determines whether the field is read-only (default: false)
    Example:
    mdn-cfg-autoedit(:field="blueprint.config0.status", v-model="data.config0.status")
    mdn-cfg-compactedit(

    The mdn-cfg-compactedit component is a compact version of mdn-cfg-autoedit, offering similar functionality in a more condensed format.

    field : Object - specifies blueprint information of field
    width : [String,Number] - specifies the width of the input field
    readonly : Boolean - determines whether the field is read-only (default: false)
    Example:
    mdn-cfg-compactedit(:field="blueprint.config0.status", v-model="data.config0.status")
    button(

    This component represents a simple Bootstrap 3 HTML button.For further customization options, refer to the Bootstrap documentation.

    Example:
    button.btn.btn-default(type='button' @click='') Button

    PAPI

    Description

    Accessing the rapidM2M REST API made easy

    OVERVIEW

    PAPI_Abstract:
    This library can be used to access JSON REST API's, mainly the myDatanet REST API.
    PAPIPortal API

    The library provides you with various things:
  • Information about the current site (see site_uid)
  • Easy to use API request functions (see get, put, post and del)
  • Automatic authorization against the currently used backend
  • Various timestamp treating functions (see TIMESTAMP TREATING)
  • Helper functions to return to the previous page (see goback)

  • API-Documentation

    Each myDatanet backend provides information about the supported API resources.
    You can find this information by calling: yourbackendurl + /api/1/doc
    eg. https://cloud.microtronics.com/api/1/doc

    How to use the PAPI

  • POV-Embedded mode:
  • → embeddedDetailsSample, embeddedListSample, povDetailsDefaultSample
  • POV-Standalone mode:
  • → standaloneDetailsSample
  • POV-Root mode:
  • → rootSample

    BASIC

    site_uid

    the unique id of the current site

    This uid can be used to make api calls.
    If you want to retrieve the full information about the site currently used, have a look at the second example.
    This is not the site name. This is the 16 character HEX representation of the site.
    Example:
    //get the unique id of the current site const site_uid = PAPI.site_uid; //site_uid will be something like '47FB40C429BF589B'
    Site information example:
    try { //Request additional information about the current site const siteInfo = await PAPI.get(`1/sites/${PAPI.site_uid}`); } catch(error) { //If the request fails, error contains additional information why. }
    customer_uid

    the unique id of the customer the current site belongs to

    This is not the customer name. This is the 16 character HEX representation of the customer.
    Example:
    //get unique id of the current site const customer_uid = PAPI.customer_uid; //customer_uid will be something like '47FB40C429BF589B'
    apm_id

    the Application Model id this Application is based on

    Example:
    //get the apm_id const apm_id = PAPI.apm_id;
    placeholders

    globally defined placeholders for automatic unfolding of string-paths

    For how to use it, have a look at the .get() or .put() request
    goback(

    function to return from the site details view to the previous page (e.g. sitelist).

    This function is only available in the details view.
    Example:
    //immediate return to the previous page PAPI.goback();
    timeout

    specifies the number of milliseconds before the request time is over.

    If the request takes longer than timeout, the request will be aborted.
    The default timeout is 15000 milliseconds (15 seconds).
    Example:
    //set the request timeout to 30 seconds PAPI.timeout = 30000;
    debug

    flag to enable/disable debug outputs;

    Default debug is set to false (e.g. debug output off)
    Example:
    // enable debug output PAPI.debug = true;
    host

    redirect api-host to an external system (instead of localhost)

    If you change the host you need to set the correct authorization with the auth property.
    Example:
    //set the new api host PAPI.host = "https://another.rapidm2m.com/api/";
    auth

    change the authorization

    With this property you can change the value for the authorization header.
    Example:
    //change the authorization header const username = 'MyUsername'; const password = 'MySecurePassword'; PAPI.auth = 'Basic ' + btoa(username + ':' + password);
    login(username, password, timeout)

    Login at the myDatanet appliance for further api request

    This login is only needed for the root mode
    username : string - user to access the api
    password : string - password to access the api
    timeout : number - Timeout until the session expires. (0-600 seconds)
    The default session timeout is set to 10 minutes.
    logout()

    Logout the current logged in session created by login()

    This is only needed in root mode

    COMMON

    error

    contains additional information about the error

    params

    optional object with query parameters, or null/undefined

    Date objects are automatically converted to rapidM2M stamps.
    For .del() requests most of the time you can set it to null.
    reply

    valid json data provided by reply body

    apipath

    api path with optional references '$xxx' to .placeholders{}

    API METHODS (HTTP REST)

    get(path, params)

    HTTP REST GET request

    path : apipath
    params : params
    returns : reply
    throws : error
    Example:
    try { //Query the logged-in user’s profile and customer list. const userProfile = await PAPI.get("1/me"); } catch(error) { //If the request fails, the error object contains further information why. } //usage with placeholders try { PAPI.placeholders.$cid="cust1"; const customer = await PAPI.get("/1/customers/$cid"); } catch(error) { //If the request fails, the error object contains further information why. }
    put(path, params)

    HTTP REST PUT request

    path : apipath
    params : params
    returns : reply
    reply contains only data if the api path returns any.
    throws : PAPIerror
    Example:
    //this example shows how to modify a site's profile //define what should be modified: const profile = { note: "This site was modified by the PAPI.put() request." } try { await PAPI.put("1/customers/mycustomer/sites/mysite", profile); } catch(error) { //If the request fails, the error object contains further information why. } //usage with placeholders try { PAPI.placeholders.$cid="cust1"; PAPI.placeholders.$sid="site1"; const customer = await PAPI.get("/1/customers/$cid/sites/$sid", profile); } catch(error) { //If the request fails, the error object contains further information why. }
    post(path, params)

    HTTP REST POST request

    works like the .put() request
    del(path, params)

    HTTP REST DEL request

    works like the .put() request

    BLO API EXTENSIONS

    extpapiblo:
    The following functions allow you to easily access your created BLO API extensions.
    Based on the development phase each BLO extension has a different prefix. But don't worry the PAPI does all the special handling for you!
    ext.get(path, params)

    EXT GET request

    works like the .get() request
    //Request data from the custom API path ... await PAPI.ext.get('<your_custom_api_path>');
    ext.put(path, params)

    EXT PUT request

    works like the .put() request
    //Push data to the custom API path ... await PAPI.ext.put('<your_custom_api_path>', {msg: 'hello world!'});
    ext.post(path, params)

    EXT POST request

    works like the .ext.put() request
    ext.del(path, params)

    EXT DEL request

    works like the .ext.del() request

    TIMESTAMP TREATING

    dateToStamp(

    Converts any javascript date object to a rapidm2m timestamp.

    Trailing zeros getting removed from the string!
    return : stamp:String - formatted as "YYYYMMDDHHmmssSSS"
    Example:
    //converts the current date into the rapidm2m timestamp equivalent const stamp = PAPI.dateToStamp(new Date());
    toStamp(dt)

    Converts times or date objects to rapidm2m timestamp

    dt : 0/null/undefined, (ISO-)string, js date object, number ms since 1970-01-01 0:00 UTC
    0/null/undefined returns the current time
    return : stamp:String - as "YYYYMMDDHHmmssSSS"
    Example:
    /* * Date given as javascript date * returns "20200617113145" - trailing zeros getting removed! */ const stamp = PAPI.toStamp(new Date('2020-06-17 11:31:45')); /* * Date given as milliseconds since 1970-01-01 0:00 UTC * returns "2020020220202" - trailing zeros getting removed! */ const stamp = PAPI.toStamp(1580671220000); /* * Date given as ISO String * returns "20200617113145213" - trailing zeros getting removed! */ const stamp = PAPI.toStamp("2020-06-17T11:31:45.213Z");
    stampToDate(stamp)

    Converts rapidm2m timestamp to date object

    stamp : String - as "YYYYMMDDHHmmssSSS"
    return : Object - Javascript Date object
    Example:
    /* * Date given as rapidm2m timestamp * returns the date object for the given stamp */ const date = PAPI.stampToDate("20200617113145213");
    stampToMS(stamp)

    Converts rapidm2m timestamp to unix timestamp

    stamp : String - as "YYYYMMDDHHmmssSSS"
    return : Number - Milliseconds since 1970-01-01 0:00 UTC
    Example:
    /* * Date given as rapidm2m timestamp * returns the milliseconds since 1970-01-01 0:00 UTC */ const milliseconds = PAPI.stampToMS("20200617113145213");
    stampFormat(stamp)

    Converts rapidm2m timestamp to a date string

    stamp : String - as "YYYYMMDDHHmmssSSS"
    return : String - as "YYYY-MM-DD HH:mm:ss.SSS"
    Example:
    /* * Date given as rapidm2m timestamp * returns the equivalent date string ("2020-06-17 11:31:45") */ const datestring = PAPI.stampFormat("20200617113145");
    stampAddMS(stamp, ms)

    Adds or removes milliseconds to/from stamp

    stamp : String - as "YYYYMMDDHHmmssSSS"
    ms : Number - any number in milliseconds
    return : String - as "YYYYMMDDHHmmssSSS"
    Example:
    /* * Date given as rapidm2m timestamp * returns the new rapidm2m timestamp with the added or removed amount of milliseconds ("20200202202022") */ const newStamp = PAPI.stampAddMS("2020020220202", 2000);

    EXPERT

    opts

    used to create a custom ~papi client

    If you provide username and password the auth property is not needed.
    host:host
    username:string - username string for basic authorization
    password:string - password string for basic authorization
    auth:auth
    timeout:timeout
    debug:debug
    x3rdparty:boolean - change the PAPI instance to access some 3rd party HTTP/REST service instead of the rapidM2M API.
    In this case only the methods .get(), .put(), post() and del() are available
    createClient(opts)

    Creates a new instance of the PAPI class

    opts
    If you want to use the library with an other host you can do it like it is shown in the following example.
    import '~papi'; //Only needed in root mode if the library wasn't loaded yet const opts = { host: 'https://another.rapidm2m.com/api/', username: 'MyUsername', password: 'MyPassword', /* If you need to use another authorization you can use the following auth property auth: '', */ timeout: 15000, debug: false, x3rdparty: false //optional - only if you want to access some 3rd party service } const FAPI = PAPI.createClient(opts); /* * use FAPI like the PAPI * await FAPI.get( ...) * await FAPI.put( ...) * await FAPI.post( ...) * await FAPI.del( ...) */

    POV-Embedded-Samples

    embeddedDetailsSample:
    This is a small sample how to use the PAPI in the embedded details view (details.vue).
    The PAPI is only available in the Vue context.
    So you need to call the PAPI functions with this.PAPI!
    <template lang="pug"> // === This is the PORTAL VIEW's DETAILS representation === .pov-details </template> <script> export default { name: "pov-details", components: {}, data() { return { } }, methods: { }, async created() { //Just request the current user's profile and customer list. const me = await this.PAPI.get('1/me'); } } </script> <style lang="less" scoped> //the list style definitions .pov-details { } </style>
    embeddedListSample:
    This is a small sample how to use the PAPI in the list view (list.vue).
    The PAPI is only available in the Vue context.
    So you need to call the PAPI functions with this.PAPI!
    <template lang="pug"> // === This is the PORTAL VIEW's LIST representation === .pov-list </template> <script> export default { name: "pov-list", components: {}, data() { return { } }, methods: { }, async created() { //Just request the current user's profile and customer list. const me = await this.PAPI.get('1/me'); } } </script> <style lang="less" scoped> //the list style definitions .pov-list { } </style>
    povDetailsDefaultSample:
    This is the default code that is used by the backend.
    <template lang="pug"> // === This is the PORTAL VIEW's DETAILS representation === // Have a look at the PAPI documentation! .pov-details // === Here starts the default DETAILS content (remove if not needed) // A loading spinner will be displayed if the 'loaded_' variable is falsy. .loading-spinner.fa.fa-spinner.fa-spin.fa-3x.fa-fw(v-if='!loaded_') template(v-else) // The mdn-foldable component displays settings in collapsible sections. mdn-foldable#basic_settings( :title="isAppl_ ? '%TXT%application' : '%TXT%site'" :expanded='true') template(slot-scope='foldableProps') mdn-autoedit(v-if='!isAppl_' :title="'%TXT%dbdefscustomer'" :value='customer.name' :readonly='true') mdn-autoedit(:title="'%TXT%dbdefssitesname'" v-model='basic.name') .row.row-config-field(v-if='!isAppl_') .col-xs-4 label.field-title.overflow-ellipsis %TXT%dbdefssitesdevice: .col-xs-8.form-inline select.form-control(v-model='selectedDevice') option(value='null') %TXT%link_none option(v-for='device in devices' v-text='device.device_id.toUpperCase()' :value='device._uid') mdn-autoedit(v-if='!isAppl_' :title="'%TXT%application'" :value='appl.name' :readonly='true') mdn-tag-controller(:title="'%TXT%tags'" v-model='basic.tags') mdn-foldable(title='%TXT%dbdefs_comment' :expanded='false') vue-editor#editor1(v-model='basic.note' :editor-toolbar='commentBoxToolbar') mdn-foldable(v-for='(cfg, name, idx) of configBlocks' v-if='loaded_ && !!data[name]' :key='name' :expanded='false' :title='getBlockTitle(name)' :id='name') mdn-cfg-autoedit(v-for='(field, alias) in cfg' v-if='field.index !== -1' :key='alias' :field='field' :alias='alias' v-model='data[name][alias]') mdn-foldable(:key="'alarms'" :title="'%TXT%site_alarms'") mdn-cfg-autoedit(v-if='basic.alarms' v-model:value='basic.alarms.ack_mode' :field="{ title: '%TXT%dbdefscfgalert_mode', \ editmask: createEMwDef(basic.options.ack_mode, '*', basic.defaults.server.alarms.ack_mode, basic.defaults.customer ? basic.defaults.customer.alarms.ack_mode : undefined)\ }") mdn-cfg-autoedit(v-if='basic.alarms' v-model:value='basic.alarms.offline' :field="{ title: '%TXT%dbdefs_config_offline_alarm_minutes', \ editmask: '%TIME%n%hh:nn', units: 'hh:mm' }") mdn-cfg-autoedit(v-if='basic.alarms' v-model:value='basic.alarms.user_alarm1' :field="{ title: '%TXT%dbdefsrm2m_useralarm 1' }") mdn-cfg-autoedit(v-if='basic.alarms' v-model:value='basic.alarms.user_alarm2' :field="{ title: '%TXT%dbdefsrm2m_useralarm 2' }") mdn-cfg-autoedit(v-if='basic.alarms' v-model:value='basic.alarms.user_alarm3' :field="{ title: '%TXT%dbdefsrm2m_useralarm 3' }") mdn-foldable(:key="'basic'" :title="'%TXT%site_settings'") mdn-cfg-autoedit(v-if='basic.general && basic.options' v-model:value='basic.general.timezone' :field="{ title: '%TXT%local_time_zone',\ editmask: createEMwDef(basic.options.timezone, null, basic.defaults.server.general.timezone)\ }") mdn-cfg-autoedit(v-if='basic.general && basic.options' v-model:value='basic.general.dst' :field="{ title: '%TXT%local_summertime',\ editmask: createEMwDef(basic.options.dst, '*', basic.defaults.server.general.dst)\ }") mdn-cfg-autoedit(v-if='!isAppl_ && basic.general && basic.options' v-model:value='basic.general.view' :field="{ title: '%TXT%dbdefssiteview', editmask: createEMwDef(basic.options.view, null) }") mdn-cfg-autoedit(v-if='basic.options' v-model:value='basic.direct_report' :field="{ title: '%TXT%view_template', editmask: createEMwDef(basic.options.direct_report, null) }") mdn-cfg-autoedit(v-if='basic.general && basic.options' v-model:value='basic.general.changelog' :field="{ title: '%TXT%dbdefs_config_uto_tracking', editmask: createEMwDef(basic.options.changelog, '*', basic.defaults.server.general.changelog, basic.defaults.customer ? basic.defaults.customer.general.changelog : undefined) }") table.btn-line(cellspacing='0' cellpadding='0' width='100%') tr td .btn-group button.btn.btn-default(type='button' @click='back') %TXT%cancel button.btn.btn-default(v-if='!newSite_' @click='apply(false)' :disabled='!!saving_') %TXT%apply button.btn.btn-default(type='button' @click='save' :disabled='!!saving_') %TXT%save td(style='text-align: right;') .btn-group button.btn.btn-default(v-if='!povMode' type='button' @click='forwardToRawConfigPage' :disabled='!!saving_') %TXT%site_raw_view </template> <script> export default { name: "pov-details", mixins: [MDN.vueSiteDetailsMixin], data() { return { } } } </script> <style lang="less" scoped> .pov-details { } </style>

    POV-Standalone-Samples

    standaloneDetailsSample:
    This is a small sample how to use the PAPI in the standalone details view (details.pug).
    In standalone mode the PAPI library is automatically attached to the window object of the browser.
    So you can simply use it with PAPI.get()
  • details.pug
  • doctype html html head meta(charset="utf-8") title POV - Details body script(src="//cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js") //our main script file: script(src='./index.mjs' type="module") //Vue root element app
  • index.mjs
  • //create new Vue instance new Vue({ el: 'app', components: { //load sub components }, async created() { //just request the current user's profile and customer list. const me = await PAPI.get('1/me'); } });

    POV-Root-Samples

    rootSample:
    This is a small sample how to get started with the Root Mode
    The PAPI library will be loaded within the index.pug file. The PAPI adds itself to the window object.
    So you can simply use it with PAPI.get()
  • index.pug
  • doctype html html head meta(charset="utf-8") title POV - Root body //load the ~papi library script(src="~papi.mjs", type="module") script(src="//cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js") //our main script file: script(src='./index.mjs' type="module") //Vue root element: app
  • index.mjs
  • //create new Vue instance new Vue({ el: 'app', components: { //load sub components }, async created() { //at first we need to log in at the backend await PAPI.login('username', 'password'); //if the login was sucsessful you can request the user's profile and customer list. const me = await PAPI.get('1/me'); } });