Serie Introduction and 'Other' Implementations

In my eyes, serie numbers are required when working with large tables and amounts of data, but I think the short description makes them seem like a way to keep table data in line and not much else. I wanted to share a couple of fun implementations of serie numbers I have come up with and tested over the last months, I hope it gets you thinking about alternate implementations of simple features.

What are serie?

They basically lets you assign an identifying number to a piece of data, with 2 main behaviors:

  • Sending the same device the same variable the same serie but a DIFFERENT value will overwrite the original value associated with the given serie
  • In a dynamic table, all variables with the same serie will be displayed on the same line.

Sample JSON payload with serie:

{
    "variable": "variableName",
    "value": "variableValue",
    "serie": 1
}

Why use them?

Prevent data from shifting over rows like this:

Add commands/request to a table pending the result, and then go back and update the current status or result in real time:

image

Those are the most basic uses, now I’d like to share some (likely unintended) ways to use them! They may be a bit advanced, feel free to comment if you have any questions on my implementation. Also, I’d love to know if you have any other cool serie implementations in your system!

Efficient Device lists

When creating a dynamic device list to display within a dynamic table, I found two options.

  1. Delete all the data and completely repopulate the list on each update. Surprise! It’s extremely slow and deleted data isn’t immediately reflected in dashboards.
  2. Use serie to update individual devices within the list as needed without deleting any data!
  • During device creation, assign each device a unique serie number. You can use the device ID, or have custom values stored as a tag, variable or however else is convenient.
  • Whenever a device is updated and you want to update the device list, look up its serie number.
  • Send data you want to update to the device list, including the device’s unique serie.
  • The data you sent overwrites the preexisting data for this device, and the time column (if used) is updated with the current time.

Because every device has a unique number, this method works even if you use multiple device lists with different or overlapping sets of devices, such as a ‘Global’ list containing ALL device and multiple ‘Local’ lists, each holding a subset of devices.

Global Serie Continuity

If you have several middleware instances running the Tago SDK, each sending data to your devices, how do you make sure they always output their data on the same table row? If each instance simply starts at 1 and increments, how do you prevent data from possibly being overwritten?

I solved this by creating a device called “PersistentData” which holds values such as serie that should remain persistent over all instances. Any time a serie is required, the following function gets the current value, increments it and sends it back. This makes sure that any amount of instances can send each set of data with a unique serie number without risk of overwrites or other complications related to repeated or no serie.

//Get the serie number, increment it within Tago and return the current value
async function getSerie() {
	//Get the current value of serie
	var getSerie = await persistentData.getData({
	  query: "last_item",
	  variable: "serie"
	});
	//Create a new serie to send back, with the serie number incremented
	const newSerie = {
		variable: "serie",
		value: getSerie[0].value + 1
	  };
	//Send the incremented serie number back
	try {
		await persistentData.sendData(newSerie);
		console.log("Successfully inserted new serie value of " + newSerie.value);
	} catch (error) {
		console.log("Error when inserting:", error);
	}
	//Return the current serie to the calling function
	return getSerie[0].value;
}

However, if multiple instances are pushing large amounts of data simultaneously, we likely don’t want to use “await getData” each time; it will cost overall time and there is the possibility that two instances pull the same serie value. In these cases we can modify it similarly to this, letting us only make 1 getData request per X serie numbers while still keeping instances synced properly!

var currentSerie; //What is the current serie number?
var remainingSerie; //How many unused reserved serie remain locally?
const serieIncrement = X; //How many serie to reserve per getData request?
async function getSerie() {
	//Are there remaining numbers locally?
	if(remainingSerie-- > 0){
		//If so, return the current local serie
		return currentSerie++;
	}
	//If not, get the current value of serie
	var getSerie = await persistentData.getData({
	  query: "last_item",
	  variable: "serie"
	});	
	currentSerie = getSerie[0].value; //Store the current serie value locally
	remainingSerie = serieIncrement - 1; //Store how many series you have reserved - 1 for the current one
	//Create a new serie to send back, with the serie number incremented by "serieIncrement"
	const newSerie = {
		variable: "serie",
		value: currentSerie + serieIncrement
	  };	
	  //Send the incremented serie number back
	try {
		await persistentData.sendData(newSerie);
		console.log("Successfully inserted new serie value of " + newSerie.value);
	} catch (error) {
		console.log("Error when inserting:", error);
	}
	//Return the current serie to the calling function and increment it locally
	return currentSerie++;
}

That’s all I have for now; hopefully you learned something new or at least found it interesting!

4 Likes

Hey @andreas.gudmundsson.
Thanks for posting this, it is really useful, I’m certain that it will help a lot of members of Tago.
I would like to increment your topic telling about the series when using a Map widget, it has a similar feature as a Table widget. In the Map widget is possible to group some data of a device in the same infobox using series. For example, the following payload can reproduce something like the image below.

[
   {
    "variable": "speed",
    "value": 21,
   "unit": "km/h",
    "serie": 1568918760037
  },
  {
    "variable": "altitude",
    "value": 31.5626,
    "unit": "feets",
    "serie": 1568918760037
  },
  {
    "variable": "location_car",
    "value": "Spark",
    "location": {
      "lat": -23.550556,
      "lng": -46.633146
    },
  "metadata": {
            "icon": "https://store-guides2.djicdn.com/guides/wp-content/uploads/2017/05/Mini-drone-DJI-Spark-1024x511.jpg",
            "color": "red",
            "img_url": "https://cdn1.appsisecommerce.com.br/clientes/cliente11742/produtos/12091/Z5613.jpg",
            "url": "https://www.dji.com/spark",
            "label": "Spark"
     },
    "serie": 1568918760037
  }
]

grouppedBySerie