From c035eb05c6043a78dd56bad91bebe9eaa6d17ea6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 05:08:06 +0000 Subject: [PATCH] deploy: c8e5d44066ef0518a2168a6cda299c1667555071 --- 404.html | 4 +-- assets/js/402d2e48.4e83c139.js | 1 + assets/js/402d2e48.7c2c1226.js | 1 - ...n.642749d5.js => runtime~main.9ba3dced.js} | 2 +- blog.html | 4 +-- blog/archive.html | 4 +-- blog/first-blog-post.html | 4 +-- blog/long-blog-post.html | 4 +-- blog/mdx-blog-post.html | 4 +-- blog/tags.html | 4 +-- blog/tags/docusaurus.html | 4 +-- blog/tags/facebook.html | 4 +-- blog/tags/hello.html | 4 +-- blog/tags/hola.html | 4 +-- blog/welcome.html | 4 +-- docs/category/examples.html | 4 +-- docs/category/fmi-simulation.html | 4 +-- docs/category/guides.html | 4 +-- docs/category/installation.html | 4 +-- docs/category/overview.html | 4 +-- docs/category/raspberry-pi.html | 4 +-- docs/category/unity-visualization.html | 4 +-- docs/examples/ball-example.html | 4 +-- docs/examples/raspberry-example.html | 4 +-- .../raspberry-example/sending-data.html | 4 +-- docs/examples/string-example.html | 4 +-- docs/fmi/API.html | 32 +++++++++++++++++-- docs/fmi/concepts.html | 4 +-- docs/fmi/installation.html | 4 +-- docs/guides/dt-schema-creation.html | 4 +-- docs/guides/type-creation.html | 4 +-- docs/guides/unity/creating-unity-build.html | 4 +-- docs/guides/unity/using-grafana-plugin.html | 4 +-- docs/installation/manual.html | 4 +-- docs/installation/requirements.html | 4 +-- docs/installation/using-helm.html | 4 +-- docs/overview/architecture.html | 4 +-- docs/overview/concepts.html | 4 +-- docs/overview/purpose.html | 4 +-- docs/quickstart.html | 4 +-- index.html | 4 +-- markdown-page.html | 4 +-- 42 files changed, 107 insertions(+), 81 deletions(-) create mode 100644 assets/js/402d2e48.4e83c139.js delete mode 100644 assets/js/402d2e48.7c2c1226.js rename assets/js/{runtime~main.642749d5.js => runtime~main.9ba3dced.js} (98%) diff --git a/404.html b/404.html index 358ac24..2b37839 100644 --- a/404.html +++ b/404.html @@ -5,13 +5,13 @@ Page Not Found | OpenTwins - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/assets/js/402d2e48.4e83c139.js b/assets/js/402d2e48.4e83c139.js new file mode 100644 index 0000000..885ffb8 --- /dev/null +++ b/assets/js/402d2e48.4e83c139.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[5176],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>k});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},d=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,d=o(e,["components","mdxType","originalType","parentName"]),u=p(n),c=i,k=u["".concat(s,".").concat(c)]||u[c]||m[c]||r;return n?a.createElement(k,l(l({ref:t},d),{},{components:n})):a.createElement(k,l({ref:t},d))}));function k(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,l=new Array(r);l[0]=c;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[u]="string"==typeof e?e:i,l[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>r,metadata:()=>o,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const r={sidebar_position:3},l="API Documentation",o={unversionedId:"fmi/API",id:"fmi/API",title:"API Documentation",description:"Welcome to the Example API documentation. This API allows developers to interact with our services easily and efficiently.",source:"@site/docs/fmi/API.md",sourceDirName:"fmi",slug:"/fmi/API",permalink:"/opentwins/docs/fmi/API",draft:!1,editUrl:"https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/docs/fmi/API.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"FMI Simulation concepts",permalink:"/opentwins/docs/fmi/concepts"}},s={},p=[{value:"Table of Contents",id:"table-of-contents",level:2},{value:"Introduction",id:"introduction",level:2},{value:"Endpoints",id:"endpoints",level:2},{value:"FMU endpoints",id:"fmu-endpoints",level:3},{value:"1. GET /{context}",id:"1-get-context",level:4},{value:"Description",id:"description",level:5},{value:"Request",id:"request",level:5},{value:"Response",id:"response",level:5},{value:"2. POST /{context}",id:"2-post-context",level:4},{value:"Description",id:"description-1",level:5},{value:"Request",id:"request-1",level:5},{value:"Response",id:"response-1",level:5},{value:"3. GET /{context}/{fmuName}",id:"3-get-contextfmuname",level:4},{value:"Description",id:"description-2",level:5},{value:"Request",id:"request-2",level:5},{value:"Response",id:"response-2",level:5},{value:"4. DELETE /{context}/{fmuName}",id:"4-delete-contextfmuname",level:4},{value:"Description",id:"description-3",level:5},{value:"Request",id:"request-3",level:5},{value:"Response",id:"response-3",level:5},{value:"Schema endpoints",id:"schema-endpoints",level:3},{value:"1. GET /{context}",id:"1-get-context-1",level:4},{value:"Description",id:"description-4",level:5},{value:"Request",id:"request-4",level:5},{value:"Response",id:"response-4",level:5},{value:"2. POST /{context}",id:"2-post-context-1",level:4},{value:"Description",id:"description-5",level:5},{value:"Request",id:"request-5",level:5},{value:"Response",id:"response-5",level:5},{value:"3. GET /{context}/{schema_id}",id:"3-get-contextschema_id",level:4},{value:"Description",id:"description-6",level:5},{value:"Request",id:"request-6",level:5},{value:"Response",id:"response-6",level:5},{value:"4. DELETE /{context}/{schema_id}",id:"4-delete-contextschema_id",level:4},{value:"Description",id:"description-7",level:5},{value:"Request",id:"request-7",level:5},{value:"Response",id:"response-7",level:5},{value:"Simulation endpoints",id:"simulation-endpoints",level:3},{value:"1. GET /{context}",id:"1-get-context-2",level:4},{value:"Description",id:"description-8",level:5},{value:"Request",id:"request-8",level:5},{value:"Response",id:"response-8",level:5},{value:"2. POST /{context}",id:"2-post-context-2",level:4},{value:"Description",id:"description-9",level:5},{value:"Request",id:"request-9",level:5},{value:"Response",id:"response-9",level:5},{value:"3. GET /{context}/{simulation_id}",id:"3-get-contextsimulation_id",level:4},{value:"Description",id:"description-10",level:5},{value:"Request",id:"request-10",level:5},{value:"Response",id:"response-10",level:5},{value:"4. DELETE /{context}/{simulation_id}",id:"4-delete-contextsimulation_id",level:4},{value:"Description",id:"description-11",level:5},{value:"Request",id:"request-11",level:5},{value:"Response",id:"response-11",level:5},{value:"5. POST /{context}/{simulation_id}/resume",id:"5-post-contextsimulation_idresume",level:4},{value:"Description",id:"description-12",level:5},{value:"Request",id:"request-12",level:5},{value:"Response",id:"response-12",level:5},{value:"6. POST /{context}/{simulation_id}/pause",id:"6-post-contextsimulation_idpause",level:4},{value:"Description",id:"description-13",level:5},{value:"Request",id:"request-13",level:5},{value:"Response",id:"response-13",level:5}],d={toc:p},u="wrapper";function m(e){let{components:t,...n}=e;return(0,i.kt)(u,(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"api-documentation"},"API Documentation"),(0,i.kt)("p",null,"Welcome to the Example API documentation. This API allows developers to interact with our services easily and efficiently."),(0,i.kt)("h2",{id:"table-of-contents"},"Table of Contents"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#introduction"},"Introduction")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#endpoints"},"Endpoints"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#fmu-endpoints"},"FMU endpoints")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#schema-endpoints"},"Schema endpoints")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#simulation-endpoints"},"Simulation endpoints"))))),(0,i.kt)("h2",{id:"introduction"},"Introduction"),(0,i.kt)("p",null,"This API provides access to FMU data and allows for the creation and management of simulation schemas and simulations. It is based on REST and returns responses in JSON format."),(0,i.kt)("h2",{id:"endpoints"},"Endpoints"),(0,i.kt)("h3",{id:"fmu-endpoints"},"FMU endpoints"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Method"),(0,i.kt)("th",{parentName:"tr",align:null},"Endpoint /fmi/fmus"),(0,i.kt)("th",{parentName:"tr",align:null},"Description"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#1-get-context"},"GET")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}"),(0,i.kt)("td",{parentName:"tr",align:null},"Retrieve a list of FMUs")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#2-post-context"},"POST")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}"),(0,i.kt)("td",{parentName:"tr",align:null},"Upload a new FMU")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#3-get-contextfmuname"},"GET")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}/{fmuName}"),(0,i.kt)("td",{parentName:"tr",align:null},"Retrieve FMU information within a context")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#4-delete-contextfmuname"},"DELETE")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}/{fmuName}"),(0,i.kt)("td",{parentName:"tr",align:null},"Delete a FMU within a context")))),(0,i.kt)("h4",{id:"1-get-context"},"1. GET /{context}"),(0,i.kt)("h5",{id:"description"},"Description"),(0,i.kt)("p",null,"Get a list of FMUs in a specific context."),(0,i.kt)("h5",{id:"request"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"GET"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}")),(0,i.kt)("h5",{id:"response"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")," A list of FMUs with information about their variables."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'[\n {\n "id": "bouncingBallCS2",\n "inputs": [],\n "outputs": [],\n "other_variables": [\n {\n "name": "h",\n "type": "Real",\n "default": {\n "start": "1"\n },\n "description": "height, used as state"\n },\n {\n "name": "v",\n "type": "Real",\n "default": {\n "start": "0",\n "reinit": "true"\n },\n "description": "velocity of ball, used as state"\n },\n {\n "name": "g",\n "type": "Real",\n "default": {\n "start": "9.81"\n },\n "description": "acceleration of gravity"\n },\n {\n "name": "e",\n "type": "Real",\n "default": {\n "start": "0.7",\n "min": "0.5",\n "max": "1"\n },\n "description": "dimensionless parameter"\n }\n ]\n }\n]\n')),(0,i.kt)("h4",{id:"2-post-context"},"2. POST /{context}"),(0,i.kt)("h5",{id:"description-1"},"Description"),(0,i.kt)("p",null,"Upload a new FMU to the sistem into a specific context."),(0,i.kt)("h5",{id:"request-1"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"POST"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}")),(0,i.kt)("h5",{id:"response-1"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'"FMU uploaded succesfully"\n')),(0,i.kt)("h4",{id:"3-get-contextfmuname"},"3. GET /{context}/{fmuName}"),(0,i.kt)("h5",{id:"description-2"},"Description"),(0,i.kt)("p",null,"Get the XML file of a specific FMU."),(0,i.kt)("h5",{id:"request-2"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"GET"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}/{fmuName}")),(0,i.kt)("h5",{id:"response-2"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-xml"},'\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n')),(0,i.kt)("h4",{id:"4-delete-contextfmuname"},"4. DELETE /{context}/{fmuName}"),(0,i.kt)("h5",{id:"description-3"},"Description"),(0,i.kt)("p",null,"Delete a FMU from the sistem in a specific context."),(0,i.kt)("h5",{id:"request-3"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"DELETE"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}/{fmuName}")),(0,i.kt)("h5",{id:"response-3"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'"FMU deleted succesfully"\n')),(0,i.kt)("hr",null),(0,i.kt)("h3",{id:"schema-endpoints"},"Schema endpoints"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Method"),(0,i.kt)("th",{parentName:"tr",align:null},"Endpoint /fmi/schemas"),(0,i.kt)("th",{parentName:"tr",align:null},"Description"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#1-get-context-1"},"GET")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}"),(0,i.kt)("td",{parentName:"tr",align:null},"Retrieve a list of schemas")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#2-post-context-1"},"POST")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}"),(0,i.kt)("td",{parentName:"tr",align:null},"Upload a new schema")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#3-get-contextschema_id"},"GET")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}/{schema_id}"),(0,i.kt)("td",{parentName:"tr",align:null},"Retrieve a schema within a context")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#4-delete-contextschema_id"},"DELETE")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}/{schema_id}"),(0,i.kt)("td",{parentName:"tr",align:null},"Delete a schema within a context")))),(0,i.kt)("h4",{id:"1-get-context-1"},"1. GET /{context}"),(0,i.kt)("h5",{id:"description-4"},"Description"),(0,i.kt)("p",null,"Get all simulation schemas in a specific context."),(0,i.kt)("h5",{id:"request-4"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"GET"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}")),(0,i.kt)("h5",{id:"response-4"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")," A list of name and id of every single simulation schemas."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'[\n {\n "id": "schema1",\n "name": "Schema 1"\n }\n]\n')),(0,i.kt)("h4",{id:"2-post-context-1"},"2. POST /{context}"),(0,i.kt)("h5",{id:"description-5"},"Description"),(0,i.kt)("p",null,"Create a new schema in a specific context."),(0,i.kt)("h5",{id:"request-5"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"POST"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}")),(0,i.kt)("h5",{id:"response-5"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")," A list of name and id of every single simulation schemas."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'"Schema created succesfully"\n')),(0,i.kt)("h4",{id:"3-get-contextschema_id"},"3. GET /{context}/{schema_id}"),(0,i.kt)("h5",{id:"description-6"},"Description"),(0,i.kt)("p",null,"Get a schema in a specific context."),(0,i.kt)("h5",{id:"request-6"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"GET"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}/{schema_id}")),(0,i.kt)("h5",{id:"response-6"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'[\n {\n "id": "schema2",\n "fmus": [\n {\n "id": "Controller",\n "inputs": [\n {\n "id": "u_s"\n },\n {\n "id": "u_m"\n }\n ],\n "outputs": [\n {\n "id": "y"\n }\n ]\n },\n {\n "id": "Drivetrain",\n "inputs": [\n {\n "id": "tau"\n }\n ],\n "outputs": [\n {\n "id": "w"\n }\n ]\n }\n ],\n "name": "Schema 2",\n "schema": [\n {\n "to": {\n "id": "Controller",\n "var": "u_s"\n },\n "from": {\n "var": "w_ref"\n }\n },\n {\n "to": {\n "id": "Controller",\n "var": "u_m"\n },\n "from": {\n "id": "Drivetrain",\n "var": "w"\n }\n },\n {\n "to": {\n "id": "Drivetrain",\n "var": "tau"\n },\n "from": {\n "id": "Controller",\n "var": "y"\n }\n },\n {\n "to": {\n "var": "w"\n },\n "from": {\n "id": "Drivetrain",\n "var": "w"\n }\n }\n ],\n "description": "Testing schema",\n "relatedTwins": [\n "Twin1"\n ]\n }\n]\n')),(0,i.kt)("h4",{id:"4-delete-contextschema_id"},"4. DELETE /{context}/{schema_id}"),(0,i.kt)("h5",{id:"description-7"},"Description"),(0,i.kt)("p",null,"Delete a schema in a specific context."),(0,i.kt)("h5",{id:"request-7"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"DELETE"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}/{schema_id}")),(0,i.kt)("h5",{id:"response-7"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'"Schema deleted succesfully"\n')),(0,i.kt)("hr",null),(0,i.kt)("h3",{id:"simulation-endpoints"},"Simulation endpoints"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Method"),(0,i.kt)("th",{parentName:"tr",align:null},"Endpoint /fmi/simulations"),(0,i.kt)("th",{parentName:"tr",align:null},"Description"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#1-get-context-2"},"GET")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}"),(0,i.kt)("td",{parentName:"tr",align:null},"Retrieve a list of running simulations")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#2-post-context-2"},"POST")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}"),(0,i.kt)("td",{parentName:"tr",align:null},"Deploy simulation")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#3-get-contextsimulation_id"},"GET")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}/{simulation_id}"),(0,i.kt)("td",{parentName:"tr",align:null},"Retrieve simulation info within a context")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#4-delete-contextsimulation_id"},"DELETE")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}/{simulation_id}"),(0,i.kt)("td",{parentName:"tr",align:null},"Delete a simulation within a context")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#5-post-contextsimulation_idresume"},"POST")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}/{simulation_id}/resume"),(0,i.kt)("td",{parentName:"tr",align:null},"Resume a specific simulation")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"#6-post-contextsimulation_idpause"},"POST")),(0,i.kt)("td",{parentName:"tr",align:null},"/{context}/{simulation_id}/pause"),(0,i.kt)("td",{parentName:"tr",align:null},"Stops a specific simulation")))),(0,i.kt)("h4",{id:"1-get-context-2"},"1. GET /{context}"),(0,i.kt)("h5",{id:"description-8"},"Description"),(0,i.kt)("p",null,"Get all running simulations in a specific context."),(0,i.kt)("h5",{id:"request-8"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"GET"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}")),(0,i.kt)("h5",{id:"response-8"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")," A list of information of every single running simulations."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'\n[\n {\n "schema-id": "schema1",\n "simulation-id": "pruebabouncingball",\n "namespace": "opentwins",\n "type": "one-time",\n "status": "Active",\n "pods": [\n {\n "simulation-id": "pruebabouncingball",\n "phase": "Running",\n "status": false,\n "creation_timestamp": "2024/09/26, 03:06:06+0000"\n }\n ]\n }\n]\n\n')),(0,i.kt)("h4",{id:"2-post-context-2"},"2. POST /{context}"),(0,i.kt)("h5",{id:"description-9"},"Description"),(0,i.kt)("p",null,"Create a new simulation using a existing schema in a specific context."),(0,i.kt)("h5",{id:"request-9"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"POST"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}")),(0,i.kt)("h5",{id:"response-9"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'"true"\n')),(0,i.kt)("h4",{id:"3-get-contextsimulation_id"},"3. GET /{context}/{simulation_id}"),(0,i.kt)("h5",{id:"description-10"},"Description"),(0,i.kt)("p",null,"Get all information about a specific simulation in a specific context."),(0,i.kt)("h5",{id:"request-10"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"GET"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}/{simulation_id}")),(0,i.kt)("h5",{id:"response-10"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n "api_version": "batch/v1",\n "kind": "Job",\n "metadata": {\n "annotations": null,\n "creation_timestamp": "2024-09-26 03:06:06+00:00",\n "deletion_grace_period_seconds": null,\n "deletion_timestamp": null,\n .\n .\n .\n }\n}\n')),(0,i.kt)("h4",{id:"4-delete-contextsimulation_id"},"4. DELETE /{context}/{simulation_id}"),(0,i.kt)("h5",{id:"description-11"},"Description"),(0,i.kt)("p",null,"Delete specific simulation in a specific context."),(0,i.kt)("h5",{id:"request-11"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"DELETE"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}/{simulation_id}")),(0,i.kt)("h5",{id:"response-11"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"Response Body:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'"Schema deleted succesfully"\n')),(0,i.kt)("h4",{id:"5-post-contextsimulation_idresume"},"5. POST /{context}/{simulation_id}/resume"),(0,i.kt)("h5",{id:"description-12"},"Description"),(0,i.kt)("p",null,"Resume paused simulation in a specific context."),(0,i.kt)("h5",{id:"request-12"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"POST"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}/{simulation_id}/resume")),(0,i.kt)("h5",{id:"response-12"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK")," "),(0,i.kt)("h4",{id:"6-post-contextsimulation_idpause"},"6. POST /{context}/{simulation_id}/pause"),(0,i.kt)("h5",{id:"description-13"},"Description"),(0,i.kt)("p",null,"Pause simulation in a specific context."),(0,i.kt)("h5",{id:"request-13"},"Request"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Method:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"POST"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("strong",{parentName:"p"},"URL:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"/{context}/{simulation_id}/pause")),(0,i.kt)("h5",{id:"response-13"},"Response"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Status code:")," ",(0,i.kt)("inlineCode",{parentName:"p"},"200 OK")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/402d2e48.7c2c1226.js b/assets/js/402d2e48.7c2c1226.js deleted file mode 100644 index f99de55..0000000 --- a/assets/js/402d2e48.7c2c1226.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[5176],{3905:(t,e,n)=>{n.d(e,{Zo:()=>s,kt:()=>k});var a=n(7294);function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function l(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,a)}return n}function i(t){for(var e=1;e=0||(r[n]=t[n]);return r}(t,e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(t);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(r[n]=t[n])}return r}var m=a.createContext({}),p=function(t){var e=a.useContext(m),n=e;return t&&(n="function"==typeof t?t(e):i(i({},e),t)),n},s=function(t){var e=p(t.components);return a.createElement(m.Provider,{value:e},t.children)},u="mdxType",d={inlineCode:"code",wrapper:function(t){var e=t.children;return a.createElement(a.Fragment,{},e)}},c=a.forwardRef((function(t,e){var n=t.components,r=t.mdxType,l=t.originalType,m=t.parentName,s=o(t,["components","mdxType","originalType","parentName"]),u=p(n),c=r,k=u["".concat(m,".").concat(c)]||u[c]||d[c]||l;return n?a.createElement(k,i(i({ref:e},s),{},{components:n})):a.createElement(k,i({ref:e},s))}));function k(t,e){var n=arguments,r=e&&e.mdxType;if("string"==typeof t||r){var l=n.length,i=new Array(l);i[0]=c;var o={};for(var m in e)hasOwnProperty.call(e,m)&&(o[m]=e[m]);o.originalType=t,o[u]="string"==typeof t?t:r,i[1]=o;for(var p=2;p{n.r(e),n.d(e,{assets:()=>m,contentTitle:()=>i,default:()=>d,frontMatter:()=>l,metadata:()=>o,toc:()=>p});var a=n(7462),r=(n(7294),n(3905));const l={sidebar_position:3},i="API Documentation",o={unversionedId:"fmi/API",id:"fmi/API",title:"API Documentation",description:"Welcome to the Example API documentation. This API allows developers to interact with our services easily and efficiently.",source:"@site/docs/fmi/API.md",sourceDirName:"fmi",slug:"/fmi/API",permalink:"/opentwins/docs/fmi/API",draft:!1,editUrl:"https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/docs/fmi/API.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"FMI Simulation concepts",permalink:"/opentwins/docs/fmi/concepts"}},m={},p=[{value:"Table of Contents",id:"table-of-contents",level:2},{value:"Introduction",id:"introduction",level:2},{value:"Endpoints",id:"endpoints",level:2},{value:"FMU endpoints",id:"fmu-endpoints",level:3},{value:"Schema endpoints",id:"schema-endpoints",level:3},{value:"Simulation endpoints",id:"simulation-endpoints",level:3}],s={toc:p},u="wrapper";function d(t){let{components:e,...n}=t;return(0,r.kt)(u,(0,a.Z)({},s,n,{components:e,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"api-documentation"},"API Documentation"),(0,r.kt)("p",null,"Welcome to the Example API documentation. This API allows developers to interact with our services easily and efficiently."),(0,r.kt)("h2",{id:"table-of-contents"},"Table of Contents"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#introduction"},"Introduction")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#endpoints"},"Endpoints"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#get-users"},"GET /users")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"#post-users"},"POST /users"))))),(0,r.kt)("h2",{id:"introduction"},"Introduction"),(0,r.kt)("p",null,"This API provides access to FMU data and allows for the creation and management of simulation schemas and simulations. It is based on REST and returns responses in JSON format."),(0,r.kt)("h2",{id:"endpoints"},"Endpoints"),(0,r.kt)("h3",{id:"fmu-endpoints"},"FMU endpoints"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Method"),(0,r.kt)("th",{parentName:"tr",align:null},"Endpoint"),(0,r.kt)("th",{parentName:"tr",align:null},"Description"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"GET"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/fmus/{context}"),(0,r.kt)("td",{parentName:"tr",align:null},"Retrieve a list of FMUs")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"POST"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/fmus/{context}"),(0,r.kt)("td",{parentName:"tr",align:null},"Upload a new FMU")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"GET"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/fmus/{context}/{fmuName}"),(0,r.kt)("td",{parentName:"tr",align:null},"Retrieve FMU information within a context")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"DELETE"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/fmus/{context}/{fmuName}"),(0,r.kt)("td",{parentName:"tr",align:null},"Delete a FMU within a context")))),(0,r.kt)("hr",null),(0,r.kt)("h3",{id:"schema-endpoints"},"Schema endpoints"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Method"),(0,r.kt)("th",{parentName:"tr",align:null},"Endpoint"),(0,r.kt)("th",{parentName:"tr",align:null},"Description"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"GET"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/schemas/{context}"),(0,r.kt)("td",{parentName:"tr",align:null},"Retrieve a list of schemas")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"POST"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/schemas/{context}"),(0,r.kt)("td",{parentName:"tr",align:null},"Upload a new schema")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"GET"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/schemas/{context}/{schema_id}"),(0,r.kt)("td",{parentName:"tr",align:null},"Retrieve a schema within a context")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"DELETE"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/schemas/{context}/{schema_id}"),(0,r.kt)("td",{parentName:"tr",align:null},"Delete a schema within a context")))),(0,r.kt)("hr",null),(0,r.kt)("h3",{id:"simulation-endpoints"},"Simulation endpoints"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Method"),(0,r.kt)("th",{parentName:"tr",align:null},"Endpoint"),(0,r.kt)("th",{parentName:"tr",align:null},"Description"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"GET"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/simulations/{context}"),(0,r.kt)("td",{parentName:"tr",align:null},"Retrieve a list of running simulations")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"POST"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/simulations/{context}"),(0,r.kt)("td",{parentName:"tr",align:null},"Deploy simulation")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"GET"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/simulations/{context}/{simulation_id}"),(0,r.kt)("td",{parentName:"tr",align:null},"Retrieve simulation info within a context")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"DELETE"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/simulations/{context}/{simulation_id}"),(0,r.kt)("td",{parentName:"tr",align:null},"Delete a simulation within a context")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"POST"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/simulations/{context}/{simulation_id}/resume"),(0,r.kt)("td",{parentName:"tr",align:null},"Resume a specific simulation")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"POST"),(0,r.kt)("td",{parentName:"tr",align:null},"/fmi/simulations/{context}/{simulation_id}/pause"),(0,r.kt)("td",{parentName:"tr",align:null},"Stops a specific simulation")))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.642749d5.js b/assets/js/runtime~main.9ba3dced.js similarity index 98% rename from assets/js/runtime~main.642749d5.js rename to assets/js/runtime~main.9ba3dced.js index 8ab05ab..906734e 100644 --- a/assets/js/runtime~main.642749d5.js +++ b/assets/js/runtime~main.9ba3dced.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,c,t,r,f={},d={};function b(e){var a=d[e];if(void 0!==a)return a.exports;var c=d[e]={id:e,loaded:!1,exports:{}};return f[e].call(c.exports,c,c.exports,b),c.loaded=!0,c.exports}b.m=f,b.c=d,e=[],b.O=(a,c,t,r)=>{if(!c){var f=1/0;for(i=0;i=r)&&Object.keys(b.O).every((e=>b.O[e](c[o])))?c.splice(o--,1):(d=!1,r0&&e[i-1][2]>r;i--)e[i]=e[i-1];e[i]=[c,t,r]},b.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return b.d(a,{a:a}),a},c=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,b.t=function(e,t){if(1&t&&(e=this(e)),8&t)return e;if("object"==typeof e&&e){if(4&t&&e.__esModule)return e;if(16&t&&"function"==typeof e.then)return e}var r=Object.create(null);b.r(r);var f={};a=a||[null,c({}),c([]),c(c)];for(var d=2&t&&e;"object"==typeof d&&!~a.indexOf(d);d=c(d))Object.getOwnPropertyNames(d).forEach((a=>f[a]=()=>e[a]));return f.default=()=>e,b.d(r,f),r},b.d=(e,a)=>{for(var c in a)b.o(a,c)&&!b.o(e,c)&&Object.defineProperty(e,c,{enumerable:!0,get:a[c]})},b.f={},b.e=e=>Promise.all(Object.keys(b.f).reduce(((a,c)=>(b.f[c](e,a),a)),[])),b.u=e=>"assets/js/"+({53:"935f2afb",114:"908ba98b",788:"b0bae498",799:"c0eb0ada",948:"8717b14a",1144:"40b0d055",1199:"ac75af2e",1368:"4899252d",1495:"d3d686ac",1683:"26bc6599",1761:"acac1da9",1826:"5a7456e0",1914:"d9f32620",2267:"59362658",2362:"e273c56f",2535:"814f3328",2614:"32191809",2690:"8dd02d2f",3085:"1f391b9e",3089:"a6aa9e1f",3237:"1df93b7f",3514:"73664a40",3608:"9e4087bc",4013:"01a85c17",4099:"8777f386",4283:"5eb90766",4397:"e9a1c9e5",4576:"0598cbc5",4699:"85531627",5041:"aa3c268d",5049:"74876495",5176:"402d2e48",5284:"6a6251fe",5391:"9281dd35",5466:"70066871",5998:"eb58dad4",6103:"ccc49370",6216:"68c71cca",6513:"d72ac48e",6933:"4c455ca7",7063:"ec3c7536",7414:"393be207",7431:"1c4bf583",7557:"928e06c2",7632:"0964aedb",7826:"8ee96214",7918:"17896441",7979:"118e913f",8364:"96e1810e",8610:"6875c492",8636:"f4f34a3a",8906:"1ba72f0d",9003:"925b3f96",9048:"e677b25a",9514:"1be78505",9521:"3c0fcc1c",9534:"3fb959cb",9642:"7661071f",9817:"14eb3368",9850:"dcc71bcc"}[e]||e)+"."+{53:"caa8fb63",114:"a7d9cf5e",210:"6e5e9f1a",788:"5ee92163",799:"b281447b",948:"74619e7a",1144:"4e4684a7",1199:"f73fc938",1368:"b3e32514",1495:"c9777f1c",1683:"3b7fe1d7",1761:"05f2b0af",1826:"a5334c36",1914:"b045544d",2267:"22a5d353",2362:"c362744b",2529:"cec79ce1",2535:"0c1d9999",2614:"bb78a4f4",2690:"7fc106b4",3085:"8fc7c9b3",3089:"845cad8c",3237:"20e07858",3514:"d45d5659",3608:"472c889f",4013:"75a76f22",4099:"1463edb8",4283:"49b2da36",4397:"f065f0d0",4576:"b9f9eae9",4699:"61a331f6",4972:"b60a5582",5041:"11260222",5049:"cd08c5dc",5176:"7c2c1226",5284:"ed7cf9fb",5391:"056d9a3c",5466:"508587da",5998:"ce7e3bd0",6103:"d9c41d1e",6216:"704c8102",6513:"cd787f6d",6933:"946c6245",7063:"adf7bfc2",7414:"6ad33c11",7431:"58dbf46d",7557:"fee88172",7632:"f08e1154",7826:"92b136ff",7918:"4f945c03",7979:"b956ff9a",8364:"a859fe5f",8610:"f37b7b5c",8636:"77d55ebe",8906:"aa79666e",9003:"a62a82fe",9048:"4140a2d3",9514:"685933da",9521:"e76176e0",9534:"7f1cc7ba",9642:"77dfe874",9817:"a5d80bcb",9850:"e2c3cca1"}[e]+".js",b.miniCssF=e=>{},b.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),b.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),t={},r="docs:",b.l=(e,a,c,f)=>{if(t[e])t[e].push(a);else{var d,o;if(void 0!==c)for(var n=document.getElementsByTagName("script"),i=0;i{d.onerror=d.onload=null,clearTimeout(s);var r=t[e];if(delete t[e],d.parentNode&&d.parentNode.removeChild(d),r&&r.forEach((e=>e(c))),a)return a(c)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),o&&document.head.appendChild(d)}},b.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},b.p="/opentwins/",b.gca=function(e){return e={17896441:"7918",32191809:"2614",59362658:"2267",70066871:"5466",74876495:"5049",85531627:"4699","935f2afb":"53","908ba98b":"114",b0bae498:"788",c0eb0ada:"799","8717b14a":"948","40b0d055":"1144",ac75af2e:"1199","4899252d":"1368",d3d686ac:"1495","26bc6599":"1683",acac1da9:"1761","5a7456e0":"1826",d9f32620:"1914",e273c56f:"2362","814f3328":"2535","8dd02d2f":"2690","1f391b9e":"3085",a6aa9e1f:"3089","1df93b7f":"3237","73664a40":"3514","9e4087bc":"3608","01a85c17":"4013","8777f386":"4099","5eb90766":"4283",e9a1c9e5:"4397","0598cbc5":"4576",aa3c268d:"5041","402d2e48":"5176","6a6251fe":"5284","9281dd35":"5391",eb58dad4:"5998",ccc49370:"6103","68c71cca":"6216",d72ac48e:"6513","4c455ca7":"6933",ec3c7536:"7063","393be207":"7414","1c4bf583":"7431","928e06c2":"7557","0964aedb":"7632","8ee96214":"7826","118e913f":"7979","96e1810e":"8364","6875c492":"8610",f4f34a3a:"8636","1ba72f0d":"8906","925b3f96":"9003",e677b25a:"9048","1be78505":"9514","3c0fcc1c":"9521","3fb959cb":"9534","7661071f":"9642","14eb3368":"9817",dcc71bcc:"9850"}[e]||e,b.p+b.u(e)},(()=>{var e={1303:0,532:0};b.f.j=(a,c)=>{var t=b.o(e,a)?e[a]:void 0;if(0!==t)if(t)c.push(t[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var r=new Promise(((c,r)=>t=e[a]=[c,r]));c.push(t[2]=r);var f=b.p+b.u(a),d=new Error;b.l(f,(c=>{if(b.o(e,a)&&(0!==(t=e[a])&&(e[a]=void 0),t)){var r=c&&("load"===c.type?"missing":c.type),f=c&&c.target&&c.target.src;d.message="Loading chunk "+a+" failed.\n("+r+": "+f+")",d.name="ChunkLoadError",d.type=r,d.request=f,t[1](d)}}),"chunk-"+a,a)}},b.O.j=a=>0===e[a];var a=(a,c)=>{var t,r,f=c[0],d=c[1],o=c[2],n=0;if(f.some((a=>0!==e[a]))){for(t in d)b.o(d,t)&&(b.m[t]=d[t]);if(o)var i=o(b)}for(a&&a(c);n{"use strict";var e,a,c,t,r,f={},d={};function b(e){var a=d[e];if(void 0!==a)return a.exports;var c=d[e]={id:e,loaded:!1,exports:{}};return f[e].call(c.exports,c,c.exports,b),c.loaded=!0,c.exports}b.m=f,b.c=d,e=[],b.O=(a,c,t,r)=>{if(!c){var f=1/0;for(i=0;i=r)&&Object.keys(b.O).every((e=>b.O[e](c[o])))?c.splice(o--,1):(d=!1,r0&&e[i-1][2]>r;i--)e[i]=e[i-1];e[i]=[c,t,r]},b.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return b.d(a,{a:a}),a},c=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,b.t=function(e,t){if(1&t&&(e=this(e)),8&t)return e;if("object"==typeof e&&e){if(4&t&&e.__esModule)return e;if(16&t&&"function"==typeof e.then)return e}var r=Object.create(null);b.r(r);var f={};a=a||[null,c({}),c([]),c(c)];for(var d=2&t&&e;"object"==typeof d&&!~a.indexOf(d);d=c(d))Object.getOwnPropertyNames(d).forEach((a=>f[a]=()=>e[a]));return f.default=()=>e,b.d(r,f),r},b.d=(e,a)=>{for(var c in a)b.o(a,c)&&!b.o(e,c)&&Object.defineProperty(e,c,{enumerable:!0,get:a[c]})},b.f={},b.e=e=>Promise.all(Object.keys(b.f).reduce(((a,c)=>(b.f[c](e,a),a)),[])),b.u=e=>"assets/js/"+({53:"935f2afb",114:"908ba98b",788:"b0bae498",799:"c0eb0ada",948:"8717b14a",1144:"40b0d055",1199:"ac75af2e",1368:"4899252d",1495:"d3d686ac",1683:"26bc6599",1761:"acac1da9",1826:"5a7456e0",1914:"d9f32620",2267:"59362658",2362:"e273c56f",2535:"814f3328",2614:"32191809",2690:"8dd02d2f",3085:"1f391b9e",3089:"a6aa9e1f",3237:"1df93b7f",3514:"73664a40",3608:"9e4087bc",4013:"01a85c17",4099:"8777f386",4283:"5eb90766",4397:"e9a1c9e5",4576:"0598cbc5",4699:"85531627",5041:"aa3c268d",5049:"74876495",5176:"402d2e48",5284:"6a6251fe",5391:"9281dd35",5466:"70066871",5998:"eb58dad4",6103:"ccc49370",6216:"68c71cca",6513:"d72ac48e",6933:"4c455ca7",7063:"ec3c7536",7414:"393be207",7431:"1c4bf583",7557:"928e06c2",7632:"0964aedb",7826:"8ee96214",7918:"17896441",7979:"118e913f",8364:"96e1810e",8610:"6875c492",8636:"f4f34a3a",8906:"1ba72f0d",9003:"925b3f96",9048:"e677b25a",9514:"1be78505",9521:"3c0fcc1c",9534:"3fb959cb",9642:"7661071f",9817:"14eb3368",9850:"dcc71bcc"}[e]||e)+"."+{53:"caa8fb63",114:"a7d9cf5e",210:"6e5e9f1a",788:"5ee92163",799:"b281447b",948:"74619e7a",1144:"4e4684a7",1199:"f73fc938",1368:"b3e32514",1495:"c9777f1c",1683:"3b7fe1d7",1761:"05f2b0af",1826:"a5334c36",1914:"b045544d",2267:"22a5d353",2362:"c362744b",2529:"cec79ce1",2535:"0c1d9999",2614:"bb78a4f4",2690:"7fc106b4",3085:"8fc7c9b3",3089:"845cad8c",3237:"20e07858",3514:"d45d5659",3608:"472c889f",4013:"75a76f22",4099:"1463edb8",4283:"49b2da36",4397:"f065f0d0",4576:"b9f9eae9",4699:"61a331f6",4972:"b60a5582",5041:"11260222",5049:"cd08c5dc",5176:"4e83c139",5284:"ed7cf9fb",5391:"056d9a3c",5466:"508587da",5998:"ce7e3bd0",6103:"d9c41d1e",6216:"704c8102",6513:"cd787f6d",6933:"946c6245",7063:"adf7bfc2",7414:"6ad33c11",7431:"58dbf46d",7557:"fee88172",7632:"f08e1154",7826:"92b136ff",7918:"4f945c03",7979:"b956ff9a",8364:"a859fe5f",8610:"f37b7b5c",8636:"77d55ebe",8906:"aa79666e",9003:"a62a82fe",9048:"4140a2d3",9514:"685933da",9521:"e76176e0",9534:"7f1cc7ba",9642:"77dfe874",9817:"a5d80bcb",9850:"e2c3cca1"}[e]+".js",b.miniCssF=e=>{},b.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),b.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),t={},r="docs:",b.l=(e,a,c,f)=>{if(t[e])t[e].push(a);else{var d,o;if(void 0!==c)for(var n=document.getElementsByTagName("script"),i=0;i{d.onerror=d.onload=null,clearTimeout(s);var r=t[e];if(delete t[e],d.parentNode&&d.parentNode.removeChild(d),r&&r.forEach((e=>e(c))),a)return a(c)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),o&&document.head.appendChild(d)}},b.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},b.p="/opentwins/",b.gca=function(e){return e={17896441:"7918",32191809:"2614",59362658:"2267",70066871:"5466",74876495:"5049",85531627:"4699","935f2afb":"53","908ba98b":"114",b0bae498:"788",c0eb0ada:"799","8717b14a":"948","40b0d055":"1144",ac75af2e:"1199","4899252d":"1368",d3d686ac:"1495","26bc6599":"1683",acac1da9:"1761","5a7456e0":"1826",d9f32620:"1914",e273c56f:"2362","814f3328":"2535","8dd02d2f":"2690","1f391b9e":"3085",a6aa9e1f:"3089","1df93b7f":"3237","73664a40":"3514","9e4087bc":"3608","01a85c17":"4013","8777f386":"4099","5eb90766":"4283",e9a1c9e5:"4397","0598cbc5":"4576",aa3c268d:"5041","402d2e48":"5176","6a6251fe":"5284","9281dd35":"5391",eb58dad4:"5998",ccc49370:"6103","68c71cca":"6216",d72ac48e:"6513","4c455ca7":"6933",ec3c7536:"7063","393be207":"7414","1c4bf583":"7431","928e06c2":"7557","0964aedb":"7632","8ee96214":"7826","118e913f":"7979","96e1810e":"8364","6875c492":"8610",f4f34a3a:"8636","1ba72f0d":"8906","925b3f96":"9003",e677b25a:"9048","1be78505":"9514","3c0fcc1c":"9521","3fb959cb":"9534","7661071f":"9642","14eb3368":"9817",dcc71bcc:"9850"}[e]||e,b.p+b.u(e)},(()=>{var e={1303:0,532:0};b.f.j=(a,c)=>{var t=b.o(e,a)?e[a]:void 0;if(0!==t)if(t)c.push(t[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var r=new Promise(((c,r)=>t=e[a]=[c,r]));c.push(t[2]=r);var f=b.p+b.u(a),d=new Error;b.l(f,(c=>{if(b.o(e,a)&&(0!==(t=e[a])&&(e[a]=void 0),t)){var r=c&&("load"===c.type?"missing":c.type),f=c&&c.target&&c.target.src;d.message="Loading chunk "+a+" failed.\n("+r+": "+f+")",d.name="ChunkLoadError",d.type=r,d.request=f,t[1](d)}}),"chunk-"+a,a)}},b.O.j=a=>0===e[a];var a=(a,c)=>{var t,r,f=c[0],d=c[1],o=c[2],n=0;if(f.some((a=>0!==e[a]))){for(t in d)b.o(d,t)&&(b.m[t]=d[t]);if(o)var i=o(b)}for(a&&a(c);n Blog | OpenTwins - +

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

· One min read
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/archive.html b/blog/archive.html index 1613924..4c6cb23 100644 --- a/blog/archive.html +++ b/blog/archive.html @@ -5,13 +5,13 @@ Archive | OpenTwins - + - + \ No newline at end of file diff --git a/blog/first-blog-post.html b/blog/first-blog-post.html index 924c770..9666c9a 100644 --- a/blog/first-blog-post.html +++ b/blog/first-blog-post.html @@ -5,13 +5,13 @@ First Blog Post | OpenTwins - +

First Blog Post

· One min read
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/long-blog-post.html b/blog/long-blog-post.html index d12dcd7..4ca0864 100644 --- a/blog/long-blog-post.html +++ b/blog/long-blog-post.html @@ -5,13 +5,13 @@ Long Blog Post | OpenTwins - +

Long Blog Post

· 3 min read
Endilie Yacop Sucipto

This is the summary of a very long blog post,

Use a <!-- truncate --> comment to limit blog post size in the list view.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/mdx-blog-post.html b/blog/mdx-blog-post.html index f6b1b9a..9a42cc6 100644 --- a/blog/mdx-blog-post.html +++ b/blog/mdx-blog-post.html @@ -5,13 +5,13 @@ MDX Blog Post | OpenTwins - +
- + \ No newline at end of file diff --git a/blog/tags.html b/blog/tags.html index 91179b4..45fc85d 100644 --- a/blog/tags.html +++ b/blog/tags.html @@ -5,13 +5,13 @@ Tags | OpenTwins - +
- + \ No newline at end of file diff --git a/blog/tags/docusaurus.html b/blog/tags/docusaurus.html index d69cf98..0620f7b 100644 --- a/blog/tags/docusaurus.html +++ b/blog/tags/docusaurus.html @@ -5,13 +5,13 @@ 4 posts tagged with "docusaurus" | OpenTwins - +

4 posts tagged with "docusaurus"

View All Tags

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

· One min read
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/tags/facebook.html b/blog/tags/facebook.html index f32146a..aafb4b0 100644 --- a/blog/tags/facebook.html +++ b/blog/tags/facebook.html @@ -5,13 +5,13 @@ One post tagged with "facebook" | OpenTwins - +

One post tagged with "facebook"

View All Tags

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- + \ No newline at end of file diff --git a/blog/tags/hello.html b/blog/tags/hello.html index 9949905..a10c93c 100644 --- a/blog/tags/hello.html +++ b/blog/tags/hello.html @@ -5,13 +5,13 @@ 2 posts tagged with "hello" | OpenTwins - +

2 posts tagged with "hello"

View All Tags

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- + \ No newline at end of file diff --git a/blog/tags/hola.html b/blog/tags/hola.html index ec72591..b8586ef 100644 --- a/blog/tags/hola.html +++ b/blog/tags/hola.html @@ -5,13 +5,13 @@ One post tagged with "hola" | OpenTwins - +

One post tagged with "hola"

View All Tags

· One min read
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/welcome.html b/blog/welcome.html index 45f997f..c5da54f 100644 --- a/blog/welcome.html +++ b/blog/welcome.html @@ -5,13 +5,13 @@ Welcome | OpenTwins - +

Welcome

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- + \ No newline at end of file diff --git a/docs/category/examples.html b/docs/category/examples.html index 0d15c14..8c60631 100644 --- a/docs/category/examples.html +++ b/docs/category/examples.html @@ -7,14 +7,14 @@ It is recommended using Postman to make all requests but youy can use your own method."> - + - + \ No newline at end of file diff --git a/docs/category/fmi-simulation.html b/docs/category/fmi-simulation.html index 2aaaa27..fa3599b 100644 --- a/docs/category/fmi-simulation.html +++ b/docs/category/fmi-simulation.html @@ -5,13 +5,13 @@ FMI Simulation | OpenTwins - + - + \ No newline at end of file diff --git a/docs/category/guides.html b/docs/category/guides.html index f2526e7..43f1d5a 100644 --- a/docs/category/guides.html +++ b/docs/category/guides.html @@ -5,13 +5,13 @@ Guides | OpenTwins - + - + \ No newline at end of file diff --git a/docs/category/installation.html b/docs/category/installation.html index 831506e..7a3b98d 100644 --- a/docs/category/installation.html +++ b/docs/category/installation.html @@ -5,13 +5,13 @@ Installation | OpenTwins - + - + \ No newline at end of file diff --git a/docs/category/overview.html b/docs/category/overview.html index df4f967..7f88037 100644 --- a/docs/category/overview.html +++ b/docs/category/overview.html @@ -5,13 +5,13 @@ Overview | OpenTwins - + - + \ No newline at end of file diff --git a/docs/category/raspberry-pi.html b/docs/category/raspberry-pi.html index 2be062a..6fd0c9d 100644 --- a/docs/category/raspberry-pi.html +++ b/docs/category/raspberry-pi.html @@ -5,13 +5,13 @@ Raspberry Pi | OpenTwins - + - + \ No newline at end of file diff --git a/docs/category/unity-visualization.html b/docs/category/unity-visualization.html index 7d07915..84dd291 100644 --- a/docs/category/unity-visualization.html +++ b/docs/category/unity-visualization.html @@ -5,13 +5,13 @@ Unity visualization | OpenTwins - + - + \ No newline at end of file diff --git a/docs/examples/ball-example.html b/docs/examples/ball-example.html index e9e0fd8..92f3786 100644 --- a/docs/examples/ball-example.html +++ b/docs/examples/ball-example.html @@ -5,13 +5,13 @@ Bouncing ball | OpenTwins - + - + \ No newline at end of file diff --git a/docs/examples/raspberry-example.html b/docs/examples/raspberry-example.html index 9e519b9..9e42513 100644 --- a/docs/examples/raspberry-example.html +++ b/docs/examples/raspberry-example.html @@ -5,7 +5,7 @@ Raspberry | OpenTwins - + @@ -14,7 +14,7 @@ A twin has two main components:

  • attributes. It contains the basic information of the twin, such as the name, location, etc.
  • features. It contains the variables of the twin. Imagine a twin of a sensor that measures humidity and temperature. You will have two features: humidity and temperature. Each feature must contain a field called properties that contains, as its name says, every property of the feature, for example, the value of the temperature and the time the value has been measured.

Once we know wich data will store our twin, it is time to create it. To create a twin, we need to make HTTP requests, we recommend you to use Postman. We need to create a PUT request to the Ditto url with the next pattern and a specific payload.

PUT http://{DITTO_IP}:{PORT}/api/2/things/{nameOfThing}

The payload has the attributes and features of the twin mentioned above. As attributes we have the location, in this case "Spain".

As features we have temperature and humidity. In this case both features has the same properties, value and timestamp, but they dont have to fit.

{
"attributes": {
"location": "Spain"
},
"features": {
"temperature": {
"properties": {
"value": null,
"timestamp": null
}
},
"humidity": {
"properties": {
"value": null,
"timestamp": null
}
}
}
}

Once we have checked that all the data is correct, just click send. You should recieve a 200 code of a correct execution.

To check if the twin has been created properly, just send a GET request to the same url.

GET http://{DITTO_IP}:{PORT}/api/2/things/{nameOfThing}

You should be granted with the schema of the new twin.

Second step. Recieving the data

A digital twin is a copy of a real object or process, but we just have a schema, so we need to feed it with data. To achieve this we can use both the Kafka or MQTT broker that are installed with the platform.

Ditto needs to recieve the data in a specific format called Ditto Protocol, so we need the data to be sent in that format. But don't worry if you recieve the data on other format, Ditto gives us the chance to create a mapping with Javascript to change the format when the data arrives to Ditto(We will always recommend you to send the data on Ditto protocol).

Asuming that we recieve that data in Ditto protocol we can configure the connection with one of the two brokers, Kafka or MQTT. To create a connection you can proceed with the same steps as creating the twins, make a POST request to the url and a payload that contains the connection information.

POST http://{DITTO_IP}:{PORT}/api/2/connections
  {
"name": "{NAME OF THE CONNECTION}",
"connectionType": "kafka",
"connectionStatus": "open",
"uri": "tcp://KAFKA_BROKER_IP",
"sources": [
{
"addresses": [
{"list Of topics to read"}
],
"consumerCount": 1,
"qos": 1,
"authorizationContext": [
"nginx:ditto"
],
"headerMapping": {
"correlation-id": "{{header:correlation-id}}",
"namespace": "{{ entity:namespace }}",
"content-type": "{{header:content-type}}",
"connection": "{{ connection:id }}",
"id": "{{ entity:id }}",
"reply-to": "{{header:reply-to}}"
},
"replyTarget": {
"address": "{{header:reply-to}}",
"headerMapping": {
"content-type": "{{header:content-type}}",
"correlation-id": "{{header:correlation-id}}"
},
"expectedResponseTypes": [
"response",
"error"
],
"enabled": true
}
}
],
"targets": [],
"clientCount": 5,
"failoverEnabled": true,
"validateCertificates": true,
"processorPoolSize": 1,
"specificConfig": {
"saslMechanism": "plain",
"bootstrapServers": "KAFKA_BROKER_IP"
},
"tags": []
}

Once we have checked that all the data is correct, just click send. You should recieve a 200 code of a correct execution.

To check if the twin has been created properly, just send a GET request to the same url adding the if of the new connection

GET http://{DITTO_IP}:{PORT}/api/2/connections/{connectionID}

You should be granted with the information of the connection.

With all this setup, the configuration should be already done, and Ditto should be recieving the data from the broker. If you want to create an example script to send the data, just click on the next link.

- + \ No newline at end of file diff --git a/docs/examples/raspberry-example/sending-data.html b/docs/examples/raspberry-example/sending-data.html index 06cdd2d..911b468 100644 --- a/docs/examples/raspberry-example/sending-data.html +++ b/docs/examples/raspberry-example/sending-data.html @@ -5,13 +5,13 @@ Sending data to Ditto | OpenTwins - +

Sending data to Ditto

In this case we will use a Raspberry Pi 3B with Raspbian buster OS connected to a DHT22 temperature and humidity sensor.

Setting up the Raspberry Pi

In the following image the pins of the Raspberry used are shown.

We will use pins 2, 6, 23 and 24.

Obtaining sensor data

To get the data from the sensor it is necessary to install its library.

sudo pip3 install Adafruit_DHT

We can test the operation of the sensor by creating a .py file with the following code (in our case it is called dht_code.py and I have placed it on the desktop).

import Adafruit_DHT
import time

SENSOR_DHT = Adafruit_DHT.DHT22
PIN_DHT = 24

while True:
humedad, temperatura = Adafruit_DHT.read(SENSOR_DHT, PIN_DHT)
if humedad is not None and temperatura is not None:
print("Temp={0:0.1f}C Hum={1:0.1f}%".format(temperatura, humedad))
else:
print("Lecture fails, chech connection");
time.sleep(3);

And we run it as follows:

cd Desktop/
python3 dht_code.py

Installing Mosquitto on Raspberry

To send the data to DITTO we will use MQTT with the Mosquitto broker.

sudo wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key
sudo apt-key add mosquitto-repo.gpg.key
cd /etc/apt/sources.list.d/
sudo wget http://repo.mosquitto.org/debian/mosquitto-buster.list
sudo -i
apt-get update
apt-get install mosquitto
apt-get install mosquitto-clients

With this we would already have Mosquitto installed on our Raspberry. To test it we can open two terminals, subscribe to a topic with one and publish to that topic with another.

mosquitto_sub -h localhost -t casa/comedor/temperatura
mosquitto_pub -h localhost -t casa/comedor/temperatura -m "Temperatura: 25ºC"

Configuring Mosquitto on raspberry

If we wanted to try to send and receive messages by MQTT between the raspberry and another device, we would have to configure the following.

  1. From the main route of the Raspberry edit the Mosquitto configuration file.
sudo nano /etc/mosquitto/mosquitto.conf
  1. Write these three lines at the end of the file to enable connections with any IP through port 1883 and configure authentication.
listener 1883 0.0.0.0

password_file /etc/mosquitto/passwd
allow_anonymous true

So that mosquito.conf would look like this:

# Place your local configuration in /etc/mosquitto/conf.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.gz

pid_file /run/mosquitto/mosquitto.pid

persistence true
persistence_location /var/lib/mosquitto/

log_dest file /var/log/mosquitto/mosquitto.log
log_type all
log_timestamp true

include_dir /etc/mosquitto/conf.d

listener 1883 0.0.0.0

password_file /etc/mosquitto/passwd
allow_anonymous true
  1. Save the file with Ctrl-O, Enter and Ctrl-X.
  2. Create a user with password using the following command. Replace USERNAME with the username you want. When you run it, it will ask you to enter a password, which will not be visible while you type it.
sudo mosquitto_passwd -c /etc/mosquitto/passwd USERNAME
  1. Restart Mosquitto with the following command:
sudo systemctl restart mosquitto

Finally, we would have Mosquitto configured to receive and send from other IPs. To do this you have to add -u "USERNAME" and -P "PASSWORD" (including quotes) to the respective command.

For example (in this case being user both the user and the password):

mosquitto_sub -h 192.168.0.27 -u "usuario" -P "usuario" -t "/Raspberry/Sensores/DHT22"

Sending data to MQTT from Raspberry

To work with MQTT in python we will need to make use of Eclipse Paho.

sudo pip3 install paho-mqtt

Now, we will create a .py file that publishes the sensor data in the corresponding topic of MQTT. For this we have adapted the code example exposed in the following link to the DHT22 sensor with the Adafruit_DHT library and the requirements of MQTT.

How to use MQTT in Python (Paho)

In addition, the message sent by MQTT regarding the Ditto Protocol has been made following both the documentation and an example of use.

Things - Create-Or-Modify protocol specification

  • Code to send sensor data to MQTT and Eclipse Ditto
    from paho.mqtt import client as mqtt_client
    import time
    import random
    import Adafruit_DHT
    import json

    #Constants to connect to MQTT
    broker = "IP OF MQTT"
    port = POR OF MQTT
    topic = "telemetry"
    client_id = f'python-mqtt-{random.randint(0, 1000)}'
    username = "raspberry_DHT22_1@ditto"
    password = "password"

    #Constantes para obtener información del sensor
    SENSOR_DHT = Adafruit_DHT.DHT22
    PIN_DHT = 24

    #Constantes para crear el mensaje de Eclipse Ditto
    DITTO_NAMESPACE = "raspberry";
    DITTO_THING_ID = "DHT22_1";

    def connect_mqtt():
    def on_connect(client, userdata, flags, rc):
    if rc == 0:
    print("Connected to MQTT Broker!")
    else:
    print("Failed to connect, return code %d\n", rc)
    # Set Connecting Client ID
    client = mqtt_client.Client(client_id)
    client.username_pw_set(username, password)
    client.on_connect = on_connect
    client.connect(broker, port)
    return client

    def publish(client):
    while True:
    time.sleep(1)
    msg = getValues();
    if msg is not None:
    result = client.publish(topic, msg)
    status = result[0]
    if status == 0:
    print(f"Send '{msg}' to topic '{topic}'")
    else:
    print(f"Failed to send message to topic {topic}")

    def getValues():
    humedad, temperatura = Adafruit_DHT.read(SENSOR_DHT, PIN_DHT)
    if humedad is not None and temperatura is not None:
    temp = "{0:0.1f}".format(temperatura)
    hum = "{0:0.1f}".format(humedad)
    output = "{\"topic\": \""
    output += DITTO_NAMESPACE
    output += "/"
    output += DITTO_THING_ID
    output += "/things/twin/commands/modify\",\"headers\":{\"response-required\":false, \"content-type\":\"application/vnd.eclipse.ditto+json\"},"
    output += "\"path\": \"/features\", \"value\":{"
    output += sensorString("temperature", temp)
    output += ","
    output += sensorString("humidity", hum)
    output += "}}"
    return output
    else:
    print("Failed on lecture, check circuit")
    return None

    def sensorString(name, value):
    return "\"" + name + "\": { \"properties\": { \"value\": " + value + "}}";

    def run():
    client = connect_mqtt()
    client.loop_start()
    publish(client)

    if __name__ == '__main__':
    run()

This code has been saved in a .py file with the name of dht22publisher.py and have saved it on the desktop. To execute it we use:

cd Desktop/
python3 dht22publisher.py
- + \ No newline at end of file diff --git a/docs/examples/string-example.html b/docs/examples/string-example.html index b2958ea..f184ceb 100644 --- a/docs/examples/string-example.html +++ b/docs/examples/string-example.html @@ -5,13 +5,13 @@ String and number | OpenTwins - +

String and number

This is a very simple example of creating a ONE way digital twin for monitoring a device. In this case, the stored information will be a string and a number, both of them have a timestamp asociated.

To create a digital twin, we must first know the schema used by Eclipse Ditto called Ditto Protocol.

- + \ No newline at end of file diff --git a/docs/fmi/API.html b/docs/fmi/API.html index 63b891b..76e5e84 100644 --- a/docs/fmi/API.html +++ b/docs/fmi/API.html @@ -5,13 +5,39 @@ API Documentation | OpenTwins - +
-

API Documentation

Welcome to the Example API documentation. This API allows developers to interact with our services easily and efficiently.

Table of Contents

Introduction

This API provides access to FMU data and allows for the creation and management of simulation schemas and simulations. It is based on REST and returns responses in JSON format.

Endpoints

FMU endpoints

MethodEndpointDescription
GET/fmi/fmus/{context}Retrieve a list of FMUs
POST/fmi/fmus/{context}Upload a new FMU
GET/fmi/fmus/{context}/{fmuName}Retrieve FMU information within a context
DELETE/fmi/fmus/{context}/{fmuName}Delete a FMU within a context

Schema endpoints

MethodEndpointDescription
GET/fmi/schemas/{context}Retrieve a list of schemas
POST/fmi/schemas/{context}Upload a new schema
GET/fmi/schemas/{context}/{schema_id}Retrieve a schema within a context
DELETE/fmi/schemas/{context}/{schema_id}Delete a schema within a context

Simulation endpoints

MethodEndpointDescription
GET/fmi/simulations/{context}Retrieve a list of running simulations
POST/fmi/simulations/{context}Deploy simulation
GET/fmi/simulations/{context}/{simulation_id}Retrieve simulation info within a context
DELETE/fmi/simulations/{context}/{simulation_id}Delete a simulation within a context
POST/fmi/simulations/{context}/{simulation_id}/resumeResume a specific simulation
POST/fmi/simulations/{context}/{simulation_id}/pauseStops a specific simulation
- +

API Documentation

Welcome to the Example API documentation. This API allows developers to interact with our services easily and efficiently.

Table of Contents

Introduction

This API provides access to FMU data and allows for the creation and management of simulation schemas and simulations. It is based on REST and returns responses in JSON format.

Endpoints

FMU endpoints

MethodEndpoint /fmi/fmusDescription
GET/{context}Retrieve a list of FMUs
POST/{context}Upload a new FMU
GET/{context}/{fmuName}Retrieve FMU information within a context
DELETE/{context}/{fmuName}Delete a FMU within a context

1. GET /{context}

Description

Get a list of FMUs in a specific context.

Request

Method: GET
+URL: /{context}

Response

Status code: 200 OK
+Response Body: A list of FMUs with information about their variables.

[
{
"id": "bouncingBallCS2",
"inputs": [],
"outputs": [],
"other_variables": [
{
"name": "h",
"type": "Real",
"default": {
"start": "1"
},
"description": "height, used as state"
},
{
"name": "v",
"type": "Real",
"default": {
"start": "0",
"reinit": "true"
},
"description": "velocity of ball, used as state"
},
{
"name": "g",
"type": "Real",
"default": {
"start": "9.81"
},
"description": "acceleration of gravity"
},
{
"name": "e",
"type": "Real",
"default": {
"start": "0.7",
"min": "0.5",
"max": "1"
},
"description": "dimensionless parameter"
}
]
}
]

2. POST /{context}

Description

Upload a new FMU to the sistem into a specific context.

Request

Method: POST
+URL: /{context}

Response

Status code: 200 OK
+Response Body:

"FMU uploaded succesfully"

3. GET /{context}/{fmuName}

Description

Get the XML file of a specific FMU.

Request

Method: GET
+URL: /{context}/{fmuName}

Response

Status code: 200 OK
+Response Body:

<?xml version="1.0" encoding="ISO-8859-1"?>
<fmiModelDescription
fmiVersion="2.0"
modelName="bouncingBall"
guid="{8c4e810f-3df3-4a00-8276-176fa3c9f003}"
numberOfEventIndicators="1">
<CoSimulation
modelIdentifier="bouncingBall"
canHandleVariableCommunicationStepSize="true"/>
<LogCategories>
<Category name="logAll"/>
<Category name="logError"/>
<Category name="logFmiCall"/>
<Category name="logEvent"/>
</LogCategories>
<ModelVariables>
<ScalarVariable name="h" valueReference="0" description="height, used as state"
causality="local" variability="continuous" initial="exact">
<Real start="1"/>
</ScalarVariable>
<ScalarVariable name="der(h)" valueReference="1" description="velocity of ball"
causality="local" variability="continuous" initial="calculated">
<Real derivative="1"/>
</ScalarVariable>
<ScalarVariable name="v" valueReference="2" description="velocity of ball, used as state"
causality="local" variability="continuous" initial="exact">
<Real start="0" reinit="true"/>
</ScalarVariable>
<ScalarVariable name="der(v)" valueReference="3" description="acceleration of ball"
causality="local" variability="continuous" initial="calculated">
<Real derivative="3"/>
</ScalarVariable>
<ScalarVariable name="g" valueReference="4" description="acceleration of gravity"
causality="parameter" variability="fixed" initial="exact">
<Real start="9.81"/>
</ScalarVariable>
<ScalarVariable name="e" valueReference="5" description="dimensionless parameter"
causality="parameter" variability="tunable" initial="exact">
<Real start="0.7" min="0.5" max="1"/>
</ScalarVariable>
</ModelVariables>
<ModelStructure>
<Derivatives>
<Unknown index="2" />
<Unknown index="4" />
</Derivatives>
<InitialUnknowns>
<Unknown index="2"/>
<Unknown index="4"/>
</InitialUnknowns>
</ModelStructure>
</fmiModelDescription>

4. DELETE /{context}/{fmuName}

Description

Delete a FMU from the sistem in a specific context.

Request

Method: DELETE
+URL: /{context}/{fmuName}

Response

Status code: 200 OK
+Response Body:

"FMU deleted succesfully"

Schema endpoints

MethodEndpoint /fmi/schemasDescription
GET/{context}Retrieve a list of schemas
POST/{context}Upload a new schema
GET/{context}/{schema_id}Retrieve a schema within a context
DELETE/{context}/{schema_id}Delete a schema within a context

1. GET /{context}

Description

Get all simulation schemas in a specific context.

Request

Method: GET
+URL: /{context}

Response

Status code: 200 OK
+Response Body: A list of name and id of every single simulation schemas.

[
{
"id": "schema1",
"name": "Schema 1"
}
]

2. POST /{context}

Description

Create a new schema in a specific context.

Request

Method: POST
+URL: /{context}

Response

Status code: 200 OK
+Response Body: A list of name and id of every single simulation schemas.

"Schema created succesfully"

3. GET /{context}/{schema_id}

Description

Get a schema in a specific context.

Request

Method: GET
+URL: /{context}/{schema_id}

Response

Status code: 200 OK
+Response Body:

[
{
"id": "schema2",
"fmus": [
{
"id": "Controller",
"inputs": [
{
"id": "u_s"
},
{
"id": "u_m"
}
],
"outputs": [
{
"id": "y"
}
]
},
{
"id": "Drivetrain",
"inputs": [
{
"id": "tau"
}
],
"outputs": [
{
"id": "w"
}
]
}
],
"name": "Schema 2",
"schema": [
{
"to": {
"id": "Controller",
"var": "u_s"
},
"from": {
"var": "w_ref"
}
},
{
"to": {
"id": "Controller",
"var": "u_m"
},
"from": {
"id": "Drivetrain",
"var": "w"
}
},
{
"to": {
"id": "Drivetrain",
"var": "tau"
},
"from": {
"id": "Controller",
"var": "y"
}
},
{
"to": {
"var": "w"
},
"from": {
"id": "Drivetrain",
"var": "w"
}
}
],
"description": "Testing schema",
"relatedTwins": [
"Twin1"
]
}
]

4. DELETE /{context}/{schema_id}

Description

Delete a schema in a specific context.

Request

Method: DELETE
+URL: /{context}/{schema_id}

Response

Status code: 200 OK
+Response Body:

"Schema deleted succesfully"

Simulation endpoints

MethodEndpoint /fmi/simulationsDescription
GET/{context}Retrieve a list of running simulations
POST/{context}Deploy simulation
GET/{context}/{simulation_id}Retrieve simulation info within a context
DELETE/{context}/{simulation_id}Delete a simulation within a context
POST/{context}/{simulation_id}/resumeResume a specific simulation
POST/{context}/{simulation_id}/pauseStops a specific simulation

1. GET /{context}

Description

Get all running simulations in a specific context.

Request

Method: GET
+URL: /{context}

Response

Status code: 200 OK
+Response Body: A list of information of every single running simulations.


[
{
"schema-id": "schema1",
"simulation-id": "pruebabouncingball",
"namespace": "opentwins",
"type": "one-time",
"status": "Active",
"pods": [
{
"simulation-id": "pruebabouncingball",
"phase": "Running",
"status": false,
"creation_timestamp": "2024/09/26, 03:06:06+0000"
}
]
}
]

2. POST /{context}

Description

Create a new simulation using a existing schema in a specific context.

Request

Method: POST
+URL: /{context}

Response

Status code: 200 OK
+Response Body:

"true"

3. GET /{context}/{simulation_id}

Description

Get all information about a specific simulation in a specific context.

Request

Method: GET
+URL: /{context}/{simulation_id}

Response

Status code: 200 OK
+Response Body:

{
"api_version": "batch/v1",
"kind": "Job",
"metadata": {
"annotations": null,
"creation_timestamp": "2024-09-26 03:06:06+00:00",
"deletion_grace_period_seconds": null,
"deletion_timestamp": null,
.
.
.
}
}

4. DELETE /{context}/{simulation_id}

Description

Delete specific simulation in a specific context.

Request

Method: DELETE
+URL: /{context}/{simulation_id}

Response

Status code: 200 OK
+Response Body:

"Schema deleted succesfully"

5. POST /{context}/{simulation_id}/resume

Description

Resume paused simulation in a specific context.

Request

Method: POST
+URL: /{context}/{simulation_id}/resume

Response

Status code: 200 OK

6. POST /{context}/{simulation_id}/pause

Description

Pause simulation in a specific context.

Request

Method: POST
+URL: /{context}/{simulation_id}/pause

Response

Status code: 200 OK

+ \ No newline at end of file diff --git a/docs/fmi/concepts.html b/docs/fmi/concepts.html index 1a5a3b6..eb7b1a8 100644 --- a/docs/fmi/concepts.html +++ b/docs/fmi/concepts.html @@ -5,13 +5,13 @@ FMI Simulation concepts | OpenTwins - +

FMI Simulation concepts

danger

The FMI simulation service is currently being tested.

Simulation schema

The schema is used to create simulation blueprints to store and create several simulation instances.

The schema can be create for a single FMU or several FMUs. The schema for a single FMU is:

{
"id":"schema1",
"name":"Schema 1",
"description":"Sample schema",
"relatedTwins":[
"Twin1"
],
"fmus":[
{
"id": "Controller",
"inputs": [
{"id": "u_s"},
{"id": "u_m"}
],
"outputs": [
{"id": "y"}
]
}
]
}
Schema for several FMUs

The following schema is designed to be used with multiple FMUs:

{
"id":"schema1",
"name":"Schema 1",
"description":"Sample schema",
"relatedTwins":[
"Twin1"
],
"fmus":[
{
"id": "Controller",
"inputs": [
{"id": "u_s"},
{"id": "u_m"}
],
"outputs": [
{"id": "y"}
]
},
{
"id": "Drivetrain",
"inputs": [
{"id": "tau"}
],
"outputs": [
{"id": "w"}
]
}
],
"schema":[
{
"from": {"var": "w_ref"},
"to": {"id": "controller", "var": "u_s"}
},
{
"from": {"id": "drivetrain", "var": "w"},
"to": {"id": "controller", "var": "u_m"}
},
{
"from": {"id": "controller", "var": "y"},
"to": {"id": "drivetrain", "var": "tau"}
},
{
"from": {"id": "drivetrain", "var": "w"},
"to": {"var": "w"}
}
]
}

This will work properly, although hovering overApparentGreetPropsmay be a little intimidating. You can reduce this boilerplate with theComponentProps utility detailed below.

Simulation running schema

Once you have a schema stored in the system, you can create a simulation instance using that schema:

{
"id":"Simulation1",
"name":"Simulation1",
"schemaId": "schema1",
"targetConnection":{
"BROKER_TYPE" : "mqtt",
"BROKER_IP" : "",
"BROKER_PORT" : "",
"BROKER_TOPIC" : "",
"BROKER_USERNAME" : "",
"BROKER_PASSWORD" : ""
},
"configuration":{
"SIMULATION_START_TIME":1,
"SIMULATION_END_TIME":7,
"SIMULATION_STEP_SIZE":1,
"SIMULATION_DELAY_WARNING": 1,
"SIMULATION_LAST_VALUE": true,
"SIMULATION_TYPESCHEDULE": "one-time"
},
"inputs":[],
"outputs": []
}

This schema is not different for one or several FMUs execution. It only contains information about execution.

- + \ No newline at end of file diff --git a/docs/fmi/installation.html b/docs/fmi/installation.html index caad8a9..9ee8e75 100644 --- a/docs/fmi/installation.html +++ b/docs/fmi/installation.html @@ -5,13 +5,13 @@ Installation Guide | OpenTwins - +

Installation Guide

danger

The FMI simulation service is currently being tested, so please be patient, as soon as it is properly tested, the public image will be available on Docker Hub.

This guide explains how to install the component using two methods:

  1. Helm (Work in Progress)
  2. Manual Installation using Kubernetes manifests (Deployment and Service)

Prerequisites

This guide asumes that you have OpenTwins already installed.

Before you begin, ensure you have the following:

  • Access to a Kubernetes cluster
  • kubectl installed and configured
  • Helm (for Helm installation)

Method 1: Helm Installation (WIP)

danger

This method is currently a Work in Progress (WIP) and may not be fully functional yet. We recomend using manual installation.

  1. Add the Helm repository (once available):

    helm repo add ertis https://ertis-research.github.io/Helm-charts/
  2. Update Helm repositories:

    helm repo update
  3. Install the component:

    helm install <release-name> <chart-name> --namespace <namespace>

For additional configuration options, refer to the Helm documentation.


Method 2: Manual Installation

You can manually deploy the component by creating a Kubernetes Deployment resource and a Service.

Step 1: Deploy the Kubernetes Deployment

Create a YAML file for the Deployment (e.g., deployment.yaml):


apiVersion: apps/v1
kind: Deployment
metadata:
labels:
name: opentwins-fmi-api
name: opentwins-fmi-api
spec:
replicas: 1
selector:
matchLabels:
name: pod-opentwins-fmi-api
template:
metadata:
labels:
name: pod-opentwins-fmi-api
name: opentwins-fmi-api
spec:
serviceAccountName: ot-agents
automountServiceAccountToken: true
containers:
- image: ertis/opentwins-fmi-simulator-api-v2:latest
name: opentwins-fmi-api
env:
- name: KUBE_NAMESPACE
value:
- name: INSIDE_CLUSTER
value:
- name: INFLUXDB_HOST
value:
- name: INFLUXDB_TOKEN
value:
- name: INFLUXDB_DB
value:
- name: MINIO_TOKEN
value:
- name: MINIO_URL
value:
- name: MINIO_A_KEY
value:
- name: MINIO_S_KEY
value:
- name: POSTGRE_HOST
value:
- name: POSTGRE_PORT
value:
- name: POSTGRE_DB
value:
- name: POSTGRE_USER
value:
- name: POSTGRE_PASSWORD
value:
- name: BROKER_TYPE
value:
- name: BROKER_IP
value:
- name: BROKER_PORT
value:
- name: BROKER_TOPIC
value:
- name: BROKER_USERNAME
value:
- name: BROKER_PASSWORD
value:
ports:
- containerPort: 8001
imagePullPolicy: Always

Apply the Deployment using the following command:

kubectl apply -f deployment.yaml -n <namespace>

Step 2: Create a Kubernetes Service

Next, create a YAML file for the Service (e.g., service.yaml):

apiVersion: v1
kind: Service
metadata:
name: opentwins-fmi-api
spec:
selector:
name: pod-opentwins-fmi-api
type: NodePort
ports:
- protocol: "TCP"
port: 8000
nodePort: <PORT>
targetPort: 8000

Apply the Service configuration using the following command:

kubectl apply -f service.yaml -n <namespace>

Step 3: Verify the installation

After deploying both the deployment and the service, veryfy that everything is running correctly:

kubectl get deployments -n <namespace>
kubectl get services -n <namespace>

You should see your Deployment and Service listed, and the component should be ready for use.

- + \ No newline at end of file diff --git a/docs/guides/dt-schema-creation.html b/docs/guides/dt-schema-creation.html index 2578745..db87372 100644 --- a/docs/guides/dt-schema-creation.html +++ b/docs/guides/dt-schema-creation.html @@ -5,7 +5,7 @@ Create a digital twin | OpenTwins - + @@ -13,7 +13,7 @@

Create a digital twin

The way to interact with Eclipse Ditto and therefore create not only digital twins, but connections, etc. is through http requests and methods. Although the graphical interface of OpenTwins makes it unnecessary to go so low level, the option to communicate directly with Eclipse Ditto is still available.

To create a new digital twin schema using OpenTwins plugin in Grafana just select "Create new twin" button in Twins tab. CreateTwin

- + \ No newline at end of file diff --git a/docs/guides/type-creation.html b/docs/guides/type-creation.html index 5dfe022..6191db7 100644 --- a/docs/guides/type-creation.html +++ b/docs/guides/type-creation.html @@ -5,7 +5,7 @@ Create a type | OpenTwins - + @@ -13,7 +13,7 @@

Create a type

The way to interact with Eclipse Ditto and therefore create not only digital twins, but connections, etc. is through http requests and methods. Although the graphical interface of OpenTwins makes it unnecessary to go so low level, the option to communicate directly with Eclipse Ditto is still available.

As explained in TWINS WIP, OpenTwins has two types of DT schemas. One for creating a single DT and other for creating a type to create multiple instances of a DT.

To create a new DT type using OpenTwins plugin in Grafana, just select "Create new type in" button in "Types" tab. CreateType

A new window with a form that will define the DT and a viewer of the produced JSON schema will have appeared.

The first required information is the identification of the twin. There are two required field.

  • Namespace: Is the name of the context to which the type belongs.
  • ID: This must be unique within the scope of the type. The name of the type will precede it automatically.

Identification

Next is type information. This basic static information about the type for description. There are several fields, but just one is required:

  • Policy* : We must select a policy.
  • Name.
  • Description.
  • Image: You can paste a image url to show in the type information.

Information

In addition to the above information, new custom attributes can be defined, normally used as static information. By simply filling in the attribute name and its value, click on the "add" button to add a new attribute.

Attributes

Finally, the features section is used to create the variables to be collected by the DT. Simply type the name and click on the "add" button. This will add a new variable to the twin schema.

Features

An example of a schema of a DT of an abstract vehicle can be seen in the following JSON:

{
"thingId": "benchmark:vehicle",
"policyId": "default:basic_policy",
"attributes": {
"name": "Vehicle",
"description": "Vehicle type for generating new vehicles.",
"image": "ImageLink",
"Brand": "EMPTY",
"Subtype": "EMPTY"
},
"features": {
"wheels": {
"properties": {
"value": null
}
},
"power": {
"properties": {
"value": null
}
},
"capacity": {
"properties": {
"value": null
}
}
}
}

- + \ No newline at end of file diff --git a/docs/guides/unity/creating-unity-build.html b/docs/guides/unity/creating-unity-build.html index 380d6f6..ca2fd88 100644 --- a/docs/guides/unity/creating-unity-build.html +++ b/docs/guides/unity/creating-unity-build.html @@ -5,7 +5,7 @@ Creation of Unity WebGL build | OpenTwins - + @@ -18,7 +18,7 @@ First step is to create a .jslib file inside Assets/Plugins/WebGL, where Assets is our project Assets folder. It is very important that the folders have these names (if one does not exist, it is necessary to create it) because otherwise the plugin will not work. The following script must be added into the created file:


mergeInto(LibraryManager.library, {
GetData: function (deviceId) {
dispatchReactUnityEvent(
"GetData",
Pointer_stringify(deviceId));
}
});

Once created the .jslib file, we need to add the following code to a script in Unity inside a class but outside Start() and Update() funcions:

#if UNITY_WEBGL && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void GetData(string deviceId);
#else
private static void GetData(string deviceId){
Debug.Log("ERROR");
}
#endif

By adding it, we are enabling the execution of events just by calling GetData() function. For example the following script, sends the name of the clicked item in Unity as event to be catched by Grafana:


using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;

public class ObjectClicker : MonoBehaviour
{
#if UNITY_WEBGL && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void GetData(string deviceId);
#else
private static void GetData(string deviceId){
Debug.Log("ERROR");
}
#endif

// Update is called once per frame
void Update(){
if (Input.GetMouseButtonDown(0)){
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

if (Physics.Raycast(ray, out hit, 100.0f)){
if (hit.transform != null){
GameObject go = hit.transform.gameObject;
SendEventWithId(go);
}
}
}
}

void SendEventWithId(GameObject go){
if(go != null){
Debug.Log("Nombre del objeto: " + go.name);
GetData(go.name);
Debug.Log("Mensaje enviado");
}
else{
Debug.Log("nulo");
}
}
}

- + \ No newline at end of file diff --git a/docs/guides/unity/using-grafana-plugin.html b/docs/guides/unity/using-grafana-plugin.html index 766e06d..0c50cce 100644 --- a/docs/guides/unity/using-grafana-plugin.html +++ b/docs/guides/unity/using-grafana-plugin.html @@ -5,13 +5,13 @@ Using Grafana Unity Plugin | OpenTwins - + - + \ No newline at end of file diff --git a/docs/installation/manual.html b/docs/installation/manual.html index 8bc5077..fc673a0 100644 --- a/docs/installation/manual.html +++ b/docs/installation/manual.html @@ -5,13 +5,13 @@ Manual | OpenTwins - +

Manual

danger

The documentation of this method is being written right now. We recommend using helm installation.

This section will explain how to deploy the platform manually. Basically, you will have to deploy or install the different components and then connect them. The procedure explained below is the one followed to deploy them in Kubernetes using in most cases the Helm option, but any other installation in which all the components are correctly installed and there is some kind of network between them to be able to communicate can be used.

It is not necessary to deploy all components if not all functionalities are to be used. Check the architecture section to find out which ones are essential and what functionality is covered by each of them.

Essential functionality

Deploy

tip

Note that the values files have the variables that we recommend for the installation of each Helm Chart, but they can be extended or modified according to your needs (please consult the Helm Chart documentation for each component).

We recommend installing all components in the same Kubernetes namespace to make it easier to identify and control them all. In our case the namespace will be opentwins.

kubectl create namespace opentwins

We installed all the components with their Helm versions and kept most of the values in their default configuration, except for those that are important for the interconnection of the components. In addition, we configure the services as NodePort to facilitate external access and set a specific port for each one.

danger

Depending on how you have persistence configured in your cluster, you may need to deploy persistent volumes for MongoDB, InfluxDB and Grafana. The values for MongoDB are shown below, but they all follow the same template.

apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-opentwins-mongodb
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 8Gi
hostPath:
path: /mnt/opentwins/mongodb
type: DirectoryOrCreate

Listed below are the essential components of the architecture along with their versions used, their Helm values and a link to the repository explaining their installation.

MongoDB v6.0

helm install mongodb -n opentwins oci://registry-1.docker.io/bitnamicharts/mongodb --version 13.8.3 -f values.yaml
values.yaml
service:
type: NodePort
nodePorts:
mongodb: 30717
persistence:
enabled: true
volumePermissions:
enabled: true
auth:
enabled: false

Eclipse Ditto v3.3

helm install --dependency-update -n opentwins ditto oci://registry-1.docker.io/eclipse/ditto --version 3.3.7 --wait -f values.yaml
danger
  • We advise not to modify any authentication configuration due to a bug in Eclipse Ditto that may cause access errors.
  • In the following values you have to replace mongodb-service-name by the MongoDB service name
values.yaml
global:
hashedBasicAuthUsers: false
basicAuthUsers:
ditto:
user: ditto
password: ditto
devops:
user: devops
password: foobar
nginx:
service:
type: NodePort
nodePort: 30525
swaggerui:
enabled: false
dittoui:
enabled: false
mongodb:
enabled: false
dbconfig:
policies:
uri: 'mongodb://<mongodb-service-name>:27017/ditto'
things:
uri: 'mongodb://<mongodb-service-name>:27017/ditto'
connectivity:
uri: 'mongodb://<mongodb-service-name>:27017/ditto'
thingsSearch:
uri: 'mongodb://<mongodb-service-name>:27017/ditto'
gateway:
config:
authentication:
enablePreAuthentication: true
devops:
devopsPassword: foobar
statusPassword: foobar

InfluxDB v2

helm repo add influxdata https://helm.influxdata.com/
helm repo update
helm install -n opentwins influxdb influxdata/influxdb2 --version 2.1.1 -f values.yaml
values.yaml
persistence:
enabled: true
service:
type: NodePort
nodePort: 30716
image:
pullPolicy: Always

Mosquitto v2.0

tip

OpenTwins supports the use of Mosquitto and Kafka as intermediaries, but we recommend using Mosquitto due to its simpler configuration. Since there is no official Helm chart for Mosquitto, we have created one of our own that works fine, although there is no documentation yet. However, you can install Mosquitto in any of the available ways.

helm repo add ertis https://ertis-research.github.io/Helm-charts/
helm repo update
helm install mosquitto ertis/mosquitto -n opentwins --wait --dependency-update -f values.yaml
values.yaml
service:
type: NodePort
nodePort: 30511
configuration:
authentication:
enabled: false

Apache Kafka v3.4

helm install kafka oci://registry-1.docker.io/bitnamicharts/kafka --version 22.0.0 -f values.yaml
values.yaml
autoCreateTopicsEnable: true

Grafana v9.5

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install grafana grafana/grafana -n opentwins --version 6.56.1 -f values.yaml
values.yaml
persistence:
enabled: true
service:
type: NodePort
nodePort: 30718
grafana.ini:
plugins:
plugin_admin_enabled: true
allow_loading_unsigned_plugins: 'ertis-opentwins,ertis-unity-panel'
extraInitContainers:
- name: install-opentwins-plugins
image: busybox
command:
- /bin/sh
- -c
- |
#!/bin/sh
set -euo pipefail
mkdir -p /grafana-storage/plugins
cd /grafana-storage/plugins
wget --no-check-certificate -O ertis-opentwins.zip https://github.com/ertis-research/opentwins-in-grafana/releases/download/latest/ertis-opentwins.zip
unzip -o ertis-opentwins.zip
rm ertis-opentwins.zip
wget --no-check-certificate -O ertis-unity-panel.zip https://github.com/ertis-research/grafana-panel-unity/releases/download/latest/ertis-unity-panel.zip
unzip -o ertis-unity-panel.zip
rm ertis-unity-panel.zip
volumeMounts:
- name: storage
mountPath: /grafana-storage

Eclipse Hono v2.4

danger

This component is completely optional. We maintain support for its connection to OpenTwins, but we do not recommend its use. For a large number of devices or messages it increases considerably the latency of the platform.

helm repo add eclipse-iot https://eclipse.org/packages/charts
helm repo update
helm install hono eclipse-iot/hono -n opentwins -f values.yaml --version=2.5.5
values.yaml
prometheus:
createInstance: false
grafana:
enabled: false
useLoadBalancer: false
probes:
livenessProbe:
initialDelaySeconds: 900
readinessProbe:
initialDelaySeconds: 45
messagingNetworkTypes:
- amqp
kafkaMessagingClusterExample:
enabled: false
amqpMessagingNetworkExample:
enabled: true
deviceRegistryExample:
type: mongodb
addExampleData: false
mongoDBBasedDeviceRegistry:
mongodb:
host: '{{ .Release.Name }}-mongodb'
port: 27017
dbName: hono
hono:
registry:
http:
insecurePortEnabled: true
adapters:
mqtt:
hono:
mqtt:
insecurePortEnabled: true
http:
hono:
http:
insecurePortEnabled: true
amqp:
hono:
amqp:
insecurePortEnabled: true

Connect

tip

Check architecture to see which connections you need to set up

Eclipse Ditto and InfluxDB

The process to connect Eclipse Ditto and InfluxDB will depend on Mosquitto or Apache Kafka. Choose the option you have selected in each step.

  1. You have to add an output connection in Eclipse Ditto that publishes the events of the twins in the intermediary. This is done with a POST request to the URL http://DITTO_NGINX_URL/api/2/connections with the following body and the basic credentials: user "devops" and password "foobar". Remember to replace DITTO_NGINX_URL by a URL that allows access to the Eclipse Ditto Nginx service, you can check how to do it here.

    You can check if the connection is working properly by reading the opentwins topic in the selected broker with some tool or script and sending updates to some twin in Ditto Protocol format. To create the twin check here and to see an example of an update message check here.

danger

Change MOSQUITTO_SERVICE_NAME to the name of the Mosquitto service. You can check it with kubectl get services.

PUT http://DITTO_NGINX_URL/api/2/connections
{
"name": "mosquitto-target-connection",
"connectionType": "mqtt-5",
"connectionStatus": "open",
"uri": "tcp://MOSQUITTO_SERVICE_NAME:1883",
"clientCount": 1,
"failoverEnabled": true,
"sources": [],
"targets": [
{
"address": "opentwins/{{ topic:channel }}/{{ topic:criterion }}/{{ thing:namespace }}/{{ thing:name }}",
"topics": [
"_/_/things/twin/events?extraFields=thingId,attributes/_parents,features/idSimulationRun/properties/value",
"_/_/things/live/messages",
"_/_/things/live/commands"
],
"qos": 1,
"authorizationContext": [
"nginx:ditto"
]
}
]
}
  1. Now we will need to obtain a token in InfluxDB with write permissions. We will then access from a browser to the InfluxDB interface and create an opentwins organization. Then, follow the instructions in their documentation to create an API token in the organization. Save this token because we will use it next.

  2. An instance of Telegraf must be deployed to read the events written in the intermediary broker and write them to the database. For this we will use the Telegraf Helm and add the necessary configuration in its values. You can check to Telegraf v1 documentation for both the application and Helm for more information.

    The commands to deploy it are the following, using the necessary values file in each case.

helm repo add influxdata https://helm.influxdata.com/
helm repo update
helm install -n opentwins telegraf influxdata/telegraf -f values.yaml --version=1.8.27 --set tplVersion=2
values.yaml
service:
enabled: false
config:
agent:
debug: true
processors:
- rename:
replace:
- tag: "extra_attributes__parents"
dest: "parent"
- tag: "headers_ditto-originator"
dest: "originator"
- tag: "extra_features_idSimulationRun_properties_value"
dest: "idSimulationRun"
- tag: "extra_thingId"
dest: "thingId"
outputs:
- influxdb_v2:
urls:
- "http://INFLUX_SERVICE_NAME:INFLUX_PORT"
token: "INFLUXDB_TOKEN"
organization: "opentwins"
bucket: "default"
inputs:
- mqtt_consumer:
servers:
- "tcp://MOSQUITTO_SERVICE_NAME:1883"
topics:
- "opentwins/#"
qos: 1
tag_keys:
- "extra_attributes__parents"
- "extra_thingId"
- "headers_ditto-originator"
- "extra_features_idSimulationRun_properties_value"
- "value_time_properties_value"
data_format: "json"
metrics:
internal:
enabled: false

With this Eclipse Ditto and InfluxDB should be connected. You can check this by sending update messages to Eclipse Ditto and verifying if they are correctly written to the InfluxDB bucket. If not, check if the messages are arriving correctly to the intermediate broker and, if so, check the logs of the Telegraf pod to see if there is any error in the configuration (usually connection problems).

InfluxDB and Grafana

  1. Obtain a read access token in InfluxDB for Grafana.
  2. Access Configuration > Data sources on the Grafana interface and click on Add data source.
  3. Select InfluxDB from the list. In the setup form it is very important to select Flux as query language. It will be necessary to fill in the URL section with the one that corresponds to InfluxDB service. You will also have to activate Auth Basic and fill in the fields (in our case we have set the default admin of InfluxDB, but you can create a new user and fill in these fields). In the InfluxDB details you should indicate the organization, the bucket (default is default) and the token you have generated.
  4. When saving and testing, it should come out that at least one bucket has been found, indicating that they are already connected.

Eclipse Ditto and Eclipse Hono

In the following diagram you can see how Eclipse Hono and Eclipse Ditto are related in OpenTwins.

Ditto and Hono relationship

Basically, you will need to create a connection between both for each Eclipse Hono tenant you want to use. Tenants basically act as device containers, so you could simply create a single tenant connected to Eclipse Ditto and store all the devices you need there. In this case we will do it this way, but you could create as many tenants and connections as your needs require.

The first thing to do is to check the IPs and ports to use with kubectl get services -n $NS. At this point we are interested in the dt-service-device-registry-ext and dt-ditto-nginx services, which correspond to Eclipse Hono and Eclipse Ditto respectively (if you have followed these instructions and services are NodePort, you will have to use port 3XXXX).

We will then create a Hono tenant called, for example, ditto (you must override the variable HONO_TENANT if you have chosen another name).

HONO_TENANT=ditto
curl -i -X POST http://$HONO_IP:$HONO_PORT/v1/tenants/$HONO_TENANT

Now we will create the connection from Eclipse Ditto, which will act as a consumer of the AMQP endpoint of that tenant. To do this you will need to know the Eclipse Ditto devops password with the following command (the variable RELEASE is the name we gave to the Helm release when installing cloud2edge, if you have followed these instructions it should be dt).

RELEASE=dt
DITTO_DEVOPS_PWD=$(kubectl --namespace ${NS} get secret ${RELEASE}-ditto-gateway-secret -o jsonpath="{.data.devops-password}" | base64 --decode)

Now we create the connection from Eclipse Ditto with the following command.

curl -i -X POST -u devops:${DITTO_DEVOPS_PWD} -H 'Content-Type: application/json' --data '{
"targetActorSelection": "/system/sharding/connection",
"headers": {
"aggregate": false
},
"piggybackCommand": {
"type": "connectivity.commands:createConnection",
"connection": {
"id": "hono-connection-for-'"${HONO_TENANT}"'",
"connectionType": "amqp-10",
"connectionStatus": "open",
"uri": "amqp://consumer%40HONO:verysecret@'"${RELEASE}"'-dispatch-router-ext:15672",
"failoverEnabled": true,
"sources": [
{
"addresses": [
"telemetry/'"${HONO_TENANT}"'",
"event/'"${HONO_TENANT}"'"
],
"authorizationContext": [
"pre-authenticated:hono-connection"
],
"enforcement": {
"input": "{{ header:device_id }}",
"filters": [
"{{ entity:id }}"
]
},
"headerMapping": {
"hono-device-id": "{{ header:device_id }}",
"content-type": "{{ header:content-type }}"
},
"replyTarget": {
"enabled": true,
"address": "{{ header:reply-to }}",
"headerMapping": {
"to": "command/'"${HONO_TENANT}"'/{{ header:hono-device-id }}",
"subject": "{{ header:subject | fn:default(topic:action-subject) | fn:default(topic:criterion) }}-response",
"correlation-id": "{{ header:correlation-id }}",
"content-type": "{{ header:content-type | fn:default('"'"'application/vnd.eclipse.ditto+json'"'"') }}"
},
"expectedResponseTypes": [
"response",
"error"
]
},
"acknowledgementRequests": {
"includes": [],
"filter": "fn:filter(header:qos,'"'"'ne'"'"','"'"'0'"'"')"
}
},
{
"addresses": [
"command_response/'"${HONO_TENANT}"'/replies"
],
"authorizationContext": [
"pre-authenticated:hono-connection"
],
"headerMapping": {
"content-type": "{{ header:content-type }}",
"correlation-id": "{{ header:correlation-id }}",
"status": "{{ header:status }}"
},
"replyTarget": {
"enabled": false,
"expectedResponseTypes": [
"response",
"error"
]
}
}
],
"targets": [
{
"address": "command/'"${HONO_TENANT}"'",
"authorizationContext": [
"pre-authenticated:hono-connection"
],
"topics": [
"_/_/things/live/commands",
"_/_/things/live/messages"
],
"headerMapping": {
"to": "command/'"${HONO_TENANT}"'/{{ thing:id }}",
"subject": "{{ header:subject | fn:default(topic:action-subject) }}",
"content-type": "{{ header:content-type | fn:default('"'"'application/vnd.eclipse.ditto+json'"'"') }}",
"correlation-id": "{{ header:correlation-id }}",
"reply-to": "{{ fn:default('"'"'command_response/'"${HONO_TENANT}"'/replies'"'"') | fn:filter(header:response-required,'"'"'ne'"'"','"'"'false'"'"') }}"
}
},
{
"address": "command/'"${HONO_TENANT}"'",
"authorizationContext": [
"pre-authenticated:hono-connection"
],
"topics": [
"_/_/things/twin/events",
"_/_/things/live/events"
],
"headerMapping": {
"to": "command/'"${HONO_TENANT}"'/{{ thing:id }}",
"subject": "{{ header:subject | fn:default(topic:action-subject) }}",
"content-type": "{{ header:content-type | fn:default('"'"'application/vnd.eclipse.ditto+json'"'"') }}",
"correlation-id": "{{ header:correlation-id }}"
}
}
]
}
}
}' http://$DITTO_IP:$DITTO_PORT/devops/piggyback/connectivity

