Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple sources for the same value #48

Closed
rob42 opened this issue Jan 7, 2015 · 80 comments
Closed

Multiple sources for the same value #48

rob42 opened this issue Jan 7, 2015 · 80 comments
Milestone

Comments

@rob42
Copy link
Contributor

rob42 commented Jan 7, 2015

It is quite possible for a key value to come from more than one device. eg position (lat/lon) could come from several gps enabled devices, and multiple depth sounders are not uncommon. We need a consistent way to handle this.

All the incoming values may well be valid in their own context, and it is feasible that all of them may be wanted, for instance, dispalying depth under each hull on a catamaran.

Hence discarding or averaging is not a solution, and since signalk is unable to derive the best way to handle multiple values it must always fall to a default action, with human over-ride when needed.

I propose we should simply store all the options in the tree, and have the main 'source' reference the options?

Then simple rules can apply:

  1. If its the first value for the key, it becomes the first option and the default value.
  2. If another value with different source arrives, we add to options - if its our preferred source (from persistent config) we auto-switch to it, otherwise we just record it
  3. Users can then view the source options for a key and select from list for a specific display. They might select courseOverGroundTrue.options.ttyUSB1 as the source for a specific need or they may select an average/mean/min/max etc. (This facility is not part of the spec, but implementation specific)
  4. maybe needs a rule to trim unreferenced options (or all options) on output.
{"vessels":
    {"self":
        {"navigation":
            {"courseOverGroundTrue":
                {
                 "value": 102.29,
                 "source": "options.actisense",
                 "options":
                    {
                    "ttyUSB1":  **just a unique name, have to generate somehow from incoming
                        {  
                            "value": 99.2900009155,
                            "source": "/dev/ttyUSB1"
                            "timestamp": "2014-08-15-16:00:00.081",
                        },
                     "actisense":
                        {
                            "value": 102.29,
                            "source": "/dev/actisense",

                            "timestamp": "2014-08-15-16:00:00.081",
                            "src": "115",
                            "pgn": "128267"
                        }
                    }
                }
            }
        }
    }
}
@tkurki
Copy link
Member

tkurki commented Jan 8, 2015

Thanks @rob42 for kicking this off.

I'll pitch in with my TDD hat on and commit a test for the new format. I modified your example a little:

  • pgn was wrong
  • added complexity: two nmea0183 sources with different sentences and two n2k sources on one bus with different pgns

I think that the way the identifer is constructed should be specified:

  • n2k: producerid-pgn-sourceid (producer id from server configuration, others from n2k data)
  • nmea0183: producerid-talkerid-sentence (like n2k)

BTW we have neglected to include nmea0183 talker id, it should be added to the schema as well.

The next step would be to fix the schema so that the test passes for the sample. You can run the test after npm install with npm test.

PS. The test code assumes that the input is without the vessels.123456789 preamble, that ought to be fixed as well.

@tkurki tkurki mentioned this issue Jan 8, 2015
@tkurki
Copy link
Member

tkurki commented Jan 8, 2015

I added Travis configuration for this and opened pull request #49 for this so now we have CI integration as well.

@rob42
Copy link
Contributor Author

rob42 commented Jan 8, 2015

Also need the extra rule to make it clear that we have options object:
4) If there is an 'options' object, then source = options.name
Or we could always have 'options', but that would be verbose for most cases...

@tkurki
Copy link
Member

tkurki commented Jan 8, 2015

Where should the additional documentation like rules 1-4 go?

@rob42
Copy link
Contributor Author

rob42 commented Jan 9, 2015

I would put it as a comment in relevant code, and I will write up a page for the web site under 'developers'.
Wonder if I can add it to a readme.md in the specification repo?

@tkurki
Copy link
Member

tkurki commented Jan 9, 2015

@pod909
Copy link

pod909 commented Jan 25, 2015

The prefered source of data probably belongs in a manifest (seperate structure defining the resources available from a vessle). Such a manifest would list the readings available from a vessel, a list of devices and contain the preferred mapping between them. If a date stamp was also included that would remove the need to litter the basic data with souce and timing data

By declairing the devices (with readings available) in a manifest and specifying a suitable URI structure for SignalK you would give the user the choice to query the individual device for data using the existing SignalK schema with out the need to add additional "option" tags.

@tkurki
Copy link
Member

tkurki commented Jan 25, 2015

We have previously discussed the need for a mapping layer in case there is need for server side processing of for example derived values. This would be part of the server configuration.

But I think that the simplest case should be supported with minimun a priori knowledge, eg. the server should just need minimum configuration to connect the producers on board (n2k bus, nmea sources, other sources) and then be able to pass that data on.

With the current implementations there is a de facto registry of all available data in the REST api contents.

There are some ideas about this type of metadata in https://github.com/SignalK/specification/blob/master/schemas/groups/sensors.json.

About timestamp: I think it belongs right next to the value as it is metadata about that particular value. It is also part of the streaming data: 'new value is x with timestamp y'.

@tkurki
Copy link
Member

tkurki commented Jan 25, 2015

More stuff available at SignalK/signalk.github.io#17

@pod909
Copy link

pod909 commented Jan 25, 2015

Appologies if this is going over old ground. I suspect I'm coming at this from a different angle..

Because this moves SignalK towards being essentially just a super transport of other messaging a user is going to need considerable prior knowlage of the exact configuration of the vessel -- including knowlage of whcih components are reliably calibrated and the way it's going the structure of the none SignalK messaging its self -- to use SignalK sucessfully.

Coming to the vessel from the outside with none of that knowlage a menu of possable readings for each value is going to be confusing at best. So either some work is done on the 'server' to pick the right source or there's a need for a manifest to tell me which is the best reading to use from the range provided.

Achitecture wise it's also pushing SignalK towards a particular and more expensive implementation model (all the work done at the end applicaiton) that doesn't allow for work to be distributed over the network. Where as a good scheme, or one that it's hoped will have a wide take up, should leave these architecture questions open. Otherwise there's a danger SignalK is going to end up tied to the implementation found here and go no further.

My interest is in using SignalK end-2-end from sensor to display via processing. Then from the boat to a nework of boats and to the internet. With a large number of derived values being generated in the chain, some of which go back in the opposit dirrection. With the right SignalK schema I'd be quite happy to translate to SignalK at the device where it's not implemented natively.

I'd like to see a single data structure to represent all the data available from a single location-device i.e. a single data structure that can be used to pass data from a device to intra location processing AND once processed out to the outside world. As start point the sensor data object should have the same structure as the vessel object, rather than the vessel object (which may not be my frame of reference for makign a query) containing all sensor values.

Intelegent multiplexing (with an explaination of how it was done and the alternative sources that can be seperatly RESTed if wanted) of the data enables me to expose my knowlage of the local system set-up in the data. The way you're taking this the use of SignalK from the device is prety unatractive and if I have to pay for N2K from device to multiplexer then I might as well stick with it e2e.

