diff --git a/mdbook/src/data_model_metadata.md b/mdbook/src/data_model_metadata.md index 2a4b398d..3e1189f3 100644 --- a/mdbook/src/data_model_metadata.md +++ b/mdbook/src/data_model_metadata.md @@ -27,16 +27,18 @@ The `meta` object exists at the same level as `value` and `$source` in each key "units": "Hz", "timeout": 1, "displayScale": {"lower": 0, "upper": 75, "type": "linear"}, + "zones": [ + {"upper": 4, "state": "alarm", "message": "Stopped or very slow"}, + {"lower": 4, "upper": 60, "state": "normal"}, + {"lower": 60, "upper": 65, "state": "warn", "message": "Approaching maximum"}, + {"lower": 65, "state": "alarm", "message": "Exceeding maximum"} + ] + "normalMethod": [], + "nominalMethod": ["visual"], "alertMethod": ["visual"], - "warnMethod": ["visual"], + "warnMethod": ["sound", "visual"], "alarmMethod": ["sound", "visual"], - "emergencyMethod": ["sound", "visual"], - "zones": [ - {"upper": 4, "state": "alarm", "message": "Stopped or very slow"}, - {"lower": 4, "upper": 60, "state": "normal"}, - {"lower": 60, "upper": 65, "state": "warn", "message": "Approaching maximum"}, - {"lower": 65, "state": "alarm", "message": "Exceeding maximum"} - ] + "emergencyMethod": ["sound", "visual"] } ``` [<]: # @@ -64,12 +66,20 @@ version may be used by consumers where space is at a premium. As with displayNam The `timeout` property tells the consumer how long it should consider the value valid. This value is specified in seconds, so for a high speed GPS sensor it may 0.1 or even 0.05. +### displayScale + The `displayScale` object provides information regarding the recommended type and extent of the scale used for displaying -values. The `lower` and `upper` indicate the extent of the scale to be shown. Some values are better shown on a non linear +values. + +The `lower` and `upper` indicate the extent of the scale to be shown. Some values are better shown on a non linear scale, for example logarithmic for luminosity, depth, signal strength, etc. whilst others may be better on a squareroot -scale eg. depth, windspeed. `type` has possible values of `linear` (default), `logarithmic`, `squareroot` or `power`. When +scale eg. depth, wind speed. + +`type` has possible values of `linear` (default), `logarithmic`, `squareroot` or `power`. When `"type": "power"` is specified an additional property `power` must be present to define the power. Note that a power of -0.5 is equivalent to `squareroot` and a power of 1 is equivalent to linear. In using these scales the type defines the +0.5 is equivalent to `squareroot` and a power of 1 is equivalent to linear. + +In using these scales the type defines the function which is applied to all values in order to calculate % scale deflection of the pointer/needle/plot: | Type | Formula for % deflection | @@ -81,64 +91,60 @@ function which is applied to all values in order to calculate % scale deflection Where: V = value, L = lower bound of the gauge, U = upper bound of the gauge and P = power -Note that on a logarithmic scale neither L nor U can be zero. +Note that on a logarithmic scale neither L nor U can be zero. -The `alertMethod`, `warnMethod`, `alarmMethod` and -`emergencyMethod` properties tell the consumer how it should respond to an abnormal data condition. Presently the -values for these properties are `sound` and `visual` and the method is specified as an array containing one or both of -these options. It is up to the consumer to decide how to convey these alerts. +### zones +Zones define operating conditions and associated severity. They help in presenting display scales and informing end users effectively. -### alertMethod, etc -The `alertMethod`, `warnMethod`, `alarmMethod` and `emergencyMethod` properties tell the consumer how it should respond to an -abnormal data condition. Presently the values for these properties are `sound` and `visual` and the method is specified as an -array containing one or both of these options. It is up to the consumer to decide how to convey these alerts. +Each item in the `zones` array represents a segment (a zone) of value states. The `state` of each item indicates the severity of the zone. Any value not part of a zone range defaults to the `normal` state. -### zones -The last property in the `meta` object is the `zones` array. This provides a series of hints to the consumer which can -be used to properly set a range on a display gauge and also color sectors of a gauge to indicate normal or dangerous -operating conditions. It also tells the consumer which state the data is in for a given range. Combined with the alert -method properties, all Signal K consumers can react the same way to a given state. +As values transition between zones, notifications are dispatched to inform about the value's state. All value-related notifications are defined within zones, and a zone's state determines the severity of the notifications. If the `meta.zones` array does not contains items or is undefined, value-related notifications are disabled (including `normal` state notifications). + +The `lower` and `upper` values in zones need not be contiguous or both present within a zone, nor do they have to fall within the upper and lower bounds specified in `displayScale`. They still trigger notifications even when outside the `displayScale` range. + +In cases where zones overlap, the zone with the highest `state` severity takes precedence. Any range not explicitly within a zone will dispatch a notification with state `normal`. + +Zones can technically have from zero to an infinite number of zone segments. The same `state` can be present in multiple segments. + +Property `message` represent the informational text that will be included in the notification. The message should be concise ie. a few words. The message may end up on a gauges with limited space. -The possible states in ascending order of severity are: +Zones should be configured with care. In practice, less is more. -| State/Zone | Description | +For detailed information on Notifications, refer to [Notifications](notifications.md). + +#### The possible `state` values in ascending order of severity + +| State | Description | |------------|--------| -| nominal | this is a special type of normal state/zone (see below) | -| normal | the normal operating range for the value in question (default) | -| alert | Indicates a safe or normal condition which is brought to the operators attention to impart information for routine action purposes | +| normal | Server generated - The normal operating range for the value in question (default) | +| nominal | The recommended optimal operation condition (see below) | +| alert | Indicates a safe or normal condition which is brought to the operators attention to impart information | | warn | Indicates a condition that requires immediate attention but not immediate action | | alarm | Indicates a condition which is outside the specified acceptable range. Immediate action is required to prevent loss of life or equipment damage | -| emergency | the value indicates a life-threatening condition | +| emergency | The value indicates a life-threatening condition | -`nominal`: A example use of this is for engine monitoring eg. coolant temperature where there is a normal (no warnings) -(green) zone between say 70C and 110C, but when the temperature is between 80C and 90C (`nominal`) the needle doesn't move at -all (typically remains vertical or horizontal). This is really useful if you have many gauges (multiple motors with multiple -sensors) where it is very easy to spot that every needle is pointing in exactly the same direction. Use of nominal will only -be relevant if the gauge/display design permits it. +### nominalMethod, alertMethod, warnMethod, alarmMethod, emergencyMethod -The `upper` and `lower` values in the zones do not need to be contiguous, they don't have to both be present in a zone, nor do -they need to be within the bounds of the `upper` and `lower` specified in `displayScale`. When they are outside of the -`displayScale` range they will still give rise to alerts. Both `upper` and `lower` values are considered to be inclusive. +Methods properties define what kind of prompts consumers should render when receiving a notification. -If zones overlap each other the state/zone with the highest severity will take precedence. This is true for both alerts and -gauge/display rendering. Any part of the range which is not explicitly within a zone is considered to be `normal` (the default). -As such, zones with a state of `normal` have no effect and their removal would result in no changes to either displays or alerts. +Methods are arrays that accepts string items of values `sound` and `visual`. The array length should be 2 or less. It can contain either or both values, or be empty. An empty array `[]` signifies that no visual and no audio prompts should be enacted by consumers. -There can be multiple zones with the same `state`, for example if a different message is required, or if they are on different parts of the scale. +Each method property name is prefix to match zone state severities. For example, a notification message for zone with `"state": "warn"` will include `warnMethod` values. -Signal K servers will use the `zone` information to monitor any data which has a `meta` object and -raise a generic alarm event. See the section on [Alarm Handling](notifications.md) for more. +Typically, an `alertMethod` would be configured as: +```json + "alertMethod": ["visual"] +``` +Consumers have the flexibility to interpret and apply these methods, enabling them to effectively communicate notifications based on their specific use cases. ## Implicit Metadata -All keys in the Signal K specification must have a `description`, and where the key is a numeric value it must have -`units`. +All keys in the Signal K specification must have a `description`, and where the key is a numeric value it must have `units`. If a client requests the `meta` property for a valid Signal K key via the HTTP REST interface, the server must return the `description` and, if applicable, `units`, even if no value has ever been generated for that key. -If a key has values determined by an enum, the server should include the enum in the meta. NB. in future versions -it is likely that this will become a mandatory requirement for the server. +If a key has values determined by an enum, the server should include the enum in the meta. NB. in future versions it is likely that this will become a mandatory requirement for the server. ```javascript // GET /signalk/v1/api/vessels/self/environment/depth/belowKeel/meta @@ -157,26 +163,60 @@ Signal K does not provide a default set of metadata, it is up to the owner or th K environment appropriately for their vessel. However, by centralizing this configuration they will only need to do it one time and any future consumers will automatically use this configuration. -## Alarm Management +## Other Benefits + +While not strictly part of the Signal K specification, metadata configuration could be shared between boats or even +provided by manufacturers of production boats or by component suppliers such as engine or refrigerator manufacturers. +Also, any device which implements Signal K should provide a baseline metadata configuration. As this standard becomes +more widespread, less individual configuration will need to be performed. + +## Examples + +An engine RPM path's redline segment, from 3200 to 3500 rpm, triggering a visual and sound prompt: With this +configuration, consumers can opt to draw a red marker over a gauge segment, flash a light on the gauge and sound an alarm when the RPM enters this zone. -An alarm watch is set by setting the `meta.zones` array appropriately. A background process on the server checks for -alarm conditions on any attribute with a `meta.zones` array. If the keys value is within a zone the server sets an -alarm key similar to `vessels.self.notifications.[original_key_suffix]`, e.g. an alarm set on -`vessels.self.navigation.courseOverGroundTrue` will become -`vessels.self.notifications.navigation.courseOverGroundTrue`. +```json + "zones": [ + {"lower": 3200, "upper": 3500, "state": "alarm", "message": "Risk of engine damage"} + ], + ... + "alarmMethod": ["sound", "visual"], + ... +``` -The object found at this key should contain the following: +A refrigeration temperature sensor with duplicate state severity containing different messages: With this +configuration, consumers can opt to draw a multiple markers on the gauge, light up a visual indicator on the gauge and sound an alarm when the RPM enters this zone. ```json -{ - "message": "any text", - "state": "[normal|alert|warn|alarm|emergency]" -} + "zones": [ + {"upper": -2, "state": "warn", "message": "Freezing temperature reached"}, + {"lower": -2, "upper": 1, "state": "alert", "message": "Excessive energy expenditure"}, + {"lower": 5, "upper": 8, "state": "warn", "message": "Perishable storage at risk"}, + {"lower": 8, "state": "alarm", "message": "Risk of bacterial growth and food spoilage"} + ] + ... + "alertMethod": ["visual"], + "warnMethod": ["sound", "visual"], + "alarmMethod": ["sound", "visual"], + ... + ``` -## Other Benefits +For engine monitoring eg. coolant temperature where there is a 'normal' (no warnings) +(green) zone between say 70C and 110C, but when the temperature is between 80C and 90C (`nominal`) the needle doesn't move at all (typically remains vertical or horizontal) indicating typical or desired range. This is really useful if you have many gauges (multiple motors with multiple +sensors) where it is very easy to spot that every needle is pointing in exactly the same direction. Use of nominal will only +be relevant if the gauge/display design permits it. -While not strictly part of the Signal K specification, metadata configuration could be shared between boats or even -provided by manufacturers of production boats or by component suppliers such as engine or refrigerator manufacturers. -Also, any device which implements Signal K should provide a baseline metadata configuration. As this standard becomes -more widespread, less individual configuration will need to be performed. +```json + "zones": [ + {"upper": 60, "state": "alert", "message": "Cold or Startup Temperature"}, + {"lower": 70, "upper": 80, "state": "nominal", "message": "Temperature Nominal"}, + {"lower": 85, "upper": 95, "state": "warn", "message": "High Temperature"}, + {"lower": 95, "state": "alarm", "message": "Engine Overheat"} + ], + "nominalMethod": ["visual"], + "alertMethod": ["visual"], + "warnMethod": ["sound", "visual"], + "alarmMethod": ["sound", "visual"], + ... +``` diff --git a/mdbook/src/notifications.md b/mdbook/src/notifications.md index fa89461a..fb0a2dcb 100644 --- a/mdbook/src/notifications.md +++ b/mdbook/src/notifications.md @@ -1,36 +1,58 @@ -# Alarm, Alert, and Notification Handling +# Notifications -Handling alarms, alerts, and notifications in Signal K is a multi-stage process. Alarms, alerts and notifications are -all handled the same way, and are all referred to as alarms below. +Signal K handles notifications through a multi-stage process, treating all notifications uniformly. Notifications originate from two sources: -We need a flexible model to define alarm conditions, and a standard way to announce and record them. +1. Path value state information +2. Vessel and crew information -## Alarm Process +A flexible model is required to define various informative and alarming severities, along with a standard method for announcement and management. -* Define alarm states as zones in the meta object attached to any Signal K value. See [[Metadata for Data Values]] -* If the value is within an alarm zone raise the defined alarm. -* If the value goes out of the zone, remove the alarm by setting its value to null -* Alarms are raised by placing an alarm object in the `vessels.self.notifications` tree +## Notification Process -## Expected implementation behaviour +* The definition of an "alarming" notification is defined by the notification's state severity. +* The lowest state severity is considered normal - not alarming. +* Notification states are defined as zone segments (array items) in the meta object attached to any Signal K path. See [Metadata](data_model_metadata.md). +* If `meta.zones` contains at least one item, the server determines the value's state and sends a notification. +* When a value doesn't fall within any `meta.zones`, the server raises a default notification indicating a normal value state. +* If the value transitions to or enters a zone, the server raises the defined notification. +* If the value leaves all zones, the server sends a normal state value notification. +* Notifications are raised by placing a notification object in the `vessels.self.notifications` tree. -* The server (or device) should monitor the current value and compare it to the defined zones. -* If a value enters an alarm zone, then a key is written to `vessels.self.notifications..` -* If a value leaves an alarm zone, then the key is removed from `vessels.self.notifications..` -* Alarms raised are monitored by an alarm process on the server, which takes appropriate action, sounding alarms, or - displaying messages. -* Clients interested in alarms can subcribe to the `vessels.self.notifications...` tree in the usual way and be informed - of alarms in the same way as normal signalk keys. -* When an alarms is removed, a delta should be sent to subscribers with the path and a null value. +## Expected Implementation Behavior + +* The server (or device) should monitor the current value and compare it to the defined meta zones. +* If the value changes zone, a key is written to `vessels.self.notifications..`. +* If the value matches no zone, a key indicating normal state is written to `vessels.self.notifications..`. +* Notifications raised are monitored by a notification process on the server, which takes appropriate action, such as sounding alarms or displaying messages. +* Clients interested in alarms can subscribe to the `vessels.self.notifications...` tree in the usual way and be informed of notifications in the same way as normal Signal K keys. +* When a notification is cleared, a delta should be sent to subscribers indicating the notification has returned to its normal state. + +## Severity and Presentation Definition + +* Notification must include a `state` property to specify its severity level. +* Notification must include a `method` array property to determine if the notification should result in a sound, visual, both, or no presentation. + +| State | Description | +|------------|--------| +| normal | Server generated - The normal value (default) | +| nominal | All systems OK | +| alert | Indicates a safe or normal condition which is brought to the operators attention to impart information | +| warn | Indicates a condition that requires immediate attention but not immediate action | +| alarm | Indicates a condition which is outside the specified acceptable range. Immediate action is required to prevent loss of life or equipment damage | +| emergency | The value indicates a life-threatening condition | + +## Resolving and silencing notifications +* To silence a notification, the serve or clients, send delta notification replica without the `sound` item the method array. +* To resolve a notification, the serve or clients, send delta notification replica with a state value of `normal`. Alarming notifications are defined by there state severity, not by their existence. ## Example eg If we exceed our anchor alarm radius: `vessels.self.navigation.anchor.currentRadius` enters `vessels.self.navigation.anchor.currentRadius.meta.zones : [ {lower: "0", upper: maxRadius, state : "normal"}, {lower: maxRadius, upper: 999999, state: "alarm"}]` -The alarm is : `vessels.self.notifications.navigation.anchor.currentRadius` +The notification is : `vessels.self.notifications.navigation.anchor.currentRadius` -The alarm object is +The notification object is [>]: # (mdpInsert ``` fsnip ../../samples/full/docs-notifications.json --snip currentRadius --ellipsify timestamp "'$..['\$source']'") ``` @@ -45,25 +67,25 @@ The alarm object is } ``` [<]: # -The server alarm manager will see this new entry and turn on the alarm. Using a manager process allows flexibility in -situations where multiple alarms are triggered and your vessel is a mass of flashing and beeping. eg A single 'Pause' +The server notification manager will see this new entry and turn on the sound. Using a manager process allows flexibility in +situations where multiple notifications of various severity are triggered and your vessel is a mass of flashing and beeping. eg A single 'Pause' button can give you 5-10 minutes to take action, stopping annoying noise, and removing popup messages from screens. Since the `vessels.self.notifications` tree mirrors the other data in the signal k model, we can selectively watch or -react to specific branches or keys. When displaying multiple alarms a screen can also sort and filter them. +react to specific branches or keys. When displaying multiple notifications a screen can also sort and filter them. -## Other Alarms +## Other notifications -Above we have discussed monitoring existing values and raising alarms. There are other alarms that must be considered, +Above we have discussed monitoring existing values and raising notification. There are other notifications that must be considered, eg MOB, fire, sinking etc, and misc alerts "GPS signal lost".etc. The `vessels.[uuid].notifications` tree is the same as any other Signal k branch. Keys can be added and removed as required in the usual way. Since the branch is being monitored we only need to add a key of any sort to create a suitable alarm. -In the case of an emergency, create a unique key: The alarm is : `vessels.[uuid].notifications.[alarm.key]` +In the case of an emergency, create a unique key: The notification is : `vessels.[uuid].notifications.[alarm.key]` -The alarm object is +The notification object is [>]: # (mdpInsert ``` fsnip ../../samples/full/docs-notifications.json --snip mob --ellipsify $ ~value) ``` @@ -77,13 +99,13 @@ The alarm object is } ``` [<]: # -Alarm objects that have been raised this way must be cleared manually, or by the process that created them. You can use -any suitable path, keeping in mind the context of the alarm. +Notification objects that have been raised this way must be cleared manually, or by the process that created them. You can use +any suitable path, keeping in mind the context of the notification. -eg In the case of an alert, create a unique key by generating a path: The alarm is : +eg In the case of an alert, create a unique key by generating a path: The notification is : `vessels.[uuid].notifications.navigation.gnss` -The alarm object is +The notification object is [>]: # (mdpInsert ``` fsnip ../../samples/full/docs-notifications.json --snip gnss --ellipsify $ ~value) ``` @@ -97,9 +119,10 @@ The alarm object is } ``` [<]: # + ### Well Known Names -Some alarms are especially important, eg MOB. This is a list of keys for special alarms. +Some notifications are especially important, eg MOB. This is a list of keys for special notifications. * `..notifications.mob.*` * `..notifications.fire.*` * `..notifications.sinking.*` @@ -111,7 +134,7 @@ Some alarms are especially important, eg MOB. This is a list of keys for special * `..notifications.piracy.*` * `..notifications.abandon.*` -An example to send an MOB alarm from an N2K source, the gateway would convert and send something like: +An example to send an MOB notification alarm from an N2K source, the gateway would convert and send something like: [>]: # (mdpInsert ``` fsnip ../../samples/delta/docs-notifications.json --delKeys $.updates[1] --ellipsify source) ``` @@ -136,7 +159,8 @@ An example to send an MOB alarm from an N2K source, the gateway would convert an } ``` [<]: # -The resulting full signalk tree would be: + +The resulting full Signal K tree would be: [>]: # (mdpInsert ``` fsnip ../../samples/full/docs-notifications.json --ellipsify $ ~vessels --delKeys navigation --ellipsify uuid "'$..['\$source']'") ``` @@ -161,7 +185,8 @@ The resulting full signalk tree would be: } ``` [<]: # -To clear the alarm condition, send: + +To clear the notification condition, send: [>]: # (mdpInsert ``` fsnip ../../samples/delta/docs-notifications.json --delKeys $.updates[0] --ellipsify source) ``` @@ -170,11 +195,15 @@ To clear the alarm condition, send: "updates": [ { "source": {...}, - "timestamp": "2017-08-15T16:00:05.538Z", + "timestamp": "2017-08-15T16:00:05.200Z", "values": [ { "path": "notifications.mob", - "value": null + "value": { + "message": "", + "state": "normal", + "method": [] + } } ] } @@ -182,22 +211,39 @@ To clear the alarm condition, send: } ``` [<]: # -## Multiple cases of the same alarm -Should multiple cases of the same alarm occur (eg a gps loses signal, then a second gps loses signal) the alarms are -handled the same as any other multiple values in signalk. However alarms will tend to be re-issued whenever the +## Multiple cases of the same notification + +Should multiple cases of the same notification occur (eg a gps loses signal, then a second gps loses signal) the notifications are +handled the same as any other multiple values in Signal K. However notification will tend to be re-issued whenever the underlying data changes. -The servers alarm monitoring processes are expected to be smart enough to know that the anchor alarm is triggered, and -its not necessary to raise a second copy of the same alarm, after all there is only one boat dragging! +The servers notification monitoring processes are expected to be smart enough to know that the anchor notification of state alarm is triggered, and +its not necessary to raise a second copy of the same notification and state, after all there is only one boat dragging! This may be handled differently for notifications. It may be useful to know that your gps's are all failing -intermittently, or that . Hence the handling of multiple copies of alarms is an implementation issue, and may vary. +intermittently, or that . Hence the handling of multiple copies of notifications is an implementation issue, and may vary. ## The key should be unique -If we have an alarm `vessels.self.notifications.navigation.anchor.currentRadius` and we attempt to write another higher -in the same tree at `vessels.self.notifications` it must not replace or remove the existing alarm. Since the -`meta.zones` structure is only valid on signalk leaf values this occurs naturally in most circumstances. But it is -possible to set an alarm value arbitrarily (eg MOB) and care should be taken in implementations that keys do not +If we have a notification `vessels.self.notifications.navigation.anchor.currentRadius` and we attempt to write another higher +in the same tree at `vessels.self.notifications` it must not replace or remove the existing notification. Since the +`meta.zones` structure is only valid on Signal K leaf values this occurs naturally in most circumstances. But it is +possible to set a notification value arbitrarily (eg MOB) and care should be taken in implementations that keys do not overwrite existing paths. + +## Notification Management + +An notification watch is set by setting the `meta.zones` array appropriately. A background process on the server checks for +notification conditions on any attribute with a `meta.zones` array. If the keys value is within a zone the server sets a notification key similar to `vessels.self.notifications.[original_key_suffix]`, e.g. a notification set on +`vessels.self.navigation.courseOverGroundTrue` will become +`vessels.self.notifications.navigation.courseOverGroundTrue`. + +The object found at this key should contain the following: + +```json +{ + "message": "any text", + "state": "[normal|alert|warn|alarm|emergency]" +} +```