This connection is configured so that if an Eclipse Hono device has the ThingId of an Eclipse Ditto twin as its identifier, its messages will be redirected to that twin directly (explained in more detail in the usage section).

Compositional support

Data prediction with machine learning

3D representation

- + \ No newline at end of file diff --git a/docs/installation/requirements.html b/docs/installation/requirements.html index 34034b7..f529e30 100644 --- a/docs/installation/requirements.html +++ b/docs/installation/requirements.html @@ -5,13 +5,13 @@ Requirements | OpenTwins - +
- + \ No newline at end of file diff --git a/docs/installation/using-helm.html b/docs/installation/using-helm.html index 027faee..9919041 100644 --- a/docs/installation/using-helm.html +++ b/docs/installation/using-helm.html @@ -5,14 +5,14 @@ Helm | OpenTwins - +

Helm

Standard version

Installation

First of all, you have to add ERTIS Research group helm repository to your helm repository list:

helm repo add ertis https://ertis-research.github.io/Helm-charts/

Once done, the next step is installing the chart by executing this line on your terminal (in our case, we will use opentwins as release name and opentwins as namespace, but you can choose the one that you prefeer). To customize the installation, please refer to Helm's values file.

danger

We recommend to modify the default passwords and tokens for Grafana and InfluxDB before deploying the platform. Currently, to avoid potential problems, we do not recommend changing Eclipse Ditto's username and password.