Time stamp

For me the timestamp should be abstracted to the sensor object with all the values coming from that sensior referencing the sensor (which I'd do in the manifest as the mapping is prety static). That way all the data can be left in the vessel object rather than being seperated out into different sensor payloads that I have to process back together again.

Timestamping for delta data belongs in the packet header no?

@keesverruijt
Copy link
Member

@pod909, I can't really make out what you are saying. On an abstract level, and on a detailed technical level. Maybe it's just me though.

@tkurki
Copy link
Member

tkurki commented Jan 25, 2015

@pod909 Maybe a real life practical example would help clarify your viewpoint?

As for "a menu of possable readings for each value is going to be confusing at best": on my boat the menu would contain items like

  • navigation.position
  • navigation.courseOverGroundTrue
  • navigation.speedThroughWater
  • navigation.speedOverGround
  • enviroment.wind.speedApparent
  • enviroment.wind.angleApparent
  • enviroment.depth.belowTransducer

Often there is only one source for each data item. All this can be gleaned from the delta messages in a minute or so or fetched from the server when the display starts.

I fail to see that list as confusing to pretty much anybody with a little navigation background.

@pod909
Copy link

pod909 commented Jan 25, 2015

no problem with that list if the data was at that level but the there's alread value, source, timestamp below that level and the propsal is to add multiple option values as well isn't it?

@tkurki
Copy link
Member

tkurki commented Jan 25, 2015

@pod909 The user interface does not need to show all the information that it has.

For example https://github.com/SignalK/instrumentpanel/ populates the grid as it receives new data items, creating cells when it sees new paths it hasn't seen before. Even if the information it receives has timestamp and source it only shows the name of data item, for exaple navigation.courseOverGround, and the value.

Please take also a look at Delta messages at http://signalk.org/dev/messageFormat.html

This addition to the spec is about having multiple values for a given data item, say navigation.courseOverGround, in the REST data model. Current delta message format already allows updates to one data item from multiple sources, for example from multiple GPS's.

@rob42
Copy link
Contributor Author

rob42 commented Aug 6, 2015

The handling of multiple values is superceeded by the method detailed in https://github.com/SignalK/specification/wiki/Multiple-Values-Handling, so closing this one

@rob42 rob42 closed this as completed Aug 6, 2015
@faceless2
Copy link

Adding multiple values as described at https://github.com/SignalK/specification/wiki/Multiple-Values-Handling doesn’t seem to work: here’s a section:

"self": {
      "navigation": {
        "courseOverGroundTrue": {
          "timestamp": "2014-08-15-16: 00: 01.083",
          "value": 102.29,
          "source": "vessels.self.sources.n2k.actisense-115-129026"
        }
      },
      "sources": {
        "n2k": {
          "actisense-115-129026": {
            "value": 102.29,
            "bus": "/dev/actisense",
            "timestamp": "2014-08-15-16: 00: 01.083",
            "src": "115",
            "pgn": "129026"
          },

The trouble is the value 102.29 in the “source” isn’t linked to the key “courseOverGroundTrue”. If that source also provided us with other values, I’d have no way of distinguishing them.

I presume this is a solved problem and that page just hasn’t been updated (and if by chance it’s not, changing the “value” in the source to a tree of values (eg : { “self”: { “navigation”: { “courseOverGroundTrue”: 102.29 } } }) would do it… although this doesn’t allow for different timestamps for different values)

(this is an edited repost of my comments on Slack here, for reference)

@tkurki
Copy link
Member

tkurki commented Aug 18, 2015

I noticed the same thing a few days ago when I resurrected my work on this.

The problem isn't really solved, but imho the solution presented at the beginning of this issue is the best if not perfect option and I'm going to move in that direction in the node ref implementation.

The wiki needs updating, but I would like to verify the thinking with some working software before a real update. I'll throw in a label that the wiki version is not normative and has problems.

@tkurki tkurki reopened this Aug 18, 2015
@fabdrol
Copy link
Member

fabdrol commented Aug 25, 2015

Just a thought; we could approach this differently:

  • we consider the value of a property to be the primary value. Which one to put there is inferred by server using the priority of the source (e.g. taken from sensors or sources group).
  • we add a optional key called alternatives or something similar which lists alternative values by source id, ordered by priority.

Example:

"01010101": {
  "navigation": {
    "courseOverGroundTrue": {
      "timestamp": "2015-08-25T08:12:17.824Z",
      "value": 102.29,
      "source": {
        "label": "aft compass",
        "type": "compass",
        "id": "sensor1"
      },
      "alternatives": {
        "sensor2": {
          "timestamp": "2015-08-25T08:12:17.824Z",
          "value": 100.11,
          "source": {
            "label": "center compass",
            "type": "compass",
            "id": "sensor2"
          }
        },
        "sensor2": {
          "timestamp": "2015-08-25T08:12:17.824Z",
          "value": 101.2,
          "source": {
            "label": "center compass",
            "type": "compass",
            "id": "sensor2"
          }
        }
      }   
    }
  }
}

A consumer could choose to simply use the primary value, or present a list of options to the user to switch to another value. The second source property in the alternatives might seem unnecessary, but I think it is important that the contents of each object in the alternatives has the same structure as the primary value since some values (e.g. position) follow a different structure (it has position.latitude, position.longitude and position.altitude instead of position.value).

@faceless2
Copy link

JavaScript maps are unordered :-) But of course if you needed to order them you could add a priority field, or you could leave them unprioritised - realistically determining priority beyond the primary might not be much use: I have 5 GPS sources on my boat, I know which one I want to use, if that's not working, well I suppose it's whichever one has a signal. So long as it's consistent.

Personally, I quite like having the alternatives bundled off in a separate section of the datamodel. There are two reasons for this.

  1. I haven't investigated the syntax for subscription yet, but if I subscribe to navigation.courseOverGround I probably don't care that about the 8 alternatives, their timestamps and labels. It's easier to subscribe to navigation/* than navigation/*[not(options)], or however it is the syntax goes - my xpath is a bit rusty, but you get the idea.
  2. Sources will typically provide more than one data value, which means if you "interleave" the various options for each data value into the main model, you're going to repeat a lot of information unless you have some sort of sources object and refer to the source by ID. You also won't be able to easily identify all values from a particular source.

I can think of three broad ways to handle this from a datamodel point of view, I'll sketch them out here. All the models below present the same information - course and speed from two different local sources (I'm assuming that sources for data can come from other vessels - kind of the point of SignalK after all - so sources are under vessels.self as in the original example in this issue).

Option 1

First, the "primary values only stored in the main datamodel, alternatives kept with their sources" option. This is the one I'd advocate. It's also only a slight variation on the model described on the wiki.

{
    "navigation": {
        "courseOverGround": {
            "value": 109.2,
            "timestamp": "2015-08-25T08:12:17.824Z",
            "source": "vessels.self.dev1"
        },
        "speedOverGround": {
            "value": 4.2,
            "timestamp": "2015-08-25T08:12:17.824Z",
            "source": "vessels.self.dev1"
        }
    },
    "sources": {
        "vessels": {
            "self": {
                "dev1": {
                    "label": "Masthead GPS",
                    "timestamp": "2015-08-25T08:12:17.824Z",
                    "bus": "n2k:/dev/actisense",
                    "values": {
                        "navigation": {
                            "courseOverGround": 109.2,
                            "speedOverGround": 4.2
                        }
                    }
                },
                "dev2": {
                    "label": "VHF Internal GPS",
                    "timestamp": "2015-08-25T08:12:17.824Z",
                    "bus": "nmea1083:/dev/ttyUSB4",
                    "values": {
                        "navigation": {
                            "courseOverGround": 108.7,
                            "speedOverGround": 4.3
                        }
                    }
                }
            }
        }
    }
}

Advantages: primary data (eg navigation, environment) is kept concise (which should simplify subscribing to just the values and not the alternatives). It's easy to determine all data from a particular source should you wish to. Disadvantages, there's no easy way to reflect two updates from an individual source with different timestamps (e.g if GPS position was updated once a second, but course/speed only every five seconds). I'm unsure if this matters.

Option 2

The model describe by fabdroi above, where the options are included inline with the primary datamodel. I've added a primary key to each option to indicate which value was chosen, but you could indicate the chosen alternative in any number of ways.

{
    "navigation": {
        "courseOverGround": {
            "value": 109.2,
            "sources": {
                "vessels": {
                    "self": {
                        "dev1": {
                            "label": "Masthead GPS",
                            "timestamp": "2015-08-25T08:12:17.824Z",
                            "bus": "n2k:/dev/actisense",
                            "value": 109.2,
                            "primary": true
                        },
                        "dev2": {
                            "label": "VHF Internal GPS",
                            "timestamp": "2015-08-25T08:12:17.824Z",
                            "bus": "nmea0183:/dev/ttyUSB4",
                            "value": 108.7
                        }
                    }
                }
            }
        },
        "speedOverGround": {
            "value": 4.2,
            "options": {
                "vessels": {
                    "self": {
                        "dev1": {
                            "label": "Masthead GPS",
                            "timestamp": "2015-08-25T08:12:17.824Z",
                            "bus": "n2k:/dev/actisense",
                            "value": 4.2,
                            "primary": true
                        },
                        "dev2": {
                            "label": "VHF Internal GPS",
                            "timestamp": "2015-08-25T08:12:17.824Z",
                            "bus": "nmea0183:/dev/ttyUSB4",
                            "value": 4.3
                        }
                    }
                }
            }
        }
    }
}

Advantages: there is no separate sources object, everything is in the one place. Disadvantages: everything is in the one place, and data must be repeated.

Option 3

A halfway-house, where the option values are stored in the primary datamodel, but the details on the various sources are stored elsewhere:

{
    "navigation": {
        "courseOverGround": {
            "value": 109.2,
            "source": "vessels.self.dev1",
            "timestamp": "2015-08-25T08:12:17.824Z",
            "options": {
                "vessels": {
                    "self": {
                        "dev1": {
                            "value": 109.2,
                            "timestamp": "2015-08-25T08:12:17.824Z"
                        },
                        "dev2": {
                            "value": 108.7,
                            "timestamp": "2015-08-25T08:12:17.824Z"
                        }
                    }
                }
            }
        },
        "speedOverGround": {
            "value": 4.2,
            "source": "vessels.self.dev1",
            "options": {
                "vessels": {
                    "self": {
                        "dev1": {
                            "value": 4.2,
                            "timestamp": "2015-08-25T08:12:17.824Z"
                        },
                        "dev2": {
                            "value": 4.3,
                            "timestamp": "2015-08-25T08:12:17.824Z"
                        }
                    }
                }
            }
        }
    },
    "sources": {
        "vessels": {
            "self": {
                "dev1": {
                    "label": "Masthead GPS",
                    "timestamp": "2015-08-25T08:12:17.824Z",
                    "bus": "n2k:/dev/actisense"
                },
                "dev2": {
                    "label": "VHF Internal GPS",
                    "timestamp": "2015-08-25T08:12:17.824Z",
                    "bus": "nmea1083:/dev/ttyUSB4"
                }
            }
        }
    }
}

Advantages: data is not repeated, all alternative values are in the main section of the datamodel. and one source can set data values with different timestamps. Disadvantages: not as brief as option 1, syntax for subscribing to just the primary values is not obvious, and you still can't identify all values from a particular source.

@tkurki
Copy link
Member

tkurki commented Aug 31, 2015

Thanks for the input. You raised an issue at least I haven't thought about before: there is currently no way to subscribe to values from a specific source.

@tkurki
Copy link
Member

tkurki commented Aug 31, 2015

Imho the values should be available logically near each other.

In your first example: how you do discover the alternative values for a data item without going through all the sources - and I assume that the sources would have all the sources on the vessel, so you would essentially have a list of all the values in a rather awkward structure.

In option 2/3 I don't get the point for the vessels.selfpart under sources (btw one is sources, other is options?). Skipping vessels.self down there it is pretty much the same as in the beginning of this thread.

The sources section in 3 is not very informative - what does the timestamp stand for there, since there is no value present? Last update - but what update?

@tkurki
Copy link
Member

tkurki commented Aug 31, 2015

I think we should also think about this as a REST api and the urls.

.../navigation/speedThroughWater should give me "best" value and metadata about it (like that there are optional/alternative values).
.../navigation/speedThroughWater/options should give me all the values and metadata about how the server considers the different options. The response should also give me a way to address a specific value in case I want just that one.

Both responses should point to other resources, like sensors and say their related calibration values, if it is not logical or convenient to represent that data as part of this resource.

@faceless2
Copy link

  1. Yes, you're right - in option one, there's no easy way to identify the list of alternatives. I've been thinking about it from a server POV until now - I have the alternatives available, I want somewhere to store them in the model. But I have to admit I'm still not clear why a client would ever need to use an alternative value rather than the specified primary value. The only example I recall where alternatives would be of use was two depth sounders, one under each hull of a cat. But for this, and all other cases I can conceive of, the client knows exactly what field it's requesting, and from what sensor it's requesting it
  2. The explicit reference to vessels.self is in all of the options because a) I wanted to make sure they all represented exactly the same data, and b) I've presumed that it's possible (in theory at least) to get data values from another vessel - e.g. I sail past a boat/buoy/marina that's broadcasting a SignalK model and has a barometer, and I do not - now I can incorporate their data. If this isn't needed then it can be dropped, of course. It was "sources" in the original example and "options" in fabdroi's example - wasn't worried about that as these were just sketching out ideas for structure, not intended as final suggestions.
  3. Yes you're right about timestamp in option 3, pointless - should be dropped.
  4. I'm not familiar with the API for subscribing to parts of the model - I've presumed there is one and that 99% of the time you won't care about the options or where it came from, just the primary value for that field. If that's not done yet, then yes I'd agree it probably needs fleshing out before this goes much further - perhaps with something like json-path (or some other xpath variation for JSON) rather than reinventing the wheel.

