{"id":713,"date":"2022-01-24T11:10:13","date_gmt":"2022-01-24T10:10:13","guid":{"rendered":"http:\/\/tescaweb.nl\/Carel\/?p=713"},"modified":"2022-01-26T21:35:22","modified_gmt":"2022-01-26T20:35:22","slug":"fischertechnik-txt4-0-and-web-sockets-1","status":"publish","type":"post","link":"http:\/\/tescaweb.nl\/Carel\/?p=713","title":{"rendered":"fischertechnik TXT4.0 and web sockets (1)"},"content":{"rendered":"\n<p>Two project about the web socket communication from a browser with the fischertechnik TXT4.0<\/p>\n\n\n\n<h1 id=\"project-1\">Project 1<\/h1>\n\n\n\n<h2 id=\"basic-code-to-receive-data-over-a-web-socket-on-the-txt\">Basic code to receive data over a web socket on the TXT. <\/h2>\n\n\n\n<p>First I tested the Pyhton code in RoboPro Coding in the text mode.<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text\/x-python&quot;,&quot;theme&quot;:&quot;blackboard&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;language&quot;:&quot;Python&quot;,&quot;modeName&quot;:&quot;python&quot;}\">import asyncio, itertools\nimport time\nimport subprocess\nfrom lib.controller import *\nfrom websockets import serve\n#simple websocket server\n\nasync def echo(websocket):\n    print('message')\n    async for message in websocket:\n        await websocket.send(message)\n\n\nprint('start')\n\nasync def handler(websocket, path):\n    # Get received data from websocket\n    print(&quot;wait for recv&quot;)\n    while True:\n      try:\n          print(&quot;before&quot;)\n          data = await websocket.recv()\n          print(&quot;after&quot;)\n      except websockets.exceptions.ConnectionClosed:\n            print(&quot;connection closed&quot;)\n            break\n      except websockets.exceptions.ConnectionClosedError:\n            print(&quot;connection closed error&quot;)\n            break\n      except websockets.exceptions.ConnectionClosedOK:\n            print(&quot;connection closed Ok&quot;)\n            break\n    # await handler( websocket, path)\n    # Send response back to client to acknowledge receiving message\n      else:\n            print('server: recv' + data)\n            await websocket.send(&quot;== &quot; + data)\n            #print(&quot;send ready&quot;)\n            await handler( websocket, path)\n\n\nprint('test1')\n# Create websocket server\nstart_server = serve(handler, &quot;0.0.0.0&quot; ,  8085)\nprint('test2')\n# Start and run websocket server forever\nasyncio.get_event_loop().run_until_complete(start_server)\nprint('test3')\nasyncio.get_event_loop().run_forever()\nprint('test4')\n\n\nwhile True:\n    pass\nprint('test5')<\/pre><\/div>\n\n\n\n<p>The web page that runs on a client and contacts the TXT4.0 websocket.<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;mode&quot;:&quot;htmlmixed&quot;,&quot;mime&quot;:&quot;text\/html&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;language&quot;:&quot;HTML&quot;,&quot;modeName&quot;:&quot;html&quot;}\">&lt;!DOCTYPE HTML PUBLIC &quot;-\/\/W3C\/\/DTD HTML 4.01\/\/EN&quot; &quot;http:\/\/www.w3.org\/TR\/html4\/strict.dtd&quot;&gt;\n&lt;html&gt;\n&lt;head&gt;\n  &lt;title&gt;Test&lt;\/title&gt;\n  &lt;script type='text\/javascript' language=&quot;javascript&quot;&gt;\n  &lt;!--\n  var connection;\n  var keepAlive = false;\n  var printError = function(error, explicit) {\n    console.log(`[${explicit ? 'EXPLICIT' : 'INEXPLICIT'}] ${error.name}: ${error.message}`);\n     }\n  const jj= '{&quot;actuators&quot;:[{&quot;M&quot;:{&quot;i&quot;:1,&quot;p&quot;:[0,0,214,214]}},{&quot;M&quot;:{&quot;i&quot;:2,&quot;p&quot;:[0,0,214,214]}},{&quot;M&quot;:{&quot;i&quot;:3,&quot;p&quot;:[0,0,214,214]}}]}';\n  const j={&quot;actuators&quot;:[{&quot;M&quot;:{&quot;i&quot;:1,&quot;p&quot;:[0,0,214,214]}},{&quot;S&quot;:[0, 6,512]},{&quot;firstName&quot;:&quot;Peter&quot;,&quot;lastName&quot;:&quot;Jones&quot;}]};\n  \/\/const j=   {&quot;actuators&quot;:[{&quot;M&quot;:{&quot;i&quot;:1,&quot;p&quot;:[0,0,214,214]}},{&quot;M&quot;:{&quot;i&quot;:2,&quot;p&quot;:[0,0,214,214]}},{&quot;M&quot;:{&quot;i&quot;:3,&quot;p&quot;:[0,0,214,214]}}]};\n\n  function webSockKeepAlive() {\n    if (keepAlive) {\n      connection.send('ping'); \/\/ Send the message 'ping' to the server\n      setTimeout(&quot;webSockKeepAlive()&quot;, 10000);\n    }\n  }\n function sendButton1(par1,par2){\n     \/\/parameters not is use for the moment\n     myJSON=JSON.stringify(j);\/\/ to a JSON object\n\t console.log('sendButton: ' + myJSON);\n     connection.send(myJSON);\n  }\nfunction sendButton2(par1,par2){\n     \/\/parameters not is use for the moment\n     myJSON=JSON.stringify(j);\/\/ to a JSON object\n\t console.log('sendButton: ' + myJSON);\n     connection.send(myJSON);\n  }  \nfunction sendButton3(par1,par2){\n     \/\/parameters not is use for the moment\n     myJSON=JSON.stringify(j);\/\/ to a JSON object\n\t console.log('sendButton: ' + myJSON);\n     connection.send(myJSON);\n  }    \n  \n  function load() {\n    connection = new  WebSocket(&quot;ws:\/\/192.168.20.175:8085\/&quot;);\n    connection.onopen = function () {\n        var send = &quot;init &quot; + Math.round(Math.random()*4294967294+1);\n        console.log('Client: ' + send);\n        connection.send(send);\n        keepAlive = true;\n  \/\/      webSockKeepAlive();\n      };\n    connection.onclose = () =&gt; {\n            console.log('Web Socket Connection Closed');\n\t\t};\t\n    connection.onerror = function (error) {\n        keepAlive = false;\n        connection.close();\n        console.log('WebSocket error: ' + error);\n        alert(&quot;WebSocket error: &quot;+ error);\n      };\n    connection.onmessage = function (e) {\n        console.log('Message: ' + e.data);\n\t\tvar msgStr = document.getElementById('msg');\n\t\ttry{\n\t\tvar objJ=JSON.parse(e.data);\n\t\tvar tt=objJ.actuators[1].S;\/\/tt=objJ.actuators[1].S[1];\n\t\tmsgStr.innerHTML =&quot;&lt;strong&gt;Data received: &lt;\/strong&gt;&lt;br\/&gt;&quot;+  e.data+&quot; &lt;br\/&gt;&lt;br\/&gt;&lt;b&gt;JSON object:&lt;\/b&gt;&lt;br&gt; &quot;+JSON.stringify(objJ) +&quot; &lt;br&gt;&lt;br&gt;&lt;b&gt;JSON object selection:&lt;\/b&gt;&lt;br&gt;&quot;+JSON.stringify(tt);\n \t\t\n\t\t}catch(ex){\n\t\t   if (ex instanceof SyntaxError) {\n             printError(ex, true);\n\t\t     msgStr.innerHTML = &quot;Exception: Parse syntax error, data received=&lt;br\/&gt;&quot;+ e.data;\n \t\t   } else {\n             printError(ex, false);\n\t\t     msgStr.innerHTML = &quot;Exception: No parse error, data received=&lt;br\/&gt;&quot;+ e.data;\n   \t       }\n \t    }\n      };\n  }\n  \/\/--&gt;\n  &lt;\/script&gt; \n\n&lt;\/head&gt;\n\/&lt;!-- &lt;body onload=&quot;load()&quot;&gt; --&gt;\n&lt;body&gt;\n &lt;input type=&quot;button&quot; onclick=&quot;load();&quot; value=&quot;Open websocket server&quot;&gt;&lt;\/button&gt;\n &lt;input type=&quot;button&quot; onclick=&quot;connection.close();&quot; value=&quot;Close websocket server&quot;&gt;&lt;\/button&gt;\n  &lt;input type=&quot;button&quot; onclick=&quot;sendButton1('msg A','10');&quot; value=&quot;msg 1&quot;&gt;&lt;\/button&gt;\n  &lt;input type=&quot;button&quot; onclick=&quot;sendButton2('msg B','10');&quot; value=&quot;msg 2&quot;&gt;&lt;\/button&gt;\n  &lt;input type=&quot;button&quot; onclick=&quot;sendButton3('msg C','10');&quot; value=&quot;msg 3&quot;&gt;&lt;\/button&gt;\n  &lt;input type=&quot;button&quot; onclick=&quot;connection.send('msg D');&quot; value=&quot;msg 4&quot;&gt;&lt;\/button&gt;\n  &lt;br\/&gt;\n  &lt;p id=&quot;msg&quot;&gt;&lt;\/p&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre><\/div>\n\n\n\n<h2 id=\"the-integration-into-the-robopro-blockly-structure\">The integration into the RoboPro Blockly structure<\/h2>\n\n\n\n<p>After testing the Python code, the code has been integrated into the Blockly structure. The callback has been placed in a sub page.<br>This project is available on the RoboPro Coding  fischertechnik GitLab page under: &#8220;CvanLeeuwenFt\/my_606_WebSocketReceiver_Bl&#8221;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-style-default\"><img loading=\"lazy\" width=\"628\" height=\"691\" src=\"http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/mainWebsocketReceiver.png\" alt=\"\" class=\"wp-image-761\" srcset=\"http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/mainWebsocketReceiver.png 628w, http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/mainWebsocketReceiver-273x300.png 273w, http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/mainWebsocketReceiver-136x150.png 136w\" sizes=\"(max-width: 628px) 100vw, 628px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" src=\"http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/callbacksWebsocketReceiver.png\" alt=\"\" class=\"wp-image-718\" width=\"468\" height=\"198\" srcset=\"http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/callbacksWebsocketReceiver.png 606w, http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/callbacksWebsocketReceiver-300x127.png 300w, http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/callbacksWebsocketReceiver-150x63.png 150w\" sizes=\"(max-width: 468px) 100vw, 468px\" \/><figcaption>The RoboPro Coding callbacks page<\/figcaption><\/figure>\n\n\n\n<p>The main problems are:<br>How to to bring the data to Blockly-level?<br>Blockly has no user defined events and event handlers to deal with these events.<\/p>\n\n\n\n<h1 id=\"project-2\">Project 2 <\/h1>\n\n\n\n<h2 id=\"virtual-joystick-and-standard-game-control-html-page\">Virtual Joystick and standard game control HTML page<\/h2>\n\n\n\n<p>A couple of years ago I already introduced a remote controller from a WIndows machine by HTML\/Websocket communication with a TXT-controller.<br>This with the help of embedded CivetWeb server and RoboPro SLI extension elements.<br>I adapted this web pages to the TXT4.0 with the Python web socket.<\/p>\n\n\n\n<p>I made use of two projects which were available on the internet:<\/p>\n\n\n\n<ul><li>https:\/\/github.com\/jeromeetienne\/virtualjoystick.js<\/li><li>Based on Gamepad API Test Written in 2013 by Ted Mielczarek <a href=\"mailto:ted@mielczarek.org\">ted@mielczarek.org<\/a><br><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Gamepad_API\">Gamepad API &#8211; Web APIs | MDN (mozilla.org)<\/a><\/li><\/ul>\n\n\n\n<h3 id=\"the-structure-of-the-maps-and-files\">The structure of the maps and files<\/h3>\n\n\n\n<p class=\"has-nv-dark-bg-color has-text-color\">The map structure for the files:<br>ws_ft\\JoystickTXT40_2022_01_16.html<br>Jscript\\txtWebContent\\txt40_poc01.js<br>Jscript\/txtWebContent\/txt40_joystick_view.js<br>Jscript\/txtWebContent\/txt40_gamepadtest.js  <br>css\\txt40_select.css<br>css\\txt40_txt.css<br><\/p>\n\n\n\n<h3 id=\"the-gui\">The GUI<\/h3>\n\n\n\n<figure class=\"wp-block-image size-full is-style-default\"><img loading=\"lazy\" width=\"676\" height=\"859\" src=\"http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/WebInterface01.jpg\" alt=\"\" class=\"wp-image-745\" srcset=\"http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/WebInterface01.jpg 676w, http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/WebInterface01-236x300.jpg 236w, http:\/\/tescaweb.nl\/Carel\/wp-content\/uploads\/2022\/01\/WebInterface01-118x150.jpg 118w\" sizes=\"(max-width: 676px) 100vw, 676px\" \/><\/figure>\n\n\n\n<h3 id=\"joysticktxt40-2022-01-16-html\"> JoystickTXT40_2022_01_16.html <\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;mode&quot;:&quot;htmlmixed&quot;,&quot;mime&quot;:&quot;text\/html&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot; JoystickTXT40_2022_01_16.html &quot;,&quot;language&quot;:&quot;HTML&quot;,&quot;modeName&quot;:&quot;html&quot;}\">&lt;!DOCTYPE html&gt;\n&lt;!--\n(c) 2019-2022 ing. C van Leeuwen Btw. Enschede\n[2022-01-16] initial changes, js file with txt40_\n--&gt;\n&lt;html&gt;\n\n&lt;head&gt;\n&lt;meta charset=&quot;UTF-8&quot;&gt;\n&lt;title&gt;Embedded websocket example&lt;\/title&gt;\n&lt;link href=&quot;.\/..\/css\/txt40_txt.css&quot; rel=&quot;stylesheet&quot; type=&quot;text\/css&quot;&gt;\n&lt;link href=&quot;.\/..\/css\/txt40_select.css&quot; rel=&quot;stylesheet&quot; type=&quot;text\/css&quot;&gt;\n&lt;script src=&quot;.\/..\/Jscript\/txtWebContent\/txt40_poc01.js&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\n&lt;script src=&quot;.\/..\/Jscript\/jquery\/jquery-3.5.1.min.js&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\n&lt;script src=&quot;.\/..\/Jscript\/underscore-1.8.3\/underscore-min.js&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\n&lt;script src=&quot;.\/..\/Jscript\/backbone-1.4.0\/backbone-min.js&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\n&lt;script src=&quot;.\/..\/Jscript\/txtWebContent\/txt40_joystick_view.js&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\n&lt;script src=&quot;.\/..\/Jscript\/txtWebContent\/txt40_gamepadtest.js&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\n&lt;\/head&gt;\n\n&lt;body onload=&quot;load()&quot;&gt;\n\n&lt;header&gt;\n\t&lt;h1&gt;Websocket and fischertechnik TXT4.0&lt;\/h1&gt;\n\t&lt;p&gt;Project ia een vervolg op deze toepassing bij de fischertechnik TXT controller.&lt;\/p&gt;\n&lt;\/header&gt;\n&lt;nav id=&quot;nav&quot;&gt;\n\t&lt;a href=&quot;#Xbox360&quot; target=&quot;_blank&quot;&gt;Game console&lt;\/a&gt; |\n\t&lt;a href=&quot;#install &quot; target=&quot;_blank&quot;&gt;Install event handlers Game console&lt;\/a&gt; \n\t|\n\t&lt;a href=&quot;https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Gamepad_API\/Using_the_Gamepad_API&quot; target=&quot;_blank&quot;&gt;\n\tGamepad API (W3C Sep 14, 2021)&lt;\/a&gt; |\n\t&lt;a href=&quot;https:\/\/html5gamepad.com\/&quot; target=&quot;_blank&quot;&gt;Gamepad tester&lt;\/a&gt;\n&lt;\/nav&gt;\n&lt;h1&gt;FtWebsocket test remote (2022-01-18)&lt;\/h1&gt;\n&lt;script &gt;\n$('.example-default-value').each(function() {\n    var default_value = this.value;\n    $(this).focus(function() {\n        if(this.value == default_value) {\n            this.value = '';\n        }\n    });\n    $(this).blur(function() {\n        if(this.value == '') {\n            this.value = default_value;\n        }\n    });\n});\nfunction f_selected_ip() {\n    $('#pbutton1t').text('Connect to a fischertechnik TXT4.0 with the websocket server on: ');\n    $('#pbutton1t').append($('#select_ip').val());\n    $('#btn01').prop('disabled', false);  \/\/$('#btn02').prop('disabled', false);\n    $('#btn03').prop('disabled', false);  $('#btn04').prop('disabled', false);\n}\nfunction started_websocket(){\n}\n&lt;\/script&gt;\n&lt;div class=&quot;ip-select&quot; style=&quot;width: 200px;&quot; &gt;\n\t&lt;select id=select_ip name='select_ip' onchange=&quot;f_selected_ip()&quot;&gt;\n\t&lt;option id=&quot;sel&quot; value=&quot;0&quot;&gt;Select IP:&lt;\/option&gt;\n\t&lt;option id=&quot;api_usb&quot; value=&quot;1&quot;&gt;192.168.7.2&lt;\/option&gt;\n\t&lt;option id=&quot;api_ip_ac&quot; value=&quot;2&quot;&gt;192.168.8.2&lt;\/option&gt;\n\t&lt;option id=&quot;api_bt&quot; value=&quot;3&quot;&gt;192.168.9.2&lt;\/option&gt;\n\t&lt;option id=&quot;api_aa&quot; value=&quot;4&quot;&gt;192.168.20.176&lt;\/option&gt;\n\t&lt;option id=&quot;api_ab&quot; value=&quot;5&quot;&gt;192.168.10.182&lt;\/option&gt;\n\t&lt;\/select&gt; &lt;\/div&gt;\n&lt;div&gt;\n\t&lt;p id=&quot;pbutton1&quot; &gt;\n\t&lt;button id=&quot;btn01&quot; name=&quot;btn01&quot; onclick=&quot;startWebsocketConnection($('#select_ip').val())&quot; type=&quot;button&quot; disabled=&quot;disabled&quot;&gt;Start websocket  \n\t&lt;\/button&gt; &lt;p id=&quot;pbutton1t&quot; &gt;\n  Connected to a fischertechnik TXT4.0 with the websocket server on:&lt;\/p&gt;\n\t&lt;\/p&gt;\n&lt;\/div&gt;\n&lt;h2 id=&quot;start&quot;&gt;Virtual joystick demo&lt;\/h2&gt;\n&lt;script id=&quot;joystick-view&quot; type=&quot;text\/html&quot;&gt;\n     &lt;canvas id=&quot;joystickCanvas&quot;  width=&quot;&lt;%= squareSize %&gt;&quot; height=&quot;&lt;%= squareSize %&gt;&quot;\n                style=&quot;border:1px solid #000000; width: &lt;%= squareSize %&gt;px; height: &lt;%= squareSize %&gt;px;&quot;&gt;\n \t  &lt;\/canvas&gt;\n\t &lt;\/script&gt;\n&lt;div class=&quot;grid-container&quot;&gt;\n\t&lt;div id=&quot;joystickContent&quot;&gt;\n\t&lt;\/div&gt;\n\t&lt;div class=&quot;square1&quot;&gt;\n\t\t&lt;div id=&quot;js_field&quot;&gt;\n\t\t\t&lt;p&gt;local x,y= [ &lt;span id=&quot;xVal&quot;&gt;&lt;\/span&gt;, &lt;span id=&quot;yVal&quot;&gt;&lt;\/span&gt;]= [&lt;span id=&quot;xyVal&quot;&gt;&lt;\/span&gt;]&lt;br \/&gt;\n\t\t\t&lt;\/p&gt;\n\t\t&lt;\/div&gt;\n\t\t&lt;div id=&quot;websock_text_field&quot;&gt;\n\t\t\tNo websocket conn yet&lt;\/div&gt;\n\t\t&lt;div id=&quot;motor_field&quot;&gt;\n\t\t\tNo motor data yet&lt;\/div&gt;\n\t\t&lt;div id=&quot;input_field&quot;&gt;\n\t\t\tNo input data yet&lt;\/div&gt;\n\t\t&lt;div id=&quot;counter_field&quot;&gt;\n\t\t\tNo fast counter data yet&lt;\/div&gt;\n\t\t&lt;div id=&quot;message_field&quot;&gt;\n\t\t\tNo message data yet&lt;\/div&gt;\n\t\t&lt;div&gt;\n\t\t\t&lt;p&gt;Xbox Hat= &lt;span id=&quot;xBoxHat&quot;&gt;&lt;\/span&gt;&lt;br \/&gt;\n\t\t\tJoystick = &lt;span id=&quot;xBoxJoy&quot;&gt;&lt;\/span&gt;&lt;br \/&gt;\n\t\t\tZaxis= &lt;span id=&quot;xBoxZax&quot;&gt;&lt;\/span&gt;&lt;br \/&gt;\n\t\t\tXbox: &lt;span id=&quot;xBoxBut&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\n\t\t&lt;\/div&gt;\n\t&lt;\/div&gt;\n&lt;\/div&gt;\n&lt;script src=&quot;.\/..\/Jscript\/txtWebContent\/txt40_poc01_joystick.js&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\n&lt;div style=&quot;Background-color: #8bf;&quot;&gt;\n\t&lt;h2 id=&quot;Xbox360&quot;&gt;Standard game controller&lt;\/h2&gt;\n\t&lt;p&gt;Activate the game controller listener,&lt;br \/&gt;\n\tPress a button on your controller to start &lt;\/p&gt;\n\t&lt;script src=&quot;&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\n\t&lt;div id=&quot;install&quot;&gt;\n\t\t&lt;p&gt;\n\t\t&lt;button id=&quot;btn03&quot; onclick=&quot;myGamepad.instalGameEvents()&quot; type=&quot;button&quot; disabled&gt;\n\t\tinstall Txt game controller listeners&lt;\/button&gt;\n\t\t&lt;button id=&quot;btn04&quot; onclick=&quot;myGamepad.removeGameEvents()&quot; type=&quot;button&quot; disabled&gt;\n\t\tremove Txt game controller listeners&lt;\/button&gt;\n\t\t&lt;\/p&gt;\n\t&lt;\/div&gt;\n&lt;\/div&gt;\n&lt;footer&gt;\n\t&lt;p&gt;(c)2019-2022 C.van Leeuwen Enschede&lt;\/p&gt;\n&lt;\/footer&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre><\/div>\n\n\n\n<h3 id=\"txt40-poc01-js\"> txt40_poc01.js <\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot;txt40_poc01.js&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\/*(c) 2019-2022 ing. C. v. Leeuwen  Bwt. Enschede\n *txt40_poc01.js, short version without JSON for motors and inputs\n *\/\nfunction isJSON(item) {\n    item = typeof item !== &quot;string&quot; ? JSON.stringify(item) : item;\n    try {\n        item = JSON.parse(item);\n    } catch (e) {\n        return false;\n    }\n    if (typeof item === &quot;object&quot; &amp;&amp; item !== null) {\n        return true;\n    }\n    return false;\n}\n\nvar i = 0;\nfunction load() {\n    connection = null;\n};\n\/*\n * Return isSuccesful\n *\/\nfunction startWebsocketConnection( mapStr = '\/ws_txt161', ipStr = '127.0.0.1', portStr = '8080') {\n    mapStr = typeof mapStr !== 'undefined' ? mapStr : '\/websocket\/fischertechnik';\n    connection = null;\n    websock_text_field = document.getElementById('websock_text_field');\n    connection = new WebSocket('ws:' + '\/\/' + ipStr + ':' + portStr + mapStr);\n    connection.bufferType = &quot;arraybuffer&quot;; \/\/ default is &quot;blob&quot;\n \n    connection.onmessage = function (e) {\n        var charMode = typeof e.data === &quot;string&quot;;\n        if (charMode) {\n            websock_text_field.innerHTML = '&lt;p&gt;text mode &lt;\/p&gt;';\n            counter_field.innerHTML = '&lt;p&gt;' + e.data + '&lt;\/p&gt;';\n            \/* \t\t\tcode removed\t\t\t*\/ \/\/ end isJSON\n        } \/\/ end string mode\n        else { \/\/ binary mode\n            websock_text_field.innerHTML = '&lt;p&gt;binary mode &lt;\/p&gt;';\n        }\n    }\n\t\n    connection.onclose = function (event) {\n        if (event.wasClean) { \/\/\n            websock_text_field.innerHTML += '&lt;p&gt;closed normal &lt;\/p&gt;';\n            alert('[close] Connection closed cleanly, code=${event.code} reason=${event.reason}');\n        } else {\n            \/\/ e.g. server process killed or network down\n            \/\/ event.code is usually 1006 in this case\n            websock_text_field.innerHTML += '&lt;p&gt;closed died &lt;\/p&gt;';\n            alert('[close] Connection died, reason= ${event.reason}');\n        }\n    }\n    connection.onerror = function (error) {\n        alert('WebSocket error and is going to close, reason=${error}');\n        websock_text_field.innerHTML += '&lt;p&gt;error &lt;\/p&gt;';\n        connection.close();\n    }\n    return true;\n}\n<\/pre><\/div>\n\n\n\n<h3 id=\"txt40-poc01-joystick-js\"> txt40_poc01_joystick.js <\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot;txt40_poc01_joystick.js&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\/*\n* (c) 2019-2022 ing. C. v. Leeuwen  Bwt. Enschede\n* combine the virtual joystick with the standard game controller input.\n* and the listerner registration.\n* txt40_poc01_joystick.js based on : \n*\/\nvar myGamepad = new X360Gamepad(&quot;MyOne&quot;);\n\n$(document).ready(\n\t\tfunction() {\n\t\t\twindow.addEventListener(&quot;XboxConHat&quot;,\/\/ &quot;joystickLeftX&quot;,\n\t\t\tfunction(e) {\n\t\t\t\t$(&quot;#xBoxHat&quot;).html(&quot;&quot; + e.detail.Hat \/\/ + &quot;,&quot;\n\t\t\t\t\t\t+ &quot; &quot;);\n\t\t\t\t\/\/ alert('Event from joystickLeftX');\n\t\t\t\tif (connection !== null) {\n\t\t\t\t\tconnection.send('{&quot;xbox1&quot;:[&quot;hat&quot;,' + e.detail.Hat + ']}');\n\t\t\t\t}\n\t\t\t});\n\t\t\twindow.addEventListener(&quot;XboxConZax&quot;,\/\/ &quot;joystickLeftX&quot;,\n\t\t\tfunction(e) {\n\t\t\t\t$(&quot;#xBoxZax&quot;).html(\n\t\t\t\t\t\t&quot;[&quot; + e.detail.Zax[0] + &quot;,&quot; + e.detail.Zax[1] + &quot;] =&quot;);\n\t\t\t\t\/\/ alert('Event from joystickLeftX');\n\t\t\t\tif (connection !== null) {\n\t\t\t\t\tconnection.send('{&quot;xbox1&quot;:[&quot;zax&quot;,' + e.detail.Zax[0] + ','\n\t\t\t\t\t\t\t+ e.detail.Zax[1] + ']}');\n\t\t\t\t}\n\t\t\t});\n\n\t\t\twindow.addEventListener(&quot;XboxConJoy&quot;,\/\/ &quot;joystickLeftX&quot;,\n\t\t\tfunction(e) {\n\t\t\t\t$(&quot;#xBoxJoy&quot;).html(\n\t\t\t\t\t\t&quot; X [&quot; + e.detail.X[0] + &quot;,&quot; + e.detail.X[1] + &quot;,&quot;\n\t\t\t\t\t\t\t\t+ e.detail.X[2] + &quot;,&quot; + e.detail.X[3] + &quot;,&quot;\n\t\t\t\t\t\t\t\t+ e.detail.X[4] + &quot;] Y=&quot; + e.detail.Y);\n\t\t\t\t\/\/ alert('Event from joystickLeftX');\n\t\t\t\tif (connection !== null) {\n\t\t\t\t\tconnection.send('{&quot;xbox1&quot;:[&quot;joy&quot;,' + e.detail.X[0] + ','\n\t\t\t\t\t\t\t+ e.detail.X[1] + ',' + e.detail.X[2] + ','\n\t\t\t\t\t\t\t+ e.detail.X[3] + ']}');\n\t\t\t\t}\n\t\t\t});\n\n\t\t\twindow.addEventListener(&quot;XboxConBut&quot;,\/\/ &quot;buttonsXbox&quot;,\n\t\t\tfunction(e) {\n\t\t\t\tvar s = &quot; X [&quot;;\n\t\t\t\tfor (x in e.detail.X) {\n\t\t\t\t\ts += e.detail.X[x] + &quot;,&quot;;\n\t\t\t\t}\n\t\t\t\ts.slice(0, s.lenght - 2);\n\t\t\t\ts += &quot;] Y=&quot; + e.detail.Y;\n\t\t\t\t$(&quot;#xBoxBut&quot;).html(s);\n\t\t\t\tif (connection !== null) {\n\t\t\t\t\tconnection.send('{&quot;xbox1&quot;:[&quot;but&quot;,' + e.detail.X[0] + ','\n\t\t\t\t\t\t\t+ e.detail.X[1] + ',' + e.detail.X[2] + ','\n\t\t\t\t\t\t\t+ e.detail.X[3] + ',' + e.detail.X[4] + ','\n\t\t\t\t\t\t\t+ e.detail.X[5] + ',' + e.detail.X[6] + ','\n\t\t\t\t\t\t\t+ e.detail.X[7] + ',' + e.detail.X[8] + ','\n\t\t\t\t\t\t\t+ e.detail.X[9] + ']}');\n\t\t\t\t}\n\n\t\t\t});\n\t\t\t\/\/ ==========================================================================\n\t\t\tvar joystickView = new JoystickView(400, 'joystick-view',\n\t\t\t\t\t'joystickCanvas', function(callbackView) {\n\t\t\t\t\t\t$(&quot;#joystickContent&quot;).append(callbackView.render().el);\n\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\tcallbackView.renderSprite();\n\t\t\t\t\t\t}, 0);\n\t\t\t\t\t});\n\n\t\t\tjoystickView.bind(&quot;verticalMove&quot;, function(y) {\n\t\t\t\ty = Math.round(y * 15.0);\n\/*\t\t\t\tif (connection !== null) {\n\t\t\t\t\tconnection.send('{&quot;jsl&quot;:[&quot;joyY&quot;,' + y + ']}');\n\t\t\t\t}\n*\/\t\t\t\t\n\t\t\t\t$(&quot;#yVal&quot;).html(y);\n\t\t\t});\n\n\t\t\tjoystickView.bind(&quot;horizontalMove&quot;, function(x) {\n\t\t\t\tx = Math.round(x * 15.0);\n\/*\t\t\t\tif (connection !== null) {\n\t\t\t\t\tconnection.send('{ &quot;jsl&quot;:[&quot;joyX&quot;,' + x.toFixed(3) + ']}');\n\t\t\t\t}\n*\/\t\t\t\n\t\t\t\t$(&quot;#xVal&quot;).html(x);\n\t\t\t});\n\t\t\tjoystickView.bind(&quot;move&quot;, function(x,y) {\n\t\t\t\tx = Math.round(x * 15.0);y = Math.round(y * 15.0);\n\t\t\t\tif (connection !== null) {\n\t\t\t\t\tconnection.send('{ &quot;jsl&quot;:[&quot;joy&quot;,' + x + ',' + y+ ']}');\n\t\t\t\t}\n\t\n\t\t\t\t$(&quot;#xyVal&quot;).html( x+','+y );\n\t\t\t});\n\t\t});<\/pre><\/div>\n\n\n\n<h3 id=\"txt40-poc01-view-js\"> txt40_poc01_view.js <\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot;txt40_poc01_view.js&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\/*\n* (c) 2019-2022 ing. C. v. Leeuwen  Bwt. Enschede\n* txt40_poc01_view.js based on the work of : \n* Jerome Etienne, Dublin, Ireland\n* https:\/\/github.com\/jeromeetienne\/virtualjoystick.js  Jerome Etienne, Dublin, Ireland\n*\/\n\nvar INACTIVE = 0;\nvar ACTIVE = 1;\nvar SECONDS_INACTIVE = 20.0;\nvar INTERVAL_MS=100;\nfunction loadSprite(src, callback) {\n    var sprite = new Image();\n    sprite.onload = callback;\n    sprite.src = src;\n    return sprite;\n};\n\nJoystickView = Backbone.View.extend({\n    events: {\n        &quot;touchstart&quot;:&quot;startControl&quot;,&quot;touchmove&quot;:&quot;move&quot;,&quot;touchend&quot;:&quot;endControl&quot;,\n        &quot;mousedown&quot;: &quot;startControl&quot;,&quot;mouseup&quot;: &quot;endControl&quot;,&quot;mousemove&quot;: &quot;move&quot;\n    },\n    initialize: function(squareSize, jsview ,jscanvas,finishedLoadCallback){\n        this.squareSize = squareSize;\n        \/\/ this.template = _.template($(&quot;#joystick-view&quot;).html());\n       \/\/ t='&quot;'+jsview+'&quot;';\n      \/\/ $(`#${this.name}`).hide()\n        this.template = _.template($(&quot;#&quot;+jsview ).html());\n        this.state = INACTIVE;\n        this.x = 0; this.xX=0;\n        this.y = 0; this.yY=0;\n        this.canvas = null;\n        this.jscanvas = jscanvas;\n        this.context = null;\n        this.radius = (this.squareSize \/ 2) * 0.5;\n        this.finishedLoadCallback = finishedLoadCallback;\n        this.joyStickLoaded = false;\n        this.backgroundLoaded = false;\n        this.lastTouch = new Date().getTime();\n        self = this;\n        setTimeout(function(){\n            self._retractJoystickForInactivity();\n        }, 1000);\n        this.sprite = loadSprite(&quot;.\/..\/img\/button.png&quot;, function(){\n            self.joyStickLoaded = true;     self._tryCallback();\n        });\n        this.background = loadSprite(&quot;.\/..\/img\/canvas.png&quot;, function(){\n            self.backgroundLoaded = true;   self._tryCallback();\n        });\n    },\n    _retractJoystickForInactivity: function(){\n        var framesPerSec = 15;\n        var self = this;\n        setTimeout(function(){\n            var currentTime = new Date().getTime();\n            if(currentTime - self.lastTouch &gt;= SECONDS_INACTIVE * 1000){\n                self._retractToMiddle();  self.renderSprite();\n            }\n            self._retractJoystickForInactivity();\n        }, parseInt(1000 \/ framesPerSec, 10));\n    },\n    _tryCallback: function(){\n        if(this.backgroundLoaded &amp;&amp; this.joyStickLoaded){\n            var self = this;\n            this.finishedLoadCallback(self);\n        }\n    },\n    startControl: function(evt){\n        this.state = ACTIVE;\n    },\n    endControl: function(evt){\n        this.state = INACTIVE;\n        this.x = 0;\n        this.y = 0;\n        this.renderSprite();\n        this._triggerChange();\n    },\n    move: function(evt){\n        if(this.state == INACTIVE){\n            return;\n        }\n\t\tnow = new Date().getTime();\n       var interval =now-this.lastTouch;    \n       if ((interval&gt;INTERVAL_MS) || (interval&lt;0))  {\n\t\t   this.lastTouch = new Date().getTime();\n           var x, y;\n          if(evt.originalEvent &amp;&amp; evt.originalEvent.touches){\n            evt.preventDefault();\n            var left = 0;\n            var fromTop = 0;\n            elem = $(this.canvas)[0];\n            while(elem) {\n                left = left + parseInt(elem.offsetLeft);\n                fromTop = fromTop + parseInt(elem.offsetTop);\n                elem = elem.offsetParent;\n            }\n            x = evt.originalEvent.touches[0].clientX - left;\n            y = evt.originalEvent.touches[0].clientY - fromTop;\n        } else {\n            x = evt.offsetX;  y = evt.offsetY;\n        }\n        this._mutateToCartesian(x, y);        this._triggerChange();\n\t\t}\n    },\n    \n    _triggerChange: function(){\n       \tif( (this.x!=this.xX) ||(this.y!=this.yY)) {\n        \t\n            var xPercent = this.x \/ this.radius;\n            var yPercent = this.y \/ this.radius;\n            if(Math.abs(xPercent) &gt; 1.0){\n                xPercent \/= Math.abs(xPercent);\n            }\n            if(Math.abs(yPercent) &gt; 1.0){\n                yPercent \/= Math.abs(yPercent);\n            }\n            this.trigger(&quot;horizontalMove&quot;, xPercent);\n            this.trigger(&quot;verticalMove&quot;, yPercent);\n            this.trigger(&quot;move&quot;,xPercent,yPercent);\n            this.xX=this.x;this.yY=this.y;\n        \t}\n    },\n    _mutateToCartesian: function(x, y){\n        x -= (this.squareSize) \/ 2;\n        y *= -1;\n        y += (this.squareSize) \/ 2;\n        if(isNaN(y)){ y = this.squareSize \/ 2;     }\n        this.x = x;        this.y = y;\n        if(this._valuesExceedRadius(this.x, this.y)){this._traceNewValues(); }\n        this.renderSprite();\n    },\n    _retractToMiddle: function(){\n        var percentLoss = 0.1;\n        var toKeep = 1.0 - percentLoss;\n        var xSign = 1;        var ySign = 1;\n        if(this.x != 0){\n            xSign = this.x \/ Math.abs(this.x);\n        }\n        if(this.y != 0) {\n            ySign = this.y \/ Math.abs(this.y);        }\n        this.x = Math.floor(toKeep * Math.abs(this.x)) * xSign;\n        this.y = Math.floor(toKeep * Math.abs(this.y)) * ySign;\n    },\n    _traceNewValues: function(){\n    \t\/\/ y was a problem 2020-01-23 v\n    \tif((this.x&gt;-1.0)&amp;&amp;(this.x&lt;1.0) ){\n    \t\t        this.y = Math.sign(this.y)* this.radius;return;   }\n    \t\/\/ if((y&gt;-1.0)&amp;&amp;(y&lt;=1.0) ){\n    \t\/\/ this.y = y; this.x =x \/ Math.abs(x)* this.radius;return; }\n    \t\/\/ =========================================\n        var slope = this.y \/ this.x;\n        var xIncr = 1;\n        if(this.x &lt; 0){\n            xIncr = -1;\n        }\n        for(var x=0; x&lt;this.squareSize \/ 2; x+=xIncr){\n            var y = x * slope;\n            if(this._valuesExceedRadius(x, y)){\n                break;\n            }\n        }\n        this.x = x;        this.y = y;\n    },\n    _cartesianToCanvas: function(x, y){\n        var newX = x + this.squareSize \/ 2;\n        var newY = y - (this.squareSize \/ 2);\n        newY = newY * -1;\n        return {   x: newX,   y: newY       }\n    },\n    _valuesExceedRadius: function(x, y){\n        if(x === 0){\n            return Math.abs( y) &gt; this.radius;\n        }\n        return Math.pow(x, 2) + Math.pow(y, 2) &gt; Math.pow(this.radius, 2);\n    },\n    renderSprite: function(){\n        var originalWidth = 89;        var originalHeight = 89;\n        var spriteWidth = 50;        var spriteHeight = 50;\n        var pixelsLeft = 0; \/\/ ofset for sprite on img\n        var pixelsTop = 0; \/\/ offset for sprite on img\n        var coords = this._cartesianToCanvas(this.x, this.y);\n        if(this.context == null){            return;       }\n        \/\/ hack dunno why I need the 2x\n        this.context.clearRect(0, 0, this.squareSize * 2, this.squareSize);\n\n        var backImageSize = 300;\n        this.context.drawImage(this.background,\n            0,            0,      backImageSize,  backImageSize,\n            0,            0,      this.squareSize,   this.squareSize\n        )\n        this.context.drawImage(this.sprite,\n            pixelsLeft,            pixelsTop,\n            originalWidth,            originalHeight,\n            coords.x - spriteWidth \/ 2,    coords.y - spriteHeight \/ 2,\n            spriteWidth,          spriteHeight\n        );\n    },\n    render: function(){\n        var renderData = {\n            squareSize: this.squareSize\n        };\n        this.$el.html(this.template(renderData));\n        \/\/ this.canvas = this.$('#joystickCanvas')[0];\n        this.canvas = this.$(&quot;#&quot;+ this.jscanvas)[0];\n        this.context = this.canvas.getContext('2d');\n        this.renderSprite();\n        return this;\n    }\n});\n<\/pre><\/div>\n\n\n\n<h3 id=\"txt40-gamepadtest-js\"> txt40_gamepadtest.js <\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot;txt40_gamepadtest.js&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\/*\n * (c)2020-2022 ing. C.van Leeuwen Btw. Enschede.\n * txt40_gamepadtest.js\n * Changed into a prototype class structure and add events for the XBOX360 controller \n * Xbox360 interface for the fischertechnik TXT controller Websocket SLI. (custom RoboPro element).\n * Needs: \n *         underscore.js, \n * Note:\n * https:\/\/stackoverflow.com\/questions\/2025789\/preserving-a-reference-to-this-in-javascript-prototype-functions\n * Based on Gamepad API Test  Written in 2013 by Ted Mielczarek &lt;ted@mielczarek.org&gt;\n * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.\n * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see &lt;http:\/\/creativecommons.org\/publicdomain\/zero\/1.0\/&gt;.\n *\/\n\/*\n * ToDo: only events in case of changes in the strucure: \n * Hat= 0..8, Z-axis= {0..15,0..15], Joysticks= [leftX,leftY, rightX, rightY]\n * Button =[x,y,a,b, leftFront, rightFront,back, start]\n *  \n *\/\n\/*\n * Conversion of the HAT button array into 1..8, 0=not pressed\n *\/\nfunction convertHat(hatButtonArray) {\n\tvar status = 0;\n\tif (hatButtonArray[0] &amp;&amp; hatButtonArray[3])\n\t\tstatus = 2;\n\telse if (hatButtonArray[3] &amp;&amp; hatButtonArray[1])\n\t\tstatus = 4;\n\telse if (hatButtonArray[1] &amp;&amp; hatButtonArray[2])\n\t\tstatus = 6;\n\telse if (hatButtonArray[2] &amp;&amp; hatButtonArray[0])\n\t\tstatus = 8;\n\telse if (hatButtonArray[0])\n\t\tstatus = 1;\n\telse if (hatButtonArray[3])\n\t\tstatus = 3;\n\telse if (hatButtonArray[1])\n\t\tstatus = 5;\n\telse if (hatButtonArray[2])\n\t\tstatus = 7;\n\treturn status;\n};\nfunction X360Gamepad(name) {\n\t\/\/ constructor(){};\n\tthis.name = name;\n\tthis.haveEvents = 'GamepadEvent' in window;\n\tthis.haveWebkitEvents = 'WebKitGamepadEvent' in window;\n\trAF = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame\n\t\t\t|| window.requestAnimationFrame;\n\tthis.controllers = {};\n\tthis.XboxConJoyEvent = null;\/\/ new Event(&quot;XboxConJoy&quot;)\n\tthis.XboxConHatEvent = null;\/\/ new Event(&quot;XboxConHat&quot;)\n\tthis.XboxConButEvent = null;\/\/ new Event(&quot;XboxConBut&quot;)\n\tthis.XboxConZaxEvent = null;\/\/ new Event(&quot;XboxConZax&quot;)\n\t\/\/ this.buttons15X = [];\n\tthis.buttons16X = [];\/\/ last update buttons true\/false\n\tthis.buttons17X = [];\/\/ last update buttons 0\/1\n\tthis.buttons18X = [];\/\/ Last update Z axis\n\tthis.buttons19X = [];\n\tthis.axis15X = [ 0, 0, 0, 0 ];\/\/ last update axis\n\tthis.sX = 0;\/\/ last update Hat\n\tthis.lastEvents = 0;\n}\n\nX360Gamepad.prototype.addgamepad = function(gamepad) {\n\tthis.controllers[gamepad.index] = gamepad;\n\tvar d = document.createElement(&quot;div&quot;);\n\td.setAttribute(&quot;id&quot;, &quot;controller&quot; + gamepad.index);\n\tvar t = document.createElement(&quot;h3&quot;);\n\tt.appendChild(document.createTextNode(&quot;gamepad: &quot; + gamepad.id));\n\td.appendChild(t);\n\tvar b = document.createElement(&quot;div&quot;);\n\tb.className = &quot;buttons&quot;;\n\tfor (var i = 0; i &lt; gamepad.buttons.length; i++) {\n\t\tvar e = document.createElement(&quot;span&quot;);\n\t\te.className = &quot;button&quot;;\n\t\te.innerHTML = i;\n\t\tb.appendChild(e);\n\t}\n\t;\n\td.appendChild(b);\n\tvar a = document.createElement(&quot;div&quot;);\n\ta.className = &quot;axes&quot;;\n\tfor (i = 0; i &lt; gamepad.axes.length; i++) {\n\t\te = document.createElement(&quot;meter&quot;);\n\t\te.className = &quot;axis&quot;;\n\t\te.setAttribute(&quot;min&quot;, &quot;-1&quot;);\n\t\te.setAttribute(&quot;max&quot;, &quot;1&quot;);\n\t\te.setAttribute(&quot;value&quot;, &quot;0&quot;);\n\t\te.innerHTML = i;\n\t\ta.appendChild(e);\n\t}\n\td.appendChild(a);\n\tdocument.getElementById(&quot;start&quot;).style.display = &quot;none&quot;;\n\t\/\/ document.body.appendChild(d);\n\t\/\/ document.getElementById(&quot;install&quot;).appendChild(d);\n\tdd = document.getElementById(&quot;install&quot;);\n\tdd.insertBefore(d, dd.childNodes[0]);\n\tvar u = self.updateStatus;\n\trAF(this.updateStatus.bind(this));\n};\n\nX360Gamepad.prototype.connecthandler = function(e) {\n\tthis.addgamepad(e.gamepad);\n};\n\nX360Gamepad.prototype.disconnecthandler = function(e) {\n\tthis.removegamepad(e.gamepad);\n};\n\nX360Gamepad.prototype.removegamepad = function(gamepad) {\n\tvar d = document.getElementById(&quot;controller&quot; + gamepad.index);\n\tdocument.body.removeChild(d);\n\tdelete this.controllers[gamepad.index];\n};\n\/\/ callback for the requestAnimationFrame\nX360Gamepad.prototype.updateStatus = function(timestamp) {\n\tself = this;\n\tself.scangamepads();\n\tfor (j in self.controllers) {\n\t\tvar controller = self.controllers[j];\n\t\tvar d = document.getElementById(&quot;controller&quot; + j);\n\t\tvar buttons = d.getElementsByClassName(&quot;button&quot;);\n\t\tvar buttons15 = [];\/\/ all buttons in pairs [true\/false,value]+hat\n\t\t\/\/ [number]\n\t\t\/\/ var buttons16 = [];\/\/buttons true\/false\n\t\tvar buttons17 = [];\/\/ buttons 0\/1\n\t\tvar buttons18 = [];\/\/ Z axis 0.0..1.0 =&gt; 0..15\n\t\tvar buttons19 = []; \/\/ Hat\n\t\tfor (var i = 0; i &lt; controller.buttons.length; i++) {\n\t\t\tvar b = buttons[i];\n\t\t\tvar val = controller.buttons[i];\n\t\t\tvar pressed = val == 1.0;\n\t\t\tif (typeof (val) == &quot;object&quot;) {\n\t\t\t\tpressed = val.pressed;\n\t\t\t\tval = val.value;\n\t\t\t}\n\t\t\tvar pct = Math.round(val * 100) + &quot;%&quot;;\n\t\t\tb.style.backgroundSize = pct + &quot; &quot; + pct;\n\t\t\tif (pressed) {\n\t\t\t\tb.className = &quot;button pressed&quot;;\n\t\t\t} else {\n\t\t\t\tb.className = &quot;button&quot;;\n\t\t\t}\n\t\t\tbuttons15.push(pressed);\n\t\t\tbuttons15.push(val);\n\t\t\tif ([ 0, 1, 2, 3, 4, 5, 8, 9, 10, 11 ].includes(i)) {\n\t\t\t\t\/\/ buttons16.push(pressed);\n\t\t\t\tbuttons17.push(val);\n\t\t\t} else if ([ 6, 7 ].includes(i)) {\n\t\t\t\t\/\/ buttons16.push(pressed);\n\t\t\t\tval = Math.round(val * 15.0)\n\t\t\t\tbuttons18.push(val);\n\t\t\t} else if ([ 12, 13, 14, 15 ].includes(i)) {\n\t\t\t\t\/\/ buttons16.push(pressed);\n\t\t\t\tbuttons19.push(pressed);\n\t\t\t}\n\t\t}\/\/ end for the buttons\n\t\tvar axes = d.getElementsByClassName(&quot;axis&quot;);\n\t\tvar axis15 = [];\n\t\tfor (var i = 0; i &lt; controller.axes.length; i++) {\n\t\t\tvar a = axes[i];\n\t\t\tvar x = controller.axes[i]\n\t\t\ta.innerHTML = i + &quot;: &quot; + x.toFixed(4);\n\t\t\ta.setAttribute(&quot;value&quot;, x);\n\t\t\tif((i===1)||(i===3)) axis15.push(-1*Math.round(x * 15.0))\n\t\t       else axis15.push(Math.round(x * 15.0));\n\t\t\n\t\t}\n\t\t\/\/ end for the axis\n\t\t\/\/ =========================================================================\n\t\t\/\/ extra for events\n\t\t\/\/ ============================================================================\n\t\tvar s = convertHat(buttons19);\n\t\tbuttons15.push(s);\/\/ general array\n\t\t\/\/ =========================================================================\n\t\t\/\/ fire events every ...msec\n\t\t\/\/ ============================================================================\n\t\tif (timestamp - this.lastEvents &gt; 180) {\n\t\t\tthis.lastEvents = timestamp; \/\/ only when changed\n\t\t\tvar b = buttons15;\n\t\t\t\/\/ var bl = buttons15.length; \/\/var rl = r.length;\n\t\t\tvar zax = buttons18;\/\/ Z axis [0..15,0..15]\n\t\t\tvar but = buttons17;\/\/ val 0\/1\n\n\t\t\tvar r = axis15;\/\/ preserve for the event\n\n\t\t\tif (!_.isEqual(this.axis15X, axis15)) {\n\t\t\t\tthis.axis15X = Array.from(axis15);\n\t\t\t\taxis15.push(&quot;ch&quot;);\n\n\t\t\t\tthis.XboxConJoyEvent = new CustomEvent(&quot;XboxConJoy&quot;, {\n\t\t\t\t\tdetail : {\n\t\t\t\t\t\tX : r,\n\t\t\t\t\t\tY : &quot;-&quot;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tdispatchEvent(this.XboxConJoyEvent);\n\n\t\t\t} else {\/* Joystick no change *\/\n\t\t\t\taxis15.push(&quot;--&quot;);\n\t\t\t}\n\n\t\t\tif (!_.isEqual(this.buttons17X, buttons17)) {\n\t\t\t\tthis.buttons17X = buttons17;\n\t\t\t\tthis.XboxConButEvent = new CustomEvent(&quot;XboxConBut&quot;, {\n\t\t\t\t\tdetail : {\n\t\t\t\t\t\tX : but,\n\t\t\t\t\t\tY : &quot;-&quot;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tdispatchEvent(this.XboxConButEvent);\n\t\t\t} else {\/* buttons 0\/1 no change *\/\n\t\t\t}\n\n\t\t\tif (!_.isEqual(this.buttons18X, buttons18)) {\n\t\t\t\tthis.buttons18X = buttons18;\n\t\t\t\tthis.XboxConZaxEvent = new CustomEvent(&quot;XboxConZax&quot;, {\n\t\t\t\t\tdetail : {\n\t\t\t\t\t\tZax : zax\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tdispatchEvent(this.XboxConZaxEvent);\n\t\t\t} else {\/* Z axis no change *\/\n\t\t\t}\n\t\t\t\n\t\t\tif (this.sX != s) {\n\t\t\t\tthis.sX = s;\n\t\t\t\tthis.XboxConHatEvent = new CustomEvent(&quot;XboxConHat&quot;, {\n\t\t\t\t\tdetail : {\n\t\t\t\t\t\tHat : s\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tdispatchEvent(this.XboxConHatEvent);\n\t\t\t} else {\/* Hat no change *\/\n\t\t\t}\n\t\t}\n\t}\/\/ end for a controller\n\trAF(this.updateStatus.bind(this));\n};\n\nX360Gamepad.prototype.scangamepads = function() {\n\tvar gamepads = navigator.getGamepads ? navigator.getGamepads()\n\t\t\t: (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\n\tfor (var i = 0; i &lt; gamepads.length; i++) {\n\t\tif (gamepads[i]) {\n\t\t\tif (!(gamepads[i].index in this.controllers)) {\n\t\t\t\taddgamepad(gamepads[i]);\n\t\t\t} else {\n\t\t\t\tthis.controllers[gamepads[i].index] = gamepads[i];\n\t\t\t}\n\t\t}\n\t}\n};\n\nX360Gamepad.prototype.instalGameEvents = function() {\n\tself = this;\n\tif (this.haveEvents) {\n\t\twindow.addEventListener(&quot;gamepadconnected&quot;, self.connecthandler\n\t\t\t\t.bind(this));\n\t\twindow.addEventListener(&quot;gamepaddisconnected&quot;, self.disconnecthandler\n\t\t\t\t.bind(this));\n\t} else if (this.haveWebkitEvents) {\n\t\twindow.addEventListener(&quot;webkitgamepadconnected&quot;, self.connecthandler\n\t\t\t\t.bind(this));\n\t\twindow.addEventListener(&quot;webkitgamepaddisconnected&quot;,\n\t\t\t\tself.disconnecthandler.bind(this));\n\t} else {\n\t\tsetInterval(this.scangamepads.bind(this), 500);\n\t}\n};\nX360Gamepad.prototype.removeGameEvents = function() {\n\tif (this.haveEvents) {\n\t\twindow.removeEventListener(&quot;gamepadconnected&quot;, this.connecthandler\n\t\t\t\t.bind(this));\n\t\twindow.removeEventListener(&quot;gamepaddisconnected&quot;,\n\t\t\t\tself.disconnecthandler.bind(this));\n\t} else if (this.haveWebkitEvents) {\n\t\twindow.removeEventListener(&quot;webkitgamepadconnected&quot;,\n\t\t\t\tself.connecthandler.bind(this));\n\t\twindow.removeEventListener(&quot;webkitgamepaddisconnected&quot;,\n\t\t\t\tself.disconnecthandler.bind(this));\n\t} else {\n\t\tsetInterval(this.scangamepads.bind(this), 500);\n\t}\n};\n<\/pre><\/div>\n\n\n\n<h3 id=\"txt40-txt-css\">txt40_txt.css<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;mode&quot;:&quot;css&quot;,&quot;mime&quot;:&quot;text\/css&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot;txt40_txt.css&quot;,&quot;language&quot;:&quot;CSS&quot;,&quot;modeName&quot;:&quot;css&quot;}\">@charset &quot;ISO-8859-1&quot;;\ntable, th, td {\n\tborder: 1px solid black;\n\tborder-collapse: collapse;\n}\n\nthead&gt;tr&gt;td {\n\tbackground-color: #f5f5f5;\n}\n\ntbody&gt;tr&gt;td {\n\tbackground-color: #f5f5c5;\n}\n\n.buttons, .axes {\n\tpadding: 1em;\n}\n\n.axis {\n\tmin-width: 200px;\n\tmargin: 1em;\n}\n\n.boxed {\n\tborder: 1px solid green;\n}\n\n.square1 {\n\tBackground-color: #8af;\n}\n\n\n.button {\n\tpadding: 1em;\n\tborder-radius: 20px;\n\tborder: 1px solid black;\n\tbackground-image:\n\t\turl(data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdjYPjPAAACAgEAqiqeJwAAAABJRU5ErkJggg==);\n\tbackground-size: 0% 0%;\n\tbackground-position: 50% 50%;\n\tbackground-repeat: no-repeat;\n}\n\n.pressed {\n\tborder: 1px solid red;\n}\n\n.grid-container {\n\twidth: auto;\n\tmax-width: 870px;\n\tjustify-content: center;\n\t display : table;\n\tgrid-template-columns: 425px 425px;\n\tbackground-color: #213433;\n\tdisplay: grid;\n\tgrid-gap : 10px;\n\tbackground-color : #213433;\n\tpadding: 10px;\n}\n\n.grid-container&gt;div {\n\theight: 410px;\n\twidth: 410px;\n\tbackground-color: rgba(100, 255, 255, 0.8);\n\ttext-align: left;\n\tpadding: 10px;\n\tfont-size: 14px;\n\tborder: 1px solid #000000;\n}<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>RoboPro Coding Blockly main page<\/p>\n","protected":false},"author":1,"featured_media":718,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"neve_meta_sidebar":"","neve_meta_container":"default","neve_meta_enable_content_width":"off","neve_meta_content_width":70,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"[\"tags\",\"title\",\"meta\",\"content\",\"post-navigation\",\"comments\"]","neve_meta_disable_header":"on","neve_meta_disable_footer":"","neve_meta_disable_title":""},"categories":[6,127,126],"tags":[108,16,52,136,137,130,124],"post_folder":[],"_links":{"self":[{"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=\/wp\/v2\/posts\/713"}],"collection":[{"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=713"}],"version-history":[{"count":1,"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=\/wp\/v2\/posts\/713\/revisions"}],"predecessor-version":[{"id":767,"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=\/wp\/v2\/posts\/713\/revisions\/767"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=\/wp\/v2\/media\/718"}],"wp:attachment":[{"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=713"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=713"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=713"},{"taxonomy":"post_folder","embeddable":true,"href":"http:\/\/tescaweb.nl\/Carel\/index.php?rest_route=%2Fwp%2Fv2%2Fpost_folder&post=713"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}