[{"id":"fec4021a.3a256","type":"tab","label":"LocalAVL","disabled":false,"info":""},{"id":"f8aebec5.3184a","type":"tcp in","z":"fec4021a.3a256","name":"AVL data","server":"client","host":"0.0.0.0","port":"4002","datamode":"stream","datatype":"utf8","newline":"","topic":"","base64":false,"x":80,"y":88,"wires":[["ed47012.6ecb1"]]},{"id":"ed47012.6ecb1","type":"split","z":"fec4021a.3a256","name":"AVLsplit","splt":"M0","spltType":"str","arraySplt":"51","arraySpltType":"len","stream":true,"addname":"topic","x":220,"y":88,"wires":[["6c0a5735.75bb58"]]},{"id":"6c0a5735.75bb58","type":"string","z":"fec4021a.3a256","name":"TrimAVL","methods":[{"name":"left","params":[{"type":"num","value":"50"}]}],"prop":"payload","propout":"payload","object":"msg","objectout":"msg","x":360,"y":88,"wires":[["864765c9.ac9fc8"]]},{"id":"864765c9.ac9fc8","type":"template","z":"fec4021a.3a256","name":"prepend 'M0'","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"M0{{payload}}","output":"str","x":530,"y":80,"wires":[["336627ba.e283b8","c17fb8d8.725c78"]]},{"id":"b4f144bd.c31538","type":"debug","z":"fec4021a.3a256","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":370,"y":228,"wires":[]},{"id":"5a9de0d0.fd13c","type":"worldmap","z":"fec4021a.3a256","name":"TramMap","lat":"53.385436","lon":"-1.411743 ","zoom":"13","layer":"OSM","cluster":"","maxage":"","usermenu":"show","layers":"show","panit":"false","panlock":"false","zoomlock":"false","hiderightclick":"false","coords":"deg","showgrid":"false","path":"/trammap","x":360,"y":180,"wires":[]},{"id":"336627ba.e283b8","type":"function","z":"fec4021a.3a256","name":"String-2-Map","func":"\n//0310 53 23 2928 001 26 1244 220023 WNGN27S01003000000000003\n\nlet m = msg.payload\n\nlet ident = m.substr(1,4)\nlet name = m.substr(1,4)\n//source radio ident\n\nlet lat = m.substr(5,2)\nlet ma = m.substr(7,2)\nlet sa = m.substr(9,4)\n// lat: DDMMdddd(decimalmins)\n\nlet lon = m.substr(13,3)\nlet mo = m.substr(16,2)\nlet so = m.substr(18,4)\n// lon: DDMMdddd(decimalmins)\n\nlet hr = m.substr(22,2)\nlet mn = m.substr(24,2)\nlet se = m.substr(26,2)\n// HHmmss\n\nlet e = (m.substr(28,1) === \"E\") ? 1 : -1\nlet n = (m.substr(29,1) === \"N\") ? 1 : -1\n// West,North flag\n\nlet gf = m.substr(30,1)\n// Good fix flag\n\nlet sd = m.substr(34,1)\n// SpeedTrack data flag\n\nlet knts = m.substr(35,3)\nlet bearing = m.substr(38,3)\nlet head = m.substr(39,3)\n//speed Knts Head Deg\n\nlet flgGD = m.substr(30,1)\n// good data flag\nlet flgST = m.substr(34,1)\n// speed-track flag\n\nlat = (lat*1 + ma/60 + sa/600000) * n\nlon = (lon*1 + mo/60 + so/600000) * e\n\nlet icon = \"bus\";\n\nlet time = hr+\":\"+mn+\":\"+se\nlet gpstime = hr+\":\"+mn+\":\"+se\n\nvar timestamp = 0\n\nsource = \"tableIn\"\n\nspeed = Math.round(knts*1.85)\n\nvar map = {payload:{name:name,lat:lat,lon:lon,speed:speed,bearing:bearing,time:time,icon:icon}};\n\n//create SQL command\nmsgDB = \"(ident,lat,lon,gpstime,timestamp,flgGD,flgST,knts,head) values(\"+\",\\'\"+ident+\"\\',\"+lat+\",\"+lon+\",\"+gpstime+\",\"+timestamp+\",\"+flgGD+\",\"+flgST+\",\"+knts+\",\"+head+\")\"\nvar topic = (\"INSERT INTO oldmobs \" + msgDB);\nvar dBase = {}; //create new msg object\n//The SQlite node expects the command in the topic field\ndBase.topic = topic;\n// message format for scrolling UI display\n\nvar scroll = {payload:{name:name,lat:lat,lon:lon,speed:speed,bearing:bearing,time:time}};\n\nreturn [map,dBase,scroll];\n","outputs":3,"noerr":0,"x":130,"y":200,"wires":[["b4f144bd.c31538","5a9de0d0.fd13c"],[],["36610d9b.ce8d82","7a868faa.fbb28"]]},{"id":"d93223b.c45e4e","type":"comment","z":"fec4021a.3a256","name":"http://localhost:1880/trammap/","info":"","x":590,"y":168,"wires":[]},{"id":"36610d9b.ce8d82","type":"debug","z":"fec4021a.3a256","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":330,"y":380,"wires":[]},{"id":"99dcc944.c41958","type":"ui_table","z":"fec4021a.3a256","group":"de0f6439.011c38","name":"AVL-20","order":1,"width":12,"height":10,"columns":[],"outputs":0,"cts":false,"x":620,"y":300,"wires":[]},{"id":"7a868faa.fbb28","type":"ring-buffer","z":"fec4021a.3a256","name":"","capacity":16,"order":"old-to-new","sendOnlyIfFull":false,"pushAfterClear":false,"extra":false,"perTopic":false,"x":430,"y":300,"wires":[["99dcc944.c41958","cb41d650.6c8038"]]},{"id":"cb41d650.6c8038","type":"debug","z":"fec4021a.3a256","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":570,"y":360,"wires":[]},{"id":"e3744ca5.8326c","type":"comment","z":"fec4021a.3a256","name":"","info":"CREATE TABLE [IF NOT EXISTS] AVLdata (ident PRIMARY KEY NOT NULL, lat INTEGER NOT NULL, long INTEGER NOT NULL, gpstime INTEGER NOT NULL, timestamp INTEGER NOT NULL, flgGD INTEGER NOT NULL, flgSD INTEGER NOT NULL, knts INTEGER NOT NULL, head INTEGER NOT NULL)\n\n","x":480,"y":440,"wires":[]},{"id":"214a9e13.dcd6e2","type":"function","z":"fec4021a.3a256","name":"TramTestData(newIn)","func":"// This node will be replaced by data from new radio system\n// exact format unknown will contain:\n// [ident,lat,long,time,speed,heading]\nnMsg = [\"M\",1,2,3,4,5,6,7,8,9,10,11,\"N28S\",13,14,\"00000000003\"]\n//M 0310 53 23 0779 001 28 9594 170951 W N G N28S 002 222 00000000003\n// M nnnn AA BB CCCC DDD EE FFFF HHMMSS W N G JTTS PPP RRR xxxyyzzzz\n// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\n// generate hackdata \n// current input trigger time from timer\n\nvar Pi = 3.14159\n\nvar nowTime = new Date();\n// initialise if NOT exists\nvar lastTime = context.get('lastTime')||nowTime;\nvar deltTime = (nowTime - lastTime)/1000;\ncontext.set('lastTime',nowTime);\n// 53.394290, -1.472675 is start location\nvar lat = 53.394290;\nvar lon = -1.472675;\nvar speed = 11.11 // 40 kph = 11.11 m/S\nvar dist = speed * deltTime\n//\nvar radius = 1000 // rotate on a radius 'radius' of 1000m \nvar circum = (radius*2*Pi) //metres\n// approximate to chord of circle by dividing dist by circum * 360\nvar deltDeg = dist / circum * 360\n// start from zero degrees then add delta\nvar degrees = context.get('degrees')||0;\n// initialise degrees to 0 if NOT exists\ndegrees = degrees + deltDeg;\n// round the circle;\nif (degrees > 360){\n degrees = 0}\ncontext.set('degrees',degrees);\nlet head = degrees + 90\nif (head > 360){\n head = head -360}\n// maths lon x = rad COS degrees, lat y = radius SIN degrees\n// 90 degrees = radius \nvar newLon = lon + (Math.sin(degrees*Pi/180)*radius/66300);\nvar newLat = lat + (Math.cos(degrees*Pi/180)*radius/111200);\n\n// +/- degrees Lat = Y(m) / 111200\n// +/- degreees Lon = X(m) / 66300\nvar time = \"00:00:00\"\ndeltTime = deltTime.toFixed(1);\ndegrees = degrees.toFixed(1);\nhead = head.toFixed(1);\nnewLat = newLat.toFixed(6);\nnewLon = newLon.toFixed(6);\n\nnewLat = 53.394290\nnewLon = -1.472675 //hard coded for testing\n\n// positive ints\nvar ident = \"0999\"\nnMsg[1] = \"0999\"; // IDENT\nnMsg[2] = ((Math.floor(Math.abs(newLat))).toString()).padStart(2, '0'); // AA\nnMsg[3] = (Math.floor(60*(newLat-nMsg[2])));// BB\nnMsg[4] = (((60*(newLat-nMsg[2])-nMsg[3]).toFixed(4)*10000).toString()).padStart(4, '0') // CCCC\nnMsg[3] = ((nMsg[3]).toString()).padStart(2, '0');// BB\n\nnMsg[5] = ((Math.floor(Math.abs(newLat))).toString()).padStart(2, '0'); // AA\nnMsg[6] = (Math.floor(60*(newLat-nMsg[5])));// BB\nnMsg[7] = (((60*(newLat-nMsg[2])-nMsg[6]).toFixed(4)*10000).toString()).padStart(4, '0') // CCCC\nnMsg[6] = ((nMsg[6]).toString()).padStart(2, '0');// BB\n\nnMsg[8] = \"235959\" // HHMMSS\nnMsg[9] = ((newLon.toString()).substr(0,1) === \"-\") ? \"W\" : \"E\"\nnMsg[10] = ((newLat.toString()).substr(0,1) === \"-\") ? \"S\" : \"N\"\nnMsg[11] = \"G\"\nnMsg[13] = \"040\" // knts\nnMsg[14] = \"090\" //RRR Heading Degrees\n\nvar avlMsg =\"\"\nfor (i = 0; i < nMsg.length; i++) {\n avlMsg += nMsg[i];\n}\nnode.warn(avlMsg)\n\n//var locnMsg = {payload:{nnnn:\"0999\",AA:AA,BB:BB,CCCC:CCCC,DDD:DDD,EE:EE,FFFF:FFFF,HHMMSS:HHMMSS,W:W,N:N,G:G,JTT:JTT,S:S,PPP:PPP,RRR:RRR,end:endMsg}}\n\nvar map = {payload:{name:ident,lat:newLat,lon:newLon,speed:speed,bearing:head,time:time}};\n\nreturn [,map];\n\n","outputs":2,"noerr":0,"x":420,"y":520,"wires":[["8d7cb7d2.5f1f78"],["de6fb02f.c7fc6"]]},{"id":"44c0550e.b90cfc","type":"inject","z":"fec4021a.3a256","name":"","topic":"MoveTram","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":180,"y":540,"wires":[["214a9e13.dcd6e2"]]},{"id":"8d7cb7d2.5f1f78","type":"debug","z":"fec4021a.3a256","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":540,"wires":[]},{"id":"de6fb02f.c7fc6","type":"debug","z":"fec4021a.3a256","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":600,"wires":[]},{"id":"7309b040.c2325","type":"comment","z":"fec4021a.3a256","name":"UI Future Dev.","info":"**Future Work**\n\nTable to display last 12 inputs as scrolling data.\n\n'nice to have' - search by ident and display fix data in table\n\nAnother nice to have: \nTime error is the Metric for system. \n\ne.g. moving at 50 Kph t=d/s\n\nWhere d is the difference between \n'prevFix' and 'thisFix'\n\nCalculate delta distance varDD then (time error) = varDD/speed\n\nplot time errors + Max error\n\nRequires a database\n\n","x":770,"y":280,"wires":[]},{"id":"c17fb8d8.725c78","type":"function","z":"fec4021a.3a256","name":"TramLoc'nArray","func":"var i = 0;\nvar sieArr= 0\nvar cityArr= 0\n//Read into two dimensional array:\n// Tram id's 301-327 and 501-507\n// [id,gpsdatastring,timestamp,newdataFlg]\n\nif (sieArr === 0);{\n for (i = 1; i < 27; i++){\n sieArr [i] = [(i+300),\"NoGPS\",\"NoTime\",false];\n } \n}\n\n// IF var CityArr does not exist create array id 501-507\nif (cityArr === 0);{\n for (i = 1; i < 8; i++){\n cityArr [i] = [(i+500),\"NoGPS\",\"NoTime\",false];\n } \n}\n\n//parse incoming string for ID:\n//0310 53 23 2928 001 26 1244 220023 WNGN27S01003000000000003\n\nlet m = msg.payload\n\nlet ident = m.substr(1,4)\n//source radio ident\n\nlet time = m.substr(22,6)\n// HHmmss\n\n//Push data into array\nif (ident <400){\n sieArr [ident-300] =[ident,msg.payload,time,true]\n}\nelse { \n cityArr [ident-500] = [ident,msg.payload,time,true]\n}\n\n//readout data into tables\n\n\n//var tableSie = {payload:{identname:name,lat:lat,lon:lon,speed:speed,bearing:bearing,time:time}};\n\nreturn [sieArr,cityArr];\n\n","outputs":2,"noerr":0,"x":800,"y":100,"wires":[["9468324c.a85f8"],["d795e505.2cf488"]]},{"id":"9468324c.a85f8","type":"debug","z":"fec4021a.3a256","name":"301-327","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1080,"y":100,"wires":[]},{"id":"d795e505.2cf488","type":"debug","z":"fec4021a.3a256","name":"501-507","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1100,"y":180,"wires":[]},{"id":"66364c4c.313f44","type":"comment","z":"fec4021a.3a256","name":"","info":"Intention is:\n\nTwo streams of async AVL data are fed into TramLoc'nArray\nThey are then read out as a serial stream\n","x":820,"y":140,"wires":[]},{"id":"de0f6439.011c38","type":"ui_group","z":"","name":"ListAVL","tab":"8777a857.1e1e88","disp":true,"width":"16","collapse":false},{"id":"8777a857.1e1e88","type":"ui_tab","z":"","name":"TramAVLdata","icon":"dashboard","order":1,"disabled":false,"hidden":false}]