@tkurki
Copy link
Member

tkurki commented Sep 1, 2015

The assumption that there is always one primary value for each data item is not valid.

A Signal K consuming client, like some kind of electronic dashboard, must be able to provide a list of the alternative values. After that it may know exactly what it wants, but discovery and exploration of the available items should be easy.

For example in a case of malfunction (for example a stuck log wheel) you may want to switch to another source.

Imho data from another entity should not be present under vessels.self.

We need to support the use case of a gauge that wants to subscribe to updates from the port hull sounder.

See http://signalk.org/developers/subscription_protocol.html

@fabdrol
Copy link
Member

fabdrol commented Sep 1, 2015

If we drop the assumption that there always is a primary data source, we could simply drop the idea of having to specify a "primary" or a priority. That would make the schema simpler:

1. one sensor, one value, indicated by using value:

{
  "navigation": {
    "speedThroughWater": {
      "timestamp": "2015-08-25T08:12:17.824Z",
      "value": 9.5,
      "source": {
        "label": "Airmar DST-800",
        "uuid": "22E63B84-749D-489B-A872-8B0D62507C54",
        "type": "NMEA0183",
        "capabilities": ["DBT", "DPT", "VHW", "VLW", "MTW"]
      }
    }
  }
}

2. Multiple sensors, multiple values. Consumers are responsible for choosing one, either by UI or (if a simpler consumer) simply selecting the first one:

{
  "environmental": {
    "depth": {
      "belowTransducer": {
        "values": {
          "starboard-hull": {
            "timestamp": "2015-08-25T08:12:17.824Z",
            "value": 9.5,
            "source": {
              "label": "Starboard DST-800",
              "uuid": "9801FBF0-76A3-47DD-ACA6-D76DC405F95C",
              "type": "NMEA2000",
              "capabilities": [59392, 600928, 126208, 126464, 126464, 126996, 128259, 128267, 128275, 130310, 130311, 130312]
            }
          },

          "port-hull": {
            "timestamp": "2015-08-25T08:12:17.824Z",
            "value": 6.5,
            "source": {
              "label": "Port DST-800",
              "uuid": "C7B2F2B7-2D49-475A-B0A1-F1E7E840607D",
              "type": "NMEA0183",
              "capabilities": ["DBT", "DPT", "VHW", "VLW", "MTW"]
            }
          }
        }
      }
    }
  }
}

3. One could even get rid of the two different notations and simply have a values map on every data property. With one source, this map contains just one value, otherwise more than one:

{
  "environmental": {
    "depth": {
      "belowTransducer": {
        "values": {
          "dst800": {
            "timestamp": "2015-08-25T08:12:17.824Z",
            "value": 9.5,
            "source": {
              "label": "Starboard DST-800",
              "uuid": "9801FBF0-76A3-47DD-ACA6-D76DC405F95C",
              "type": "NMEA2000",
              "capabilities": [59392, 600928, 126208, 126464, 126464, 126996, 128259, 128267, 128275, 130310, 130311, 130312]
            }
          }
        }
      }
    }
  }
}

In case of option 2/3 and subscriptions, if a user subscribes to ~/environmental/depth/belowTransducer the user simply receives updates for both values (not that different from when a user subscribes to a higher up path like ~/environmental/depth, where he would receive updates for belowTransducer, belowKeel etc).
REST is not an issue either: when requesting /signalk/v1/api/self/environmental/depth/belowTransducer the user simply receives the contents of the values map:

{
  "values": {
    "starboard-hull": {
      "timestamp": "2015-08-25T08:12:17.824Z",
      "value": 9.5,
      "source": {
        "label": "Airmar DST-800",
        "uuid": "22E63B84-749D-489B-A872-8B0D62507C54",
        "type": "NMEA0183",
        "capabilities": ["DBT", "DPT", "VHW", "VLW", "MTW"]
      }
    },

    "port-hull": {
      "timestamp": "2015-08-25T08:12:17.824Z",
      "value": 6.5,
      "source": {
        "label": "Airmar DST-800",
        "uuid": "22E63B84-749D-489B-A872-8B0D62507C54",
        "type": "NMEA0183",
        "capabilities": ["DBT", "DPT", "VHW", "VLW", "MTW"]
      }
    }
  }
}

I did include timestamps everywhere as a consumer could use that information for determining what value to use. For instance, a research vessel might have many identical sensors, each transmitting at 1hz, running a consumer that logs this into a database. The consumers will simply pick the latest value in order to get more data points than once a second.

@rob42
Copy link
Contributor Author

rob42 commented Sep 1, 2015

SInce a single source (like GPS RMC message) can have several diverse values, we should support the idea of the source being a reference to more data. So we can use the same rule as elsewhere, if source is a string its a reference to another location, if its an object it holds the source data directly.
Then we can benefit from a many-to-one type of simplification.

{
    "environmental": {
        "depth": {
            "belowTransducer": {
                "timestamp": "2015-08-25T08:12:17.824Z",
                "value": 6.5,
                "source": "sources.n2k.some-unique-name",
                "values": {
                    "dst800": {
                        "timestamp": "2015-08-25T08:12:17.824Z",
                        "value": 9.5,
                        "source": "sources.n2k.some-other-unique-name"
                    }
                }
            }
        }
    },
    "sources": {
        "n2k": {
            "some-unique-name": {
                "label": "StarboardDST-800",
                "uuid": "9801FBF0-76A3-47DD-ACA6-D76DC405F95C",
                "type": "NMEA2000",
                "capabilities": [....]
            },
            "some-other-unique-name": {
                "label": "PortDST-800",
                "uuid": "7701FBF0-5546-47DD-ACA6-D76DC405F95C",
                "type": "NMEA2000",
                "capabilities": [....]
            }
        }
    }
}

That also means we can store config and lots of misc data like version, manufacturer etc without polluting the model. And its still easily available when needed.

In practice I think that multiple values will be the minority, but still important. So I like the idea of a simple value key for a simple value.

For the multple values I think we can then use values is a map of alternative values. This means they are discoverable, and using source refs, not too verbose. So the rules:

  1. if there is a value key, thats the only or 'official' value.
  2. If there is a values key, that holds alternatives
  3. If there is no value key, pick your choice from values