helm upgrade --install opentwins ertis/OpenTwins --wait --dependency-update --debug

After waiting some time, the installation will be ready for use.

Configuration

If you have kept the default values of the Helm chart, you will only need to configure the OpenTwins interface plugin for Grafana.

Obtain external URLs for Eclipse Ditto, Ditto extended API and Grafana.

Get the name of the services with kubectl get services. The result will look something similar to the following image. If you have changed the name of the release, the names will not be preceded by opentwins, but by the name you have assigned. We are interested in the services opentwins-grafana, opentwins-ditto-nginx and opentwins-ditto-extended-api.

Kubectl get services

The method to obtain the URL may vary depending on the configuration of your cluster. Generally, the URL for each service will match the cluster IP and the NodePort (the number after the colon). For example, if our cluster IP is 192.168.32.25, the URL for Grafana would be 192.168.32.25:30718.

Are you using Minikube to deploy OpenTwins?

As Minikube is a local cluster, you cannot directly use the IP of the cluster. Therefore, you will have to expose the services that you want to use externally with a command.

Open three terminals, one for each service, and run the following command on each terminal with a different service name. These will return a URL of your localhost with a port that will forward all traffic to the specified service. These are the URLs you should use.

minikube service <service-name> --url

Add URLs to OpenTwins plugin configuration

