Splitting text string into array

I need to split parts of a fixed length string of serial GPS data into an array by vehicle ID (bold in example below). Chrs 4,5 are degs Lat, 6,7 mins...and so on...

I have 18 different values in the string, (some are just flags) and 100 vehicle ID's

031053232928001261244220023WNGN27S01003000000000003

I do know that I can do this with a function node and slice() - but the coding is beyond me despite reading through examples.

Once I have the data sorted I can manipulate it and present on NodeRed dash-board or even populate a map.. :slight_smile:

Hope someone can assist.

To give you a hint for a function node, inject your string into this node:

let m = msg.payload

let vid = m.substr(0,4)
let lat = m.substr(4,2);
let mins = m.substr(6,2)
//etc...
return {payload:{vehicleid:vid,lat:lat,mins:mins}}

In this case substr takes arguments: start position, number of characters, with a zero based index (ie. start position of character minus 1).

Output example:

{"vehicleid":"0310","lat":"53","mins":"23"}

That is straight-forward - I need to understand how to manupulate the output into an array, and overwrite stale data but first I will help myself and then if stuck ask again.

No. At that point it is then already parsed. Just need to turn degrees and minutes into decimal degrees ready for map

I should explain a bit more. This is about trams. GPS data is transmitted over a private radio system and then passed onto operator and passenger stop display systems which acceptcurrent format it 'as is'. I have the job of re engineering the radios and new ones will use a slightly different gps protocol presented on a separate serial port. So task is to read both streams of location data in and then back out in the original protocol to give a seamless customer experience. The radios will swapped over a period of months. I have the job of doing everything...I have ideas and a node red UI showing Gps status during the transition will be very useful. Mapping is actually not really needed.
Hope that is clear

Sounds like a good project - hopefully you can share some details once done (or photos etc). But yes - if you use node-red-contrib-web-worldmap node(s) - you just need a name lat and long property and it'll start working... so should only take about 10 mins :slight_smile:

If you can share what the other fields are I'm sure someone would help.
image

Here's the flow for that

[{"id":"953664c0.4a4b38","type":"inject","z":"bac0960.1c51468","name":"","topic":"","payload":"031053232928001261244220023WNGN27S01003000000000003","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":120,"wires":[["14f3cdd2.8266b2","6cb16a68.8a48f4"]]},{"id":"b8d9b24c.ce47d","type":"debug","z":"bac0960.1c51468","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":470,"y":180,"wires":[]},{"id":"44e1acd2.d7f844","type":"worldmap","z":"bac0960.1c51468","name":"","lat":"","lon":"","zoom":"","layer":"","cluster":"","maxage":"","usermenu":"show","layers":"show","panit":"true","panlock":"false","zoomlock":"false","hiderightclick":"false","coords":"none","showgrid":"false","path":"/worldmap","x":460,"y":120,"wires":[]},{"id":"14f3cdd2.8266b2","type":"function","z":"bac0960.1c51468","name":"","func":"\n//0310 53 23 2928 001 26 1244 220023 WNGN27S01003000000000003\n\nlet m = msg.payload\n\nlet name = m.substr(0,4)\n\nlet lat = m.substr(4,2)\nlet ma = m.substr(6,2)\nlet sa = m.substr(8,4)\n\nlet lon = m.substr(12,3)\nlet mo = m.substr(15,2)\nlet so = m.substr(17,4)\n\nlet hr = m.substr(21,2)\nlet mn = m.substr(23,2)\nlet se = m.substr(25,2)\n\nlet e = (m.substr(27,1) === \"E\") ? 1 : -1\nlet n = (m.substr(28,1) === \"N\") ? 1 : -1\n\nlat = (lat*1 + ma/60 + sa/360000) * n\nlon = (lon*1 + mo/60 + so/360000) * e\n\nlet icon = \"bus\";\n\nlet time = hr+\":\"+mn+\":\"+se\n\nreturn {payload:{name:name,lat:lat,lon:lon,time:time,icon:icon}}\n","outputs":1,"noerr":0,"x":270,"y":120,"wires":[["b8d9b24c.ce47d","44e1acd2.d7f844"]]}]

For the actual project I would probably use the node-red-contrib-binary node as that can be configured to slice up things - but once set - will also work the other way - so could help with the recombine as well. The syntax is a bit weird so is harder to get your head around than the simple function, but again happy to help.

1 Like

dceejay,

Great work with my example data. I am much encouraged and spending this evening adding to it