@faceless2
Copy link

  1. @tkurki , your point about vessels.self has just dawned on me - I'd overlooked that the "navigation" entry is already under vessels.self.

  2. @fabdrol, I'd strongly suggest that the server needs to be able to specify a primary value for some fields. I have two electronic compasses, one is +/- about 5 degrees, one is much more accurate. And one of my water temperature sensors keeps adding about 5° to the ambient temperature - sadly, I have to prefer the other one. The server can be told this in its configuration, the client can't, so I don't want to rely on whichever one happens to be parsed first in the data structure.

    Remember, the implied order that you see when the data is serialised is not necessarily the order the keys will be stored in the client environment. The order is not only undefined, it's unstable - add another key to the map and the order may change, which means a display that just chooses the "first" value may oscillate unpredictable between multiple sources.

  3. I really like the "value" vs "values" idea (and accompanying example) - I think it covers all possibilities without being too verbose. The only other thing I'd suggest is as well as (or instead of) "capabilities", which is hardware/source/bus-dependent, perhaps a "provides" key to identify the keys in the model that this source has provided values for - e.g. provides: ["environment.depth.belowTransducer"]. This would allow you to find all the values in the model provided by a particular source without having to walk the entire tree (aside: I see arrays have been downgraded from evil to tolerated...)

@faceless2
Copy link

A few thoughts:

  1. If you specify "value" as "holds the best value according to the server, which may be the latest or some other preferred value" then you'd cover this either way. It would be nice to allow the server to make the decision if the information is available, and not have the spec constrain it to just reporting the latest value.

  2. Crazy not to use this opportunity to give each source a URN - e.g."self://org.signalk.n2k.115.128259", or (bearing in mind these will be used as keys in the "value" map) "self_n2k_115_128259". That you're only comparing one key to map from "source" to "values" is just the first benefit - it also allows for "self_n0183_dev_ttyACM0" or whatever without defining new keys (some of us are still on the old bus, thanks - no "pgm" here).

    The exact schema for URN can be defined later, but using a single, unique, vaguely hierarchical and vaguely human readable key for each source is, I think, a key point and a will make life easier everywhere: the UI (when choosing a sensor), logging (when identifying input from a sensor), broadcasting (should it ever come to the point where one boat has access to anothers sensor data), and new buses (the future Signal-K certified custom hardware I'm sure we're all working on :-)

  3. Maybe I'm being thick but given your comments I'm expecting to see "navigation.speedThroughWater.value" in the above example, and I don't. Assuming it's supposed to be there (and I think it should be), I agree with Rob's observation

@timmathews
Copy link
Member

@faceless2,

  1. I believe the source for the top level value can change depending on logic in the server, the spec doesn't constrain it. It also doesn't require that the server have this capability. It's entirely possible for a server implementation to only populate value from the source the user selects.

  2. Agreed, better identifiers are required, and they need to support busses other than n2k. We've proposed using "maritime resource names" for vessel identification, these look like urn:mrn:signalk:uuid:<uuid>. We could (should) use a similar thing here. And to keep them shorter we can incorporate IPv6 shorthand notation: keep the colons, leave out redundant stuff like so: urn:::n2k:<name>:<pgn>.

    See below for more on what <name> means.

  3. Yeah, I think value got left out in the example above.

Identifiers for values members

NMEA 0183: urn:::n0183:<device>:<instance>:<sentence>
NMEA 2000: urn:::n2k:<name>:<pgn>

For n2k, <name> by default should be a 32-bit integer representing the unique identifier (ISO Identity Number) and manufacturer ID (these are fields 1 and 2 of PGN 60928 - ISO Address Claim). The server needs to maintain a mapping between these and the source address of the device. <name> can be overridden by the user, so something more friendly can be applied like port-depth or aft-gps.

For n0183, <device> is the serial port name (e.g. ttyS0 or COM1). We need <instance> to support NMEA 0183 multiplexers like the Actisense NDC-4. According to the docs for the NDC-4, it can be configured to tag sentences to identify the originating device, see NDC FAQ #2. I don't have one of these, so I'm not clear on how it works, but it's a scenario we need to support. Whatever it uses to ID the device would go in the <instance> field.

That said, I think we may be able to leave the <sentence> and <pgn> parts off because even if a device transmits multiple PGNs or sentences for the same data (RMA/RMB/RMC sentences and the 1303xx PGNs for instance), the value will be the same since it's coming from the same piece of hardware. Is there another compelling reason to include <sentence> or <pgn> in the source identifier?

@pod909
Copy link

pod909 commented Nov 14, 2015

SI this level of source information really necessary for a v1 spec?

On 14 November 2015 at 07:21, Tim Mathews notifications@github.com wrote:

@faceless2 https://github.com/faceless2,

I believe the source for the top level value can change depending on
logic in the server, the spec doesn't constrain it. It also doesn't require
that the server have this capability. It's entirely possible for a server
implementation to only populate value from the source the user selects.
2.

Agreed, better identifiers are required, and they need to support
busses other than n2k. We've proposed using "maritime resource names" for
vessel identification, these look like urn:mrn:signalk:uuid:. We
could (should) use a similar thing here. And to keep them shorter we can
incorporate IPv6 shorthand notation: keep the colons, leave out redundant
stuff like so: urn:::n2k::.

See below for more on what means.
3.

Yeah, I think value got left out in the example above.

Identifiers for values members

NMEA 0183: urn:::n0183:::
NMEA 2000: urn:::n2k::

For n2k, by default should be a 32-bit integer representing the
unique identifier (ISO Identity Number) and manufacturer ID (these are
fields 1 and 2 of PGN 60928 - ISO Address Claim). The server needs to
maintain a mapping between these and the source address of the device.
can be overridden by the user, so something more friendly can be
applied like port-depth or aft-gps.

For n0183, is the serial port name (e.g. ttyS0 or COM1). We need
to support NMEA 0183 multiplexers like the Actisense NDC-4.
According to the docs for the NDC-4, it can be configured to tag sentences
to identify the originating device, see NDC FAQ #2
http://actisense.com/products/nmea-0183/ndc-4/faqs-multiplex4.html?faqitem=faqitem2.
I don't have one of these, so I'm not clear on how it works, but it's a
scenario we need to support. Whatever it uses to ID the device would go in
the field.

That said, I think we may be able to leave the and parts
off because even if a device transmits multiple PGNs or sentences for the
same data (RMA/RMB/RMC sentences and the 1303xx PGNs for instance), the
value will be the same since it's coming from the same piece of hardware.
Is there another compelling reason to include or in the
source identifier?


Reply to this email directly or view it on GitHub
#48 (comment)
.

@tkurki
Copy link
Member

tkurki commented Nov 14, 2015

Two separate issues to pgn/sentence:

  • is it included in the model?
  • is it used to distinguish sources?

Imho it needs to go into the model, distinguishing is debatable.

@tkurki
Copy link
Member