Access Grafana in any browser with the URL you have obtained. The credentials must match those indicated in the Helm values, which by default are user admin and password admin.

Access the left drop-down menu and select Administration > Plugins. Once there, find the OpenTwins plugin and activate it by clicking Enable. Then, go to the Configuration tab where you will need to enter the Eclipse Ditto and Extended API URLs in the corresponding fields. Use ditto for both the Eclipse Ditto username and password for the moment. Then click on Save settings to complete the plugin configuration.

note

If you are using the latest version of the interface, you may find two fields intended for an agent service. This functionality is currently under development and is not yet available, so leave them empty and disregard them for now.

Find the available application in the App > OpenTwins section of the left drop-down menu.

You can now start using OpenTwins.

Screenshots
Plugin
Configuration
Configuration

Lightweight version

OpenTwins has it's own lightweight version that aims to run on IoT devices such as Raspberry Pi devices. To install this versión, you have to follow the first step in order to add ERTIS repository to your repository list and then install the platform using the command bellow:

helm install ot ertis/OpenTwins-Lightweight -n opentwins

In this case connections still need to be made for the platform to work properly.

- + \ No newline at end of file diff --git a/docs/overview/architecture.html b/docs/overview/architecture.html index 8939e3c..3308708 100644 --- a/docs/overview/architecture.html +++ b/docs/overview/architecture.html @@ -5,14 +5,14 @@ Architecture | OpenTwins - +

Architecture

OpenTwins is built on a open source microservices architecture, designed to enhance scalability, flexibility and efficiency in the development, extension, deployment and maintenance of the platform. All the components that make up this architecture are encapsulated in Docker containers, ideally managed through Kubernetes, which ensures efficient portability and management.

Standard architecture

note

Although it is possible to deploy and connect the different components without containerization, this approach is not recommended due to the difficulties involved in terms of installation and management. However, it is important to note that OpenTwins could be manually connected to non-containerized components, such as a local instance of Grafana.

The following image illustrates the current architecture of OpenTwins, in which each color of the boxes represents the functionality covered by each component. Most of these components are external projects to our organization, however, we also include certain services specifically designed to enrich the functionality of the platform. Both the code and documentation of the components are available in their respective repositories.

Architecture

Essential functionality

The elements highlighted in blue form the heart of OpenTwins, as they provide the essential functionalities of a digital twin development platform: the definition of digital twins, the connection to IoT devices, the storage of information and the user-friendly visualisation of data. The tools used in this case include:

  • Eclipse Ditto. This is the core component of OpenTwins, an open-source framework for digital twins developed by the Eclipse Foundation. Eclipse Ditto provides an abstract entity "Thing", which allows describing digital twins through JSON schemas that include both static and dynamic data of the entity. The framework stores the current state of the "Thing" entity and facilitates its connection to input and output data sources through various IoT protocols. In a typical scenario, the Thing entity will update its information via a source connection, generating events that are sent to the indicated target connections. In addition, the tool provides an API that allows querying the current state of the entity and managing its schema and connections.

  • Eclipse Hono. This component facilitates the reception of data through various IoT protocols and centralizes it into a single endpoint, either AMQP 1.0 or Kafka. This output connects directly to Eclipse Ditto, eliminating the need for users to manually connect to an external broker to extract data. This allows the platform to receive data through the most common IoT protocols, giving devices the flexibility to connect to the most appropriate protocol for their particular case.

    danger

    Despite its advantages, we have observed that Eclipse Hono does not scale correctly when the message frequency is high, so we do not recommend its use in these cases. For this reason, or if it is not necessary to offer different input protocols, you can choose to connect Eclipse Ditto to one or more specific messaging brokers, such as Mosquitto or RabbitMQ.

  • MongoDB. This tool is the internal database used by Eclipse Hono and Eclipse Ditto. Eclipse Ditto stores data about the current state of digital twins ("things"), policies, connections and recent events, while Eclipse Hono stores information about defined devices and groups.

  • InfluxDB v2. This database provides an optimized architecture for time series, which guarantees superior performance in storing and querying digital twin data. Its high scalability and simplicity of use allow it to efficiently handle large volumes of data, facilitating the integration and analysis of information in real time. In addition, it is one of the most popular options in the field of the Internet of Things (IoT), generating an active community that consolidates its position as a robust solution.

  • Telegraf. This server-based agent for collecting and sending metrics offers easy configuration and a wide range of plugins to integrate various data sources and destinations. It is the recommended choice for data ingestion into InfluxDB. Its role in the platform is to capture digital twin updates, presented as Eclipse Ditto events, processing the data as time series for storage in the database.

  • Apache Kafka or Eclipse Mosquitto. An intermediary messaging broker is required for Telegraf to collect event data from Eclipse Ditto, since none of these technologies provide this role and do not have a direct connection. For this purpose, any messaging broker where Eclipse Ditto can publish and read Telegraf is valid. The options available on the platform include Apache Kafka, known for its scalability and error tolerance in processing large volumes of data, and Mosquitto, recognized for its efficiency in messaging and its flexibility in IoT environments.

  • Grafana. This solution acts as the platform's main front-end, providing a highly adaptable data visualization that allows users to create intuitive and easily understandable dashboards. Its ability to integrate with a wide variety of data sources and its active community of users and developers make it a powerful tool for monitoring and analyzing complex systems, such as digital twins. In addition, it allows users to expand its functionality by creating custom plugins, giving them the ability to integrate new visualizations, use-case specific panels and connectors to additional data sources.

Compositional support

The composition of digital twins represents one of the main contributions of this platform, distinguishing it from other similar solutions. In addition, OpenTwins provides the ability to define and compose "types" of digital twins, making development simpler. The services marked in green in the architecture are responsible for integrating these functionalities.

  • Extended API for Eclipse Ditto. The Thing entity provided by Eclipse Ditto must follow a specific JSON schema, although it offers great flexibility within it. Our goal is to simplify type definition and entity composition by taking advantage of the flexibility of this schema. This "extended API" acts as a layer on top of the Eclipse Ditto API, distinguishing between the management of twins and types, and applying all the necessary constraints and checks to ensure the composition of these entities according to the constraints imposed by each (types form graphs, while twins form trees).

  • OpenTwins app plugin for Grafana. To have a pleasant and usable platform for as many users as possible, it is important to have a simple interface capable of performing the functionalities available. Therefore, an app plugin is included for Grafana that uses the extended API to query and manage twins, types and their composition in a user-friendly way. Moreover, this approach keeps the entire platform front-end within a single tool, making it easy to use and accessible.

Data prediction with machine learning

The architecture highlights in yellow the components that facilitate the integration of digital twins with Machine Learning models. Providing this support represents a crucial aspect in the development of a digital twin, since it provides a complementary or comparative perspective with real data, enriching the understanding of the replicated object. To achieve this goal, the following tools are used:

  • Kafka-ML. This open source framework manages the lifecycle of ML/AI applications in production environments through continuous data streams. Unlike traditional frameworks that work with static data sets, Kafka-ML enables both training and inference with continuous data streams, allowing users to have fine-grained control over ingestion data. Currently, Kafka-ML is compatible with the most popular ML frameworks, TensorFlow and PyTorch, and enables the management and deployment of ML models, from definition to final deployment for inference. This component operates as a black box in OpenTwins, receiving input data for a deployed model through a Kafka topic and sending the predicted result to another topic, which is connected to Eclipse Ditto in a way that updates to the corresponding digital twin.

  • Eclipse Hono to Kafka-ML. Kafka-ML can receive input data from any source that is able to publish to a Kafka topic. However, at OpenTwins we have decided to simplify this process when the data comes from Eclipse Hono. Therefore, we have developed an optional service that automates the data feed of ML models deployed in Kafka-ML. This service automatically sends the data needed to make a prediction every time a new data is received from any of the devices required by the model. To use this tool, we provide an API that allows you to specify which devices should be taken into account, what data is required from these devices and how they should be formatted to work correctly as input for Kafka-ML.

  • Error detection for Eclipse Hono with Kafka-ML. An ML model useful in the construction of a digital twin is one capable of generating data that a sensor should produce in case it loses connection or experiences a failure. To automate this, we have developed an optional service with similar functionalities to the one mentioned above, but with an important particularity: it will only invoke the model when an interruption in data reception by the device is detected. This service takes into account the frequency with which the data is emitted by the device. As soon as an anomaly is identified, the service will format and send the last data received following the expected frequency until the connection is restored and real data is received again. In this way, the normal operation of the device is simulated, ensuring continuity of information for the digital twin.

3D representation

note

Although Unity is not open source, at the moment it is the only graphics engine we have tested the plugin with. However, it could be replaced by any other graphics engine that compiles in WebGL, such as Godot, since the plugin is expected to be able to render any WebGL compilation. However, since we have not yet tested with other graphics engines, we cannot guarantee this.

Although it may seem a secondary aspect, the representation of the digital twin makes a major difference in its utility and adoption. An intuitive and attractive visual interface for users will facilitate the understanding and accessibility of the data received, which simplifies the optimization of the actual system. 3D representations stand out as one of the most effective options in this sense, which motivates most private digital twin platforms to offer support for them. For this reason, OpenTwins allows adding an interactive 3D representation of the digital twin that reacts dynamically to data received from any source (real, predicted or simulated). The components highlighted in red are the ones that enable this functionality.

  • Unity This powerful graphics engine, versatile in both 3D and 2D development, is widely used in the creation of video games, simulations and engineering. It is recognized as one of the most popular, supported by a large and active community. Although it is not open source software, it can be used free of charge for personal or low-profit projects. The technology of this engine allows to assign behaviors to 3D objects by means of scripts, which facilitates interaction both with the user and with other elements of the environment.

    info

    Unity offers a wide range of formats for importing 3D models, but we emphasize its integration with Blender, an open source 3D creation suite that is equally popular and supported by an active community.

    On the other hand, Unity provides the possibility to compile projects in the Unity WebGL format, perfect for web rendering. This option is the choice of OpenTwins, as it allows easy integration with existing web technologies, ensuring accessibility and distribution without the need for additional installations.

  • Unity panel plugin for Grafana. Since Grafana serves as the front-end of OpenTwins, it is convenient that the 3D representations are embedded directly into this tool, providing a unified management and visualization of the digital twin. To achieve this, a plugin capable of rendering WebGL compilations within a Grafana panel has been developed. This plugin is able to send the digital twin data from Grafana to the compilation, allowing it to influence the rendering in real time. In addition, this plugin enables direct user interaction with the 3D model, allowing actions on 3D elements to affect other panels of the dashboard. For example, by clicking on a 3D element, another Grafana panel can automatically display data related to it.