I am after some help having become a bit stuck with my array, understanding how to define and then push data into it. - actually just looking at it again I'm doing daft things subtracting numbers from strings..

var i = 0;
var sieArr= 0
var cityArr= 0
//Read into two dimensional array:
// Tram id's 301-327 and 501-507
// [id,gpsdatastring,timestamp,newdataFlg]

if (sieArr === 0);{
   for (i = 1; i < 27; i++){
       sieArr [i] = [(i+300),"NoGPS","NoTime",false];
   }  
}

// IF var CityArr does not exist create array id 501-507
if (cityArr === 0);{
      for (i = 1; i < 8; i++){
       cityArr [i] = [(i+500),"NoGPS","NoTime",false];
   }  
}

//parse incoming string for ID:
//0310 53 23 2928 001 26 1244 220023 WNGN27S01003000000000003

let m = msg.payload

let ident = m.substr(1,4)
//source radio ident

let time = m.substr(22,6)
// HHmmss

//Push data into array
if (ident <400){
    sieArr [ident-300] =[ident,msg.payload,time,true]
}
else { 
    cityArr [ident-500] = [ident,msg.payload,time,true]
}
//readout data into tables
//var tableSie = {payload:{identname:name,lat:lat,lon:lon,speed:speed,bearing:bearing,time:time}};
return [sieArr,cityArr];

Flow below is my hack-space. If anyone would like to look at live data PM me for the URL.

TramNode.txt (11.4 KB)

Thanks to dceejay for helping me with this off line. With guidance I am now using context and the array node is sort-of-working, but incoming data overwrites the array, also the array is being re-initialized every few seconds, have spend a few hours wondering why - anyone with ideas please?

Here is my function node:

var i;
var l;
var m;
var length;

// Load arrays from context or start with blank one.

siemArr = context.seimArr || [];
cityArr = context.cityArr || [];

//Read into two dimensional array:
// Tram id's 301-327 and 501-507
// [id,gpsdatastring,timestamp,newdataFlg]

// If sieArr is empty create array id 301-326
if (siemArr[1[0]] !== 301) {
    for (i = 1; i < 27; i++) {
        siemArr[i] = [(i+300),"NoGPS","NoTime",false];
    }  
}

// If var cityArr is empty create array id 501-507
if (cityArr[1[0]] !== 501) {
    for (i = 1; i < 8; i++) {
        cityArr[i] = [(i+500),"NoGPS","NoTime",false];
    }  
}

//parse incoming string for ID:
//M0310 53 23 2928 001 26 1244 220023 WNGN27S01003000000000003

m = msg.payload;
length = msg.payload.length // 52
prefix = m.substr(0,1) // M
good = m.substr(30,1)  // G

//sanity checking for valid data 
if (length == 52 && prefix == "M" && good == "G"){
   //source radio ident
   let ident = Number(m.substr(2,3));
   let time = m.substr(22,6)
   // HHmmss

   //Push data into array
   if (ident >= 301 && ident <= 327 ){
       siemArr[ident-301] = [ident,msg.payload,time,true]
    }
   if (ident >= 501 && ident <= 507 ){
       cityArr[ident-501] = [ident,msg.payload,time,true]
    }
}
//readout data into tables
//var tableSie = {payload:{identname:name,lat:lat,lon:lon,speed:speed,bearing:bearing,time:time}};
//var tableSie = {payload:{identname:name,lat:lat,lon:lon,speed:speed,bearing:bearing,time:time}};

// Save the arrays back to context ready for next time
context.siemArr = siemArr;
context.cityArr = cityArr;
return [{payload:context.siemArr},{payload:context.cityArr}];

Output to table

<table id="table" border="1">
 <tr>
 <th>Ident</th> 
 <th>GPSdata</th>
 <th>Time</th>
  <th>New</th>
 </tr>
 <tbody>
 <tr ng-repeat="row in msg.payload">
 <td ng-repeat="item in row" >{{item}}</td>
 </tr>
 </tbody>
</table>

Why not only initialise it if the length is 0 - if (siemArr.length === 0)... ?
It should not re-initialise unless it is being reset somewhere.

Is the point of this to create a table for the ui ? I thought the rest of your flow that included that map already did that ?

Not sure what you mean by the data overwriting... I thought that was the point - so that each tram index was updated to the latest every time ?

there may have been a spelling mistake in my previous attempt... an seiArr vs sieArr :slight_smile:
I can feed it direct into a ui-table node no problem ...