tkurki commented Nov 14, 2015

I think the model should support also passive capture playback eg situation where we have n2k query capability, just a dump of n2k traffic.

@tkurki
Copy link
Member

tkurki commented Nov 14, 2015

Id scheme must support also non-nmea sources.

@tkurki
Copy link
Member

tkurki commented Nov 14, 2015

Imho sentence/pgn should be there also for debugging purposes. When I see the value I want to be able to trace back where it came from.

@tkurki
Copy link
Member

tkurki commented Nov 14, 2015

For N2K and NMEA0183 we need to support multiple inputs/buses. In the current node server this is generated from configuration file, eg. it is user configurable per source.

Non-serial line NMEA0183 sources exits, for example udp and tcp, so referring to the input as <device> is too restricting.

We must support multiple N2K buses and NMEA0183 inputs and non-nmea sources.

Current schema has label field in source object, which is imho the best solution.

@tkurki
Copy link
Member

tkurki commented Nov 14, 2015

This is what node server (multiple-values branch) outputs now with simulated multiple sources, eg. the same n2k and nmea0183 files configured twice:

{
    "speedThroughWater": {
        "value": 3.1277777777777778,
        "source": {
            "type": "NMEA0183",
            "src": "VHW",
            "label": "nmea2"
        },
        "timestamp": "2015-11-14T15:18:33.000Z",
        "values": {
            "nmea1:VHW": {
                "value": 3.1361111111111106,
                "source": {
                    "type": "NMEA0183",
                    "src": "VHW",
                    "label": "nmea1"
                },
                "timestamp": "2015-11-14T15:17:56.000Z"
            },
            "nmea2:VHW": {
                "value": 3.1277777777777778,
                "source": {
                    "type": "NMEA0183",
                    "src": "VHW",
                    "label": "nmea2"
                },
                "timestamp": "2015-11-14T15:17:57.000Z"
            },
            "n2k1:115:128259": {
                "value": 3.6,
                "source": {
                    "label": "n2k1",
                    "type": "NMEA2000",
                    "pgn": 128259,
                    "src": "115"
                }
            },
            "n2k2:115:128259": {
                "value": 3.6,
                "source": {
                    "label": "n2k2",
                    "type": "NMEA2000",
                    "pgn": 128259,
                    "src": "115"
                }
            },
            "n2k1:160:128259": {
                "source": {
                    "label": "n2k1",
                    "type": "NMEA2000",
                    "pgn": 128259,
                    "src": "160"
                }
            },
            "n2k2:160:128259": {
                "source": {
                    "label": "n2k2",
                    "type": "NMEA2000",
                    "pgn": 128259,
                    "src": "160"
                }
            }
        }
    }
}

@tkurki
Copy link
Member

tkurki commented Nov 14, 2015

Talker id is used in NMEA0183 multiplexers to identify the originating device / system, see for example http://www.navstore.com/1120-shipmodul-miniplex-2s-advance-nmea-0183-multiplexer.html and should be included in the identifier.

Nmea0183 parse doesn't yet support it: SignalK/nmea0183-signalk#37

@timmathews
Copy link
Member

Reading the manual for the Actisense device, it sounds like they handle this differently. It sounds like they inject a proprietary sentence prior to the multiplexed sentence to identify the source. Maybe Phil could donate one so we can make sure it works correctly? 😄

It sounds to me like changing the talker ID the way the ShipModul device does, i.e. GP -> G1, G2, etc, technically breaks the NMEA 0183 spec since there is a defined list of standard IDs. We need to support it, but we also need to support what Actisense is doing.

What other 0183 multiplexers are out there?

@faceless2
Copy link

1 x 8-port USB hub and 8 x FTDI RS232->USB boards :-) And it's a bit cheaper than that Actisense box too.

Personally, I think so long as the URN is unique, I can't visualise many situations where it's going to be broken down and parsed. I kind of agree with pod909 here - while you obviously need a URN for a sensor, it should be designed with more than just this issue in mind and this thread might not be the best place for it.

Having said that, I can't resist :-)

First, I'd suggest that trying to cater for the specific Actisense box is the wrong approach, as you can guarantee if you design their method into the URL, you'll find something that takes a different one tomorrow.

HTTP URLs have the format protocol:host/path?options, which covers everything you need here. Some hypothetical examples if we followed that sort of model:

  1. nmea0183:///dev/ttyACM1?talker=VHW&instance=2
  2. n2k://self/160/128259
  3. n2k:///160/128259
  4. nmea0183:///tcp/8080?talker=VHW
  5. json:///dev/ttyUSB1
  6. wifi://mrn:itu:mmsi:538070999/
  7. internal:///declination/

Explanations:

  1. nmea0183 from /dev/ttyACM1 where a multiplexer has identified the instance as device 2
  2. N2K feed from pgn 160, sentence 128259 on the local boat
  3. Same as 2, if we assumed "self" when host was missing
  4. NMEA0183 relayed over TCP
  5. I've got some boards that spit out raw JSON rather than 0183 or N2K. Just merge it with the model directly, much easier.
  6. Wildly hypothetical example of what it might look like if you could get a sensor feed from a passing vessel (based on very interesting recent post to SignalK mailing list about URN naming of resources by Kaspar Neilsen)
  7. If you had an internal module that took lat/lon and addded information about magnetic declination to the model, it could look like this.

I'm not wedded to the exact syntax, but if this is going to extend I think you need to allow for:
a) unlimited number of protocols
b) host (i.e. vessel) should be specifiable, although "self" would be the implied default
c) a flexible list of options.
d) the rules for "path" component should vary depending on the protocol.

Final point for this post. I'd question the need for anything more than the URN for the device in the values list above: for example:

        "n2k2:160:128259": {
                "source": {
                    "label": "n2k2",
                    "type": "NMEA2000",
                    "pgn": 128259,
                    "src": "160"
                }
            }

Why do we have the "source" map here? All the information we need is in the URN already - this is a very verbose way of saying the same thing twice.

And even if the URN doesn't contain everything there is to know about the device, I'm not sure all this static, unchanging information should be broadcast with every property that the device sets. A GPS could set lat/lon/vmg/cog/sog plus umpteen properties about satellites. Here, the source map above would be included with each property, vastly inflating the size of the model. If additional information about a sensor is required, it could be placed in a supplementary map and keyed on the same URL.

I would propose instead:

{
    "speedThroughWater": {
        "value": 3.1277777777777778,
        "source": "nmea0183:///dev/ttyUSB0?sentence=VHW?instance=2"
        "timestamp": "2015-11-14T15:18:33.000Z",
        "values": {
            "nmea0183:///dev/ttyUSB1?sentence=VHW": {
                "value": 3.1361111111111106,
                 "timestamp": "2015-11-14T15:17:56.000Z"
            },
            "nmea0183:///dev/ttyUSB0?sentence=VHW": {
                "value": 3.1277777777777778,
                "timestamp": "2015-11-14T15:17:57.000Z"
            },
            "n2k:///115/128259": {
                "value": 3.6,
                "timestamp": "2015-11-14T15:17:57.000Z"
            },
            "n2k:///160/128259": {
                "value": 3.6,
                "timestamp": "2015-11-14T15:17:57.000Z"
            },
            "n2k:///160/128260": {
                "value": 3.6,
                "timestamp": "2015-11-14T15:17:57.000Z"
            },
        }
    }
}

Note I have dropped the "label" concept here. I think it's redundant - the URN is unique by definition, if a label is to be added it could be done as part of that, eg n2k:///160/128260?label=Masthead+Sensor, and the UI could look for that value if required.

The two main disadvantages to this that I see are:

  1. The keys are not JSON friendly: you can't do navigation.speedthroughWater.values.n2k:///160/128259?this=that.value (but you can do navigation.speedthroughWater.values["n2k:///160/128259?this=that"].value, which is the same thing)
  2. nmea0183 is a stupid name for a protocol, surely we can shorten it somehow?!?!

(edit: Sorry, that one was longer than I expected it to be when I started writing it...)

@pod909
Copy link

pod909 commented Nov 15, 2015

Excellent suggestion faceless

On 14 November 2015 at 22:31, faceless2 notifications@github.com wrote:

1 x 8-port USB hub and 8 x FTDI RS232->USB boards :-) And it's a bit
cheaper than that Actisense box too.

Personally, I think so long as the URN is unique, I can't visualise many
situations where it's going to be broken down and parsed. I kind of agree
with pod909 here - while you obviously need a URN for a sensor, it should
be designed with more than just this issue in mind and this thread might
not be the best place for it.

Having said that, I can't resist :-)

First, I'd also suggest that trying to cater for the specific Actisense
box is the wrong approach, as you can guarantee if you design their method
into the URL, you'll find something that takes a different one tomorrow.

My 2p would be that HTTP URLs have the format protocol:host/path?options,
which covers everything you need here. Some examples:

  1. nmea0183:///dev/ttyACM1?talker=VHW&instance=2
  2. n2k://self/160/128259
  3. n2k:///160/128259
  4. nmea0183:///tcp/8080?talker=VHW
  5. json:///dev/ttyUSB1
  6. wifi://mrn:itu:mmsi:538070999/

Explanations:

  1. nmea0183 from /dev/ttyACM1 where a multiplexer has identified the
    instance as device 2
  2. N2K feed from pgn 160, sentence 128259 on the local boat
  3. Same as 2, if we assumed "self" when host was missing
  4. NMEA0183 relayed over TCP
  5. I've got some boards that spit out raw JSON rather than 0183 or N2K.
    Just merge it with the model directly, much easier.
  6. Wildly hypothetical example of what it might look like if you could get
    a sensor feed from a passing vessel (based on very interesting recent post
    to SignalK mailing list about URN naming of resources by Kaspar Neilsen)

Final point for this post. I'd question the need for anything more than
the URN for the device in the values list above: for example:

    "n2k2:160:128259": {
            "source": {
                "label": "n2k2",
                "type": "NMEA2000",
                "pgn": 128259,
                "src": "160"
            }
        }

Why do we have the "source" map here? All the information we need is in
the URN already - this is a very verbose way of saying the same thing twice.

And even if the URN doesn't contain everything there is to know about
the device, I'm not sure all this static, unchanging information should be
broadcast with every property that the device sets. A GPS could set
lat/lon/vmg/cog/sog plus umpteen properties about satellites. Here, the
source map above would be included with each property, vastly inflating the
size of the model.

If additional information about a sensor is required, it could be placed
in a supplementary map and keyed on the same URL. This could include
location on the boat etc. if required.

I would propose instead:

{
"speedThroughWater": {
"value": 3.1277777777777778,
"source": "nmea0183:///dev/ttyUSB0?sentence=VHW?instance=2"
"timestamp": "2015-11-14T15:18:33.000Z",
"values": {
"nmea0183:///dev/ttyUSB1?sentence=VHW": {
"value": 3.1361111111111106,
"timestamp": "2015-11-14T15:17:56.000Z"
},
"nmea0183:///dev/ttyUSB0?sentence=VHW": {
"value": 3.1277777777777778,
"timestamp": "2015-11-14T15:17:57.000Z"
},
"n2k:///115/128259": {
"value": 3.6,
"timestamp": "2015-11-14T15:17:57.000Z"
},
"n2k:///160/128259": {
"value": 3.6,
"timestamp": "2015-11-14T15:17:57.000Z"
},
"n2k:///160/128260": {
"value": 3.6,
"timestamp": "2015-11-14T15:17:57.000Z"
},
}
}
}

Note I have dropped the "label" concept here. I think it's redundant - the
URN is unique by definition, if a label is to be added it could be done as
part of that, eg n2k:///160/128260?label=Masthead+Sensor, and the UI could
look for that value if required.

The two main disadvantages to this that I see are:

The keys are not JSON friendly: you can't do
navigation.speedthroughWater.values.n2k:///160/128259?this=that.value (but
you can do
navigation.speedthroughWater.values["n2k:///160/128259?this=that"].value,
which is the same thing)
2.

nmea0183 is a stupid name for a protocol, surely we can shorten it
somehow?!?!


Reply to this email directly or view it on GitHub
#48 (comment)
.

@sumps
Copy link
Contributor

sumps commented Nov 15, 2015

I like this proposal and it is inline with what we have been discussing internally during the iKommunicate development.

I think n183 is the most commonly used abbreviated version of NMEA0183.

Well done faceless

@fabdrol
Copy link
Member

fabdrol commented Nov 15, 2015

Ah, very good. I like this idea. Apart from the source format being much less verbose than the current map, to me the major improvement is the fact that it is so much more flexible.

Re labels and other meta data: if a source needs this, I'd argue for an extended object with the same name under vessels/self/sources, providing information like brand, physical location, supported data etc for consumers to use to build up the UI. This information only needs to be retrieved once, so putting it in the "source" key of the data doesn't make sense to me

@fabdrol
Copy link
Member

fabdrol commented Nov 15, 2015

See: #84

@faceless2
Copy link

Yes to fabdroi's idea on the sources! And yes for n183 :-)

@tkurki
Copy link
Member

tkurki commented Nov 15, 2015

The verbose source under value is leftover.

Remember that the keys in JSON objects act as paths in the REST API urls, so stuff we use as property keys should make sense there as well.

@tkurki
Copy link
Member

tkurki commented Nov 15, 2015

I have now node code that will generate a structure like https://gist.github.com/tkurki/84cd71d2329f3ffae9f0.