Lightweight architecture

note

Although it is possible to deploy and connect the different components without containerization, this approach is not recommended due to the difficulties involved in terms of installation and management. However, it is important to note that Lightweight version of OpenTwins could be manually connected to non-containerized components.

The following image illustrates the current lightweigh architecture of OpenTwins, the field of digital twins is closely linked to the IoT, highlighting the critical importance of reducing data transmission delays. Additionally, IoT devices typically have limited processing capabilities, which makes running resource-demanding software impractical. This new architecture has been developed to facilitate dependable IoT applications within a distributed Edge/Fog/Cloud infrastructure. Developing a lightweight distributed version of OpenTwins can offer several advantages and address various needs depending on the context and goals of the project.

LightweightArchitecture

Like the main platform, the core component of this new architecture remains Eclipse Ditto. Most of the components has been removed to achieve lower consumptions.

  • Message broker: The Apache Kafka messaging broker has been replaced by Eclipse Mosquitto that uses MQTT5, as it is best suited messaging protocol for IoT.

  • Persistence: As the platform is designed to be used in low-resource environments such as IoT devices or Raspberry Pi, for example, it has been decided not to have persistence of historical data, so InfluxDB is not used. In the case of a persistence need, the MongoDB database that Eclipse Ditto needs to work can be used. Telegraf can introduce the historical data in this database. In this way, although we do not have a database optimized to store data in the form of time series, it would be possible to have persistence with a single database.

  • Visualization: Both IoT and Edge devices are not typically used for visualization-related tasks and are sometimes lacking the power to do so. This is why it has been decided to remove this component from the new lightweight architecture.

  • Simulation components: Due to the technical limitations mentioned above, the simulation components present in the original architecture have been eliminated.

  • ML components: As mentioned above, some edge or IoT are not capable of handling the necessary components to run the ML module, so its installation, although it has been used in this work, is optional.

All modules that are not present in this version of the platform are still fully available and compatible, so users can make use of any component if necessary by activating them in the platform installation process by Helm(WIP)

- + \ No newline at end of file diff --git a/docs/overview/concepts.html b/docs/overview/concepts.html index 055a62a..4b45ee0 100644 --- a/docs/overview/concepts.html +++ b/docs/overview/concepts.html @@ -5,13 +5,13 @@ Concepts | OpenTwins - +

Concepts

In this section, we will explore in depth the concept of a digital twin as defined by the platform. We will detail the information it can contain, explain the idea of a "digital twin type", and discuss how the composition works.

Digital twin definition

In the platform, a digital twin is defined as a replica of a real entity, whether tangible or not. This replica can be considered as an enhancement to monitoring the entity because, although it is not strictly necessary to be classified as a digital twin, it is beneficial to connect the real data of the entity with those generated by means of mathematical simulations or artificial intelligence. In this way, the digital twin becomes a central point that integrates all available sources of information on the entity, facilitating a unified, fast and effective query that promotes decision-making and, therefore, the optimization of the real entity.

Digital twin content

A digital twin is composed of static and dynamic data.

  • Static data. Information relevant to the digital twin that is expected to remain constant, such as the model, the date of acquisition or the location of the machine we are replicating.

  • Dynamic data. Data that changes over time and that we will record in time series, such as the position of a mobile robot or the values measured by a sensor.

For example, consider a DHT22 temperature and humidity sensor. Its digital twin, represented in JSON format following the schema provided by Eclipse Ditto, would look like this:

{
"policyId": "example:DHT22",
"attributes": {
"location": "Spain"
},
"features": {
"temperature": {
"properties": {
"value": null
}
},
"humidity": {
"properties": {
"value": null
}
}
}
}

Digital twin type

Digital twins composition

- + \ No newline at end of file diff --git a/docs/overview/purpose.html b/docs/overview/purpose.html index 9b2dbfd..2a11d6c 100644 --- a/docs/overview/purpose.html +++ b/docs/overview/purpose.html @@ -5,13 +5,13 @@ Purpose | OpenTwins - +

Purpose

This platform has been designed to facilitate the development of digital twins and is characterised by the exclusive use of open source components. The aim is to achieve a platform that covers all the functionalities that a digital twin may require, from the most basic ones, such as simply checking its real-time state, to more advanced ones, such as the inclusion of predicted or simulated data or visualisation of 3D models of the twins.

Take care

This platform is currently under development, so its use in production environments is not recommended at this stage.

- + \ No newline at end of file diff --git a/docs/quickstart.html b/docs/quickstart.html index 1d8a262..6411165 100644 --- a/docs/quickstart.html +++ b/docs/quickstart.html @@ -5,7 +5,7 @@ Quickstart | OpenTwins - + @@ -50,7 +50,7 @@ However, to take full advantage of its capabilities, we recommend including other functionalities or additional data sources. This will allow you to obtain a more complete and accurate view of the real system. You can check our guides for more information

- + \ No newline at end of file diff --git a/index.html b/index.html index 23ec28b..210a6d6 100644 --- a/index.html +++ b/index.html @@ -5,13 +5,13 @@ OpenTwins - +
Docusaurus themed imageDocusaurus themed image
opentwins

Innovative open-source platform that specializes in
developing next-gen compositional digital twins

ertis logoertis logoitis logoitis logouma logouma logo
- + \ No newline at end of file diff --git a/markdown-page.html b/markdown-page.html index 25a1c53..daea7b9 100644 --- a/markdown-page.html +++ b/markdown-page.html @@ -5,13 +5,13 @@ Markdown page example | OpenTwins - +

Markdown page example

You don't need React to write simple standalone pages.

- + \ No newline at end of file