There is now sources subtree under the vessel object, keyed by configured sources, in this case two NMEA0183 and two N2K inputs. The subtree has entries keyed on talker id/n2k source and the entries contain lists of pgns and sentences, with timestamp of last received message of that type thrown in.

The value data has just references to the sources. The references are paths starting at the sources, delimited with .. Value data has the pgn/sentence that produced that value and timestamp.

With this structure the data is referencable like
/signalk/v1/api/vessels/self/navigation/speedThroughWater/values/nmea1.II

{
    "value": 3.1361111111111106,
    "timestamp": "2015-11-15T15:05:25.336Z",
    "sentence": "VHW"
}

and access metadata about that source with /signalk/v1/api/vessels/10101011/sources/nmea1/II.

Here the only thing really coming from manually entered configuration is the identity of the different inputs (the two nmea0183 and n2k inputs). For a software-only server like node server this makes sense and for a hardware server this would be static data derived from the hardware configuration.

@rob42
Copy link
Contributor Author

rob42 commented Nov 21, 2015

There are two things in Teppos solution:
1)
format and naming of source info for n183 and n2k - looks good to me, but I would save the n183 sentence too - useful later maybe?

The underlying concept here is that $source is a reference to the source data. I think thats a good thing, and something that will be useful in general in signalk.

However we can combine this with the embedded model, as we have discussed before. We just need simple rules for source (we dont need the $):

  1. if source = primitive, eg string, then its a reference, maybe prefixed with $ref: to make it definitive
  2. if source = object, its embedded

That means we can send data outside the server with either the reference to source (leaving the client to retrieve it), or embedded. Hence the source should follow some format that allows it to be externally resolved and retrieved- doesnt matter what, so long as its consistent.

We should formalise the 2) - its a generally useful feature.

@sumps
Copy link
Contributor

sumps commented Nov 21, 2015

I prefer the sentence (or PGN) to be sent with the value, as the source in most cases will be sending out multiple sentences or PGNs. This information is not critical but nice to have for traceability.
I do not understand this "embedded model", can you point me in the direction of the earlier discussions.

@rob42
Copy link
Contributor Author

rob42 commented Nov 22, 2015

Cant find the original idea, but its discussed above, near the top. For clarity what I mean is that this 'referenced' version

{
    "environmental": {
        "depth": {
            "belowTransducer": {
                "timestamp": "2015-08-25T08:12:17.824Z",
                "value": 6.5,
                "source": "sources.n2k.some-unique-name"
            }
        }
    },
    "sources": {
        "n2k": {
            "some-unique-name": {
                "label": "StarboardDST-800",
                "uuid": "9801FBF0-76A3-47DD-ACA6-D76DC405F95C",
                "type": "NMEA2000",
                "capabilities": [....]
            }
        }
    }
}

is funtionally eqivalent to this 'embedded' version

{
    "environmental": {
        "depth": {
            "belowTransducer": {
                "timestamp": "2015-08-25T08:12:17.824Z",
                "value": 6.5,
                "source": {
                      "label": "StarboardDST-800",
                      "uuid": "9801FBF0-76A3-47DD-ACA6-D76DC405F95C",
                      "type": "NMEA2000",
                       "capabilities": [....]
           }
            }
        }
    }
}

But with the referenced version several keys can point to the same source (eg n0183 RMC source for efficiency, and if I send out the value, I might not include the source object, just the reference, making a smaller and cleaner message

@tkurki
Copy link
Member

tkurki commented Nov 22, 2015

A bit of clarification: most of this multiple values/sources is about full tree and REST api.

The delta stream has no trouble representing multiple values for the same Signal K key, as each individual message is independent.

Not so with the full tree: so far it has been able to hold just one value per key. The aim here is to change the schema of the full tree so that we can manage multiple values for a single key.

The reason that this issue has delved into the sources issue is that we need persistent keys in the values object for the multiple objects. As there is need for an independent structure that holds metadata about sensors the solution here includes sources under vessel to move at the same time towards that solution.

@tkurki
Copy link
Member

tkurki commented Nov 22, 2015

@rob42 from your examples I see that we intrerpret things a bit differently. I've used the current label source property to denote an input, eg. some kind of id for the NMEA0183, N2K or other bus or input where the data is coming from.

This way sources has a natural hierarchy. The type of the input is at the correct level and children according to the input type.

"sources": {
    "nmea1": { //"NMEA0183 input 1 of this server"
        "label": "nmea1",
        "type": "NMEA0183",
        "II": {
            "talker": "II",
            "sentences": {
                "VHW": "2015-11-15T14:52:01.092Z",
                "VTG": "2015-11-15T14:52:01.192Z",
                ....
            }
        }
    },
    "nmea2": { //NMEA0183 input 2 of this server
       ..... 
    },
    "n2k1": { //"NMEA2000 bus 1 connected to this server"
        "115": {
            "src": "115",
            "pgns": {
                "128259": "2014-08-15-19:00:01.086",
                "128267": "2014-08-15-19:00:00.591",
                ...
            }
        },

The node server uses the ids from the configuration file as input labels.

Label, type, talker and src are duplicated as keys and properties to make them easier to use from the properties (if you access just ../source/nmea1 you get an object with label property).

@tkurki
Copy link
Member

tkurki commented Nov 22, 2015

If the name label is confusing to denote the input we should change that.

I agree that human readable information like "Starboard DST-800" belongs under the sources. In N2K structure it works well, but I'm not so sure about NMEA0183, as talker is really talker type and not talker id as Tim pointed out someplace. I hope somebody with NMEA0183 multiplexing could shed a bit of light on this - if you multiplex two depth sensors is it usually the case that hey have the same or different talker ids or are there other mechanisms to consider here.

Furthermore I would like to concentrate on the multiple values issue here and not source/sensor details, as I feel they are bit different things.

@sumps
Copy link
Contributor

sumps commented Nov 22, 2015

I think values and sources are so intrinsically linked that to try and consider them separately is not the right approach. We keep using depth or GPS as examples but in reality there are hundreds of examples on a large boat installation. Maybe we should think of a source as a "thing" which creates values "data".
Some things create data duplicates i.e. Depth, GPS, etc. While other things create unique data i.e. RPM of engine 1, voltage of battery 2, etc.
We can actually handle all of these things and the data they produce in the same way as they all have a physical source i.e. N2K1,115,iKommunicate or N183,com3,node-server and they all need a human readable (editable) label and they all produce a value. Whether it is a depth sounder, engine, temperature sensor, battery, etc. We can handle them in the same way if we store their source information and then transmit the value in the same way with the value linked to the source.
Servers and consumers can decide if they want to treat the data as duplicate or unique data but it is all handled in the same way.

@tkurki tkurki added this to the v1 milestone Jan 9, 2016
@tkurki
Copy link
Member

tkurki commented Feb 3, 2016

Fixed by #118

@tkurki tkurki closed this as completed Feb 3, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants