diff --git a/node-red/flows.json b/node-red/flows.json index 552131e..4e2f8c8 100644 --- a/node-red/flows.json +++ b/node-red/flows.json @@ -178,7 +178,7 @@ { "id": "f486fb61943eeb89", "type": "subflow", - "name": "GetDatapoints", + "name": "Broker.GetDatapoints", "info": "# GetDatapoints\n\nRequest a set of datapoints (values)\n\nExpects `msg.payload` to contain an array with multiple VSS signal names or a string containing a single name.\n\nReturns an object containing all the requested datapoints as `msg.payload`.\nIf there is an error, `msg.error` is set to the error description.\n", "category": "SDV", "in": [ @@ -312,7 +312,7 @@ { "id": "4f41580d2b2e81cf", "type": "subflow", - "name": "Subscribe", + "name": "Broker.Subscribe", "info": "", "category": "SDV", "in": [ @@ -328,7 +328,7 @@ ], "out": [ { - "x": 940, + "x": 920, "y": 60, "wires": [ { @@ -338,7 +338,7 @@ ] }, { - "x": 950, + "x": 930, "y": 120, "wires": [ { @@ -363,7 +363,7 @@ ], "icon": "node-red-contrib-grpc/grpc.svg", "status": { - "x": 980, + "x": 960, "y": 180, "wires": [ { @@ -434,7 +434,7 @@ { "id": "2a90df8a197fb8f2", "type": "subflow", - "name": "SetDatapoint", + "name": "Broker.SetDatapoint", "info": "", "category": "SDV", "in": [ @@ -492,7 +492,7 @@ { "id": "194e312172a28c52", "type": "subflow", - "name": "GetMetadata", + "name": "Broker.GetMetadata", "info": "", "category": "SDV", "in": [ @@ -550,6 +550,64 @@ ] } }, + { + "id": "3ba7d7d5ee4707e1", + "type": "subflow", + "name": "VAL.Set", + "info": "", + "category": "SDV", + "in": [ + { + "x": 60, + "y": 60, + "wires": [ + { + "id": "edd6965ab2fc6f0b" + } + ] + } + ], + "out": [ + { + "x": 1290, + "y": 160, + "wires": [ + { + "id": "b94efb2bff0d6e7d", + "port": 0 + }, + { + "id": "bf1747db2b73a08d", + "port": 0 + }, + { + "id": "17e5cf57d548e040", + "port": 0 + } + ] + } + ], + "env": [], + "meta": { + "module": "VAL.Set", + "license": "Apache-2.0" + }, + "color": "#5d8dab", + "outputLabels": [ + "error" + ], + "icon": "node-red-contrib-grpc/grpc.svg", + "status": { + "x": 1320, + "y": 100, + "wires": [ + { + "id": "2e011263cb6fa367", + "port": 0 + } + ] + } + }, { "id": "fb585e7f0dd51cfd", "type": "mqtt-broker", @@ -722,7 +780,7 @@ "id": "1f6dc92172f4e94b", "type": "grpc-server", "port": "${BROKER_PORT}", - "name": "leda-databroker", + "name": "leda sdv.databroker.v1", "server": "${BROKER_ADDR}", "protoFile": "/********************************************************************************\n * Copyright (c) 2022 Contributors to the Eclipse Foundation\n *\n * See the NOTICE file(s) distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Apache License 2.0 which is available at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * SPDX-License-Identifier: Apache-2.0\n ********************************************************************************/\n\nsyntax = \"proto3\";\n\npackage sdv.databroker.v1;\n\nimport \"google/protobuf/timestamp.proto\";\n\n// Data type of a signal\n//\n// Protobuf doesn't support int8, int16, uint8 or uint16.\n// These are mapped to sint32 and uint32 respectively.\n//\nenum DataType {\n STRING = 0;\n BOOL = 1;\n INT8 = 2;\n INT16 = 3;\n INT32 = 4;\n INT64 = 5;\n UINT8 = 6;\n UINT16 = 7;\n UINT32 = 8;\n UINT64 = 9;\n FLOAT = 10;\n DOUBLE = 11;\n TIMESTAMP = 12;\n STRING_ARRAY = 20;\n BOOL_ARRAY = 21;\n INT8_ARRAY = 22;\n INT16_ARRAY = 23;\n INT32_ARRAY = 24;\n INT64_ARRAY = 25;\n UINT8_ARRAY = 26;\n UINT16_ARRAY = 27;\n UINT32_ARRAY = 28;\n UINT64_ARRAY = 29;\n FLOAT_ARRAY = 30;\n DOUBLE_ARRAY = 31;\n TIMESTAMP_ARRAY = 32;\n}\n\nenum DatapointError {\n UNKNOWN_DATAPOINT = 0;\n INVALID_TYPE = 1;\n ACCESS_DENIED = 2;\n INTERNAL_ERROR = 3;\n OUT_OF_BOUNDS = 4;\n}\n\nenum EntryType {\n ENTRY_TYPE_UNSPECIFIED = 0;\n ENTRY_TYPE_SENSOR = 1;\n ENTRY_TYPE_ACTUATOR = 2;\n ENTRY_TYPE_ATTRIBUTE = 3;\n}\n\nenum ChangeType {\n STATIC = 0; // Value never changes\n ON_CHANGE = 1; // Updates are provided every time the value changes (i.e.\n // window is open / closed)\n CONTINUOUS = 2; // Value is updated continuously. Broker needs to tell\n // provider the preferred (update) frequency.\n}\n\nmessage StringArray {\n repeated string values = 1;\n}\n\nmessage BoolArray {\n repeated bool values = 1;\n}\n\nmessage Int32Array {\n repeated sint32 values = 1;\n}\n\nmessage Int64Array {\n repeated sint64 values = 1;\n}\n\nmessage Uint32Array {\n repeated uint32 values = 1;\n}\n\nmessage Uint64Array {\n repeated uint64 values = 1;\n}\n\nmessage FloatArray {\n repeated float values = 1;\n}\n\nmessage DoubleArray {\n repeated double values = 1;\n}\n\nmessage Datapoint {\n // Timestamp of the value\n google.protobuf.Timestamp timestamp = 1;\n\n // values\n oneof value {\n Failure failure_value = 10;\n string string_value = 11;\n bool bool_value = 12;\n sint32 int32_value = 13;\n sint64 int64_value = 14;\n uint32 uint32_value = 15;\n uint64 uint64_value = 16;\n float float_value = 17;\n double double_value = 18;\n StringArray string_array = 21;\n BoolArray bool_array = 22;\n Int32Array int32_array = 23;\n Int64Array int64_array = 24;\n Uint32Array uint32_array = 25;\n Uint64Array uint64_array = 26;\n FloatArray float_array = 27;\n DoubleArray double_array = 28;\n }\n\n enum Failure {\n // The data point is known, but doesn't have a valid value\n INVALID_VALUE = 0;\n // The data point is known, but no value is available\n NOT_AVAILABLE = 1;\n // Unknown datapoint\n UNKNOWN_DATAPOINT = 2;\n // Access denied\n ACCESS_DENIED = 3;\n // Unexpected internal error\n INTERNAL_ERROR = 4;\n }\n}\n\nmessage Metadata {\n // Id to be used in \"get\" and \"subscribe\" requests. Ids stay valid during\n // one power cycle, only.\n int32 id = 1;\n EntryType entry_type = 2;\n string name = 4;\n DataType data_type = 5;\n ChangeType change_type = 6; // CONTINUOUS or STATIC or ON_CHANGE\n string description = 7;\n\n // int32 min_update_hz = 10; // Only for CONTINUOUS\n // int32 max_update_hz = 11; // Only for CONTINUOUS\n}\n\nservice Broker {\n // Request a set of datapoints (values)\n //\n // Returns a list of requested data points.\n //\n // InvalidArgument is returned if the request is malformed.\n rpc GetDatapoints(GetDatapointsRequest) returns (GetDatapointsReply);\n\n // Set a datapoint (values)\n rpc SetDatapoints(SetDatapointsRequest) returns (SetDatapointsReply);\n\n // Subscribe to a set of data points or conditional expressions\n // using the Data Broker Query Syntax (described in QUERY.md)\n //\n // Returns a stream of replies.\n //\n // InvalidArgument is returned if the request is malformed.\n rpc Subscribe(SubscribeRequest) returns (stream SubscribeReply);\n\n // Request the metadata of a set of datapoints\n //\n // Returns metadata of the requested data points that exist.\n rpc GetMetadata(GetMetadataRequest) returns (GetMetadataReply);\n}\n\nmessage GetDatapointsRequest {\n // A list of requested data points.\n repeated string datapoints = 1;\n}\n\nmessage GetDatapointsReply {\n // Contains the values of the requested data points.\n // If a requested data point is not available, the corresponding Datapoint\n // will have the respective failure value set.\n map datapoints = 1;\n}\n\nmessage SetDatapointsRequest {\n // A map of data points to set\n map datapoints = 1;\n}\n\nmessage SetDatapointsReply {\n // A map of errors (if any)\n map errors = 1;\n}\n\nmessage SubscribeRequest {\n // Subscribe to a set of data points (or expressions) described\n // by the provided query.\n // The query syntax is a subset of SQL and is described in more\n // detail in the QUERY.md file.\n string query = 2;\n}\n\nmessage SubscribeReply {\n // Contains the fields specified by the query.\n // If a requested data point value is not available, the corresponding\n // Datapoint will have it's respective failure value set.\n map fields = 1;\n}\n\nmessage GetMetadataRequest {\n // Request metadata for a list of data points referenced by their names.\n // e.g. \"Vehicle.Cabin.Seat.Row1.Pos1.Position\" or \"Vehicle.Speed\".\n //\n // If no names are provided, metadata for all known data points will be\n // returned.\n repeated string names = 1;\n}\n\nmessage GetMetadataReply {\n // Contains metadata of the requested data points. If a data point\n // doesn't exist (i.e. not known to the Data Broker) the corresponding\n // Metadata isn't part of the returned list.\n repeated Metadata list = 1;\n}", "ca": "", @@ -888,6 +946,22 @@ "collapse": false, "className": "" }, + { + "id": "81356b1220393dc3", + "type": "grpc-server", + "z": "3ba7d7d5ee4707e1", + "port": "${BROKER_PORT}", + "name": "leda kuksa.val.v1", + "server": "${BROKER_ADDR}", + "protoFile": "/********************************************************************************\n * Copyright (c) 2022 Contributors to the Eclipse Foundation\n *\n * See the NOTICE file(s) distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Apache License 2.0 which is available at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * SPDX-License-Identifier: Apache-2.0\n ********************************************************************************/\n\nsyntax = \"proto3\";\n\npackage kuksa.val.v1;\n\nimport \"google/protobuf/timestamp.proto\";\n\n// Describes a VSS entry\n// When requesting an entry, the amount of information returned can\n// be controlled by specifying either a `View` or a set of `Field`s.\nmessage DataEntry {\n // Defines the full VSS path of the entry.\n string path = 1; // [field: FIELD_PATH]\n\n // The value (datapoint)\n Datapoint value = 2; // [field: FIELD_VALUE]\n\n // Actuator target (only used if the entry is an actuator)\n Datapoint actuator_target = 3; // [field: FIELD_ACTUATOR_TARGET]\n\n // Metadata for this entry\n Metadata metadata = 10; // [field: FIELD_METADATA]\n}\n\nmessage Datapoint {\n google.protobuf.Timestamp timestamp = 1;\n\n oneof value {\n string string = 11;\n bool bool = 12;\n sint32 int32 = 13;\n sint64 int64 = 14;\n uint32 uint32 = 15;\n uint64 uint64 = 16;\n float float = 17;\n double double = 18;\n StringArray string_array = 21;\n BoolArray bool_array = 22;\n Int32Array int32_array = 23;\n Int64Array int64_array = 24;\n Uint32Array uint32_array = 25;\n Uint64Array uint64_array = 26;\n FloatArray float_array = 27;\n DoubleArray double_array = 28;\n }\n}\n\nmessage Metadata {\n // Data type\n // The VSS data type of the entry (i.e. the value, min, max etc).\n //\n // NOTE: protobuf doesn't have int8, int16, uint8 or uint16 which means\n // that these values must be serialized as int32 and uint32 respectively.\n DataType data_type = 11; // [field: FIELD_METADATA_DATA_TYPE]\n\n // Entry type\n EntryType entry_type = 12; // [field: FIELD_METADATA_ENTRY_TYPE]\n\n // Description\n // Describes the meaning and content of the entry.\n optional string description = 13; // [field: FIELD_METADATA_DESCRIPTION]\n\n // Comment [optional]\n // A comment can be used to provide additional informal information\n // on a entry.\n optional string comment = 14; // [field: FIELD_METADATA_COMMENT]\n\n // Deprecation [optional]\n // Whether this entry is deprecated. Can contain recommendations of what\n // to use instead.\n optional string deprecation = 15; // [field: FIELD_METADATA_DEPRECATION]\n\n // Unit [optional]\n // The unit of measurement\n optional string unit = 16; // [field: FIELD_METADATA_UNIT]\n\n // Value restrictions [optional]\n // Restrict which values are allowed.\n // Only restrictions matching the DataType {datatype} above are valid.\n ValueRestriction value_restriction = 17; // [field: FIELD_METADATA_VALUE_RESTRICTION]\n\n // Entry type specific metadata\n oneof entry_specific {\n Actuator actuator = 20; // [field: FIELD_METADATA_ACTUATOR]\n Sensor sensor = 30; // [field: FIELD_METADATA_SENSOR]\n Attribute attribute = 40; // [field: FIELD_METADATA_ATTRIBUTE]\n }\n}\n\n///////////////////////\n// Actuator specific fields\nmessage Actuator {\n // Nothing for now\n}\n\n////////////////////////\n// Sensor specific\nmessage Sensor {\n // Nothing for now\n}\n\n////////////////////////\n// Attribute specific\nmessage Attribute {\n // Nothing for now.\n}\n\n// Value restriction\n//\n// One ValueRestriction{type} for each type, since\n// they don't make sense unless the types match\n//\nmessage ValueRestriction {\n oneof type {\n ValueRestrictionString string = 21;\n // For signed VSS integers\n ValueRestrictionInt signed = 22;\n // For unsigned VSS integers\n ValueRestrictionUint unsigned = 23;\n // For floating point VSS values (float and double)\n ValueRestrictionFloat floating_point = 24;\n }\n}\n\nmessage ValueRestrictionInt {\n optional sint64 min = 1;\n optional sint64 max = 2;\n repeated sint64 allowed_values = 3;\n}\n\nmessage ValueRestrictionUint {\n optional uint64 min = 1;\n optional uint64 max = 2;\n repeated uint64 allowed_values = 3;\n}\n\nmessage ValueRestrictionFloat {\n optional double min = 1;\n optional double max = 2;\n\n // allowed for doubles/floats not recommended\n repeated double allowed_values = 3;\n}\n\n// min, max doesn't make much sense for a string\nmessage ValueRestrictionString {\n repeated string allowed_values = 3;\n}\n\n// VSS Data type of a signal\n//\n// Protobuf doesn't support int8, int16, uint8 or uint16.\n// These are mapped to int32 and uint32 respectively.\n//\nenum DataType {\n DATA_TYPE_UNSPECIFIED = 0;\n DATA_TYPE_STRING = 1;\n DATA_TYPE_BOOLEAN = 2;\n DATA_TYPE_INT8 = 3;\n DATA_TYPE_INT16 = 4;\n DATA_TYPE_INT32 = 5;\n DATA_TYPE_INT64 = 6;\n DATA_TYPE_UINT8 = 7;\n DATA_TYPE_UINT16 = 8;\n DATA_TYPE_UINT32 = 9;\n DATA_TYPE_UINT64 = 10;\n DATA_TYPE_FLOAT = 11;\n DATA_TYPE_DOUBLE = 12;\n DATA_TYPE_TIMESTAMP = 13;\n DATA_TYPE_STRING_ARRAY = 20;\n DATA_TYPE_BOOLEAN_ARRAY = 21;\n DATA_TYPE_INT8_ARRAY = 22;\n DATA_TYPE_INT16_ARRAY = 23;\n DATA_TYPE_INT32_ARRAY = 24;\n DATA_TYPE_INT64_ARRAY = 25;\n DATA_TYPE_UINT8_ARRAY = 26;\n DATA_TYPE_UINT16_ARRAY = 27;\n DATA_TYPE_UINT32_ARRAY = 28;\n DATA_TYPE_UINT64_ARRAY = 29;\n DATA_TYPE_FLOAT_ARRAY = 30;\n DATA_TYPE_DOUBLE_ARRAY = 31;\n DATA_TYPE_TIMESTAMP_ARRAY = 32;\n}\n\n// Entry type\nenum EntryType {\n ENTRY_TYPE_UNSPECIFIED = 0;\n ENTRY_TYPE_ATTRIBUTE = 1;\n ENTRY_TYPE_SENSOR = 2;\n ENTRY_TYPE_ACTUATOR = 3;\n}\n\n// A `View` specifies a set of fields which should\n// be populated in a `DataEntry` (in a response message)\nenum View {\n VIEW_UNSPECIFIED = 0; // Unspecified. Equivalent to VIEW_CURRENT_VALUE unless `fields` are explicitly set.\n VIEW_CURRENT_VALUE = 1; // Populate DataEntry with value.\n VIEW_TARGET_VALUE = 2; // Populate DataEntry with actuator target.\n VIEW_METADATA = 3; // Populate DataEntry with metadata.\n VIEW_FIELDS = 10; // Populate DataEntry only with requested fields.\n VIEW_ALL = 20; // Populate DataEntry with everything.\n}\n\n// A `Field` corresponds to a specific field of a `DataEntry`.\n//\n// It can be used to:\n// * populate only specific fields of a `DataEntry` response.\n// * specify which fields of a `DataEntry` should be set as\n// part of a `Set` request.\n// * subscribe to only specific fields of a data entry.\n// * convey which fields of an updated `DataEntry` have changed.\nenum Field {\n FIELD_UNSPECIFIED = 0; // \"*\" i.e. everything\n FIELD_PATH = 1; // path\n FIELD_VALUE = 2; // value\n FIELD_ACTUATOR_TARGET = 3; // actuator_target\n FIELD_METADATA = 10; // metadata.*\n FIELD_METADATA_DATA_TYPE = 11; // metadata.data_type\n FIELD_METADATA_DESCRIPTION = 12; // metadata.description\n FIELD_METADATA_ENTRY_TYPE = 13; // metadata.entry_type\n FIELD_METADATA_COMMENT = 14; // metadata.comment\n FIELD_METADATA_DEPRECATION = 15; // metadata.deprecation\n FIELD_METADATA_UNIT = 16; // metadata.unit\n FIELD_METADATA_VALUE_RESTRICTION = 17; // metadata.value_restriction.*\n FIELD_METADATA_ACTUATOR = 20; // metadata.actuator.*\n FIELD_METADATA_SENSOR = 30; // metadata.sensor.*\n FIELD_METADATA_ATTRIBUTE = 40; // metadata.attribute.*\n}\n\n// Error response shall be an HTTP-like code.\n// Should follow https://www.w3.org/TR/viss2-transport/#status-codes.\nmessage Error {\n uint32 code = 1;\n string reason = 2;\n string message = 3;\n}\n\n// Used in get/set requests to report errors for specific entries\nmessage DataEntryError {\n string path = 1; // vss path\n Error error = 2;\n}\n\nmessage StringArray {\n repeated string values = 1;\n}\n\nmessage BoolArray {\n repeated bool values = 1;\n}\n\nmessage Int32Array {\n repeated sint32 values = 1;\n}\n\nmessage Int64Array {\n repeated sint64 values = 1;\n}\n\nmessage Uint32Array {\n repeated uint32 values = 1;\n}\n\nmessage Uint64Array {\n repeated uint64 values = 1;\n}\n\nmessage FloatArray {\n repeated float values = 1;\n}\n\nmessage DoubleArray {\n repeated double values = 1;\n}\n\n// Note on authorization:\n// Tokens (auth-token or auth-uuid) are sent as (GRPC / http2) metadata.\n//\n// The auth-token is a JWT compliant token as the examples found here:\n// https://github.com/eclipse/kuksa.val/tree/master/kuksa_certificates/jwt\n//\n// See also https://github.com/eclipse/kuksa.val/blob/master/doc/jwt.md\n//\n// Upon reception of auth-token, server shall generate an auth-uuid in metadata\n// that the client can use instead of auth-token in subsequent calls.\n\nservice VAL {\n // Get entries\n rpc Get(GetRequest) returns (GetResponse);\n\n // Set entries\n rpc Set(SetRequest) returns (SetResponse);\n\n // Subscribe to a set of entries\n //\n // Returns a stream of notifications.\n //\n // InvalidArgument is returned if the request is malformed.\n rpc Subscribe(SubscribeRequest) returns (stream SubscribeResponse);\n\n // Shall return information that allows the client to determine\n // what server/server implementation/version it is talking to\n // eg. kuksa-databroker 0.5.1\n rpc GetServerInfo(GetServerInfoRequest) returns (GetServerInfoResponse);\n}\n\n// Define which data we want\nmessage EntryRequest {\n string path = 1;\n View view = 2;\n repeated Field fields = 3;\n}\n\n// Request a set of entries.\nmessage GetRequest {\n repeated EntryRequest entries = 1;\n}\n\n// Global errors are specified in `error`.\n// Errors for individual entries are specified in `errors`.\nmessage GetResponse {\n repeated DataEntry entries = 1;\n repeated DataEntryError errors = 2;\n Error error = 3;\n}\n\n// Define the data we want to set\nmessage EntryUpdate {\n DataEntry entry = 1;\n repeated Field fields = 2;\n}\n\n// A list of entries to be updated\nmessage SetRequest {\n repeated EntryUpdate updates = 1;\n}\n\n// Global errors are specified in `error`.\n// Errors for individual entries are specified in `errors`.\nmessage SetResponse {\n Error error = 1;\n repeated DataEntryError errors = 2;\n}\n\n// Define what to subscribe to\nmessage SubscribeEntry {\n string path = 1;\n View view = 2;\n repeated Field fields = 3;\n}\n\n// Subscribe to changes in datapoints.\nmessage SubscribeRequest {\n repeated SubscribeEntry entries = 1;\n}\n\n// A subscription response\nmessage SubscribeResponse {\n repeated EntryUpdate updates = 1;\n}\n\nmessage GetServerInfoRequest {\n // Nothing yet\n}\n\nmessage GetServerInfoResponse {\n string name = 1;\n string version = 2;\n}\n", + "ca": "", + "chain": "", + "key": "", + "mutualTls": false, + "ssl": false, + "selfsigned": false, + "localServer": false + }, { "id": "1ef13f666f02eb22", "type": "function", @@ -1385,7 +1459,7 @@ "method": "Subscribe", "chain": "", "key": "", - "x": 460, + "x": 440, "y": 60, "wires": [ [ @@ -1433,7 +1507,7 @@ "from": "", "to": "", "reg": false, - "x": 790, + "x": 770, "y": 60, "wires": [ [] @@ -1458,7 +1532,7 @@ "from": "", "to": "", "reg": false, - "x": 790, + "x": 770, "y": 120, "wires": [ [] @@ -1475,7 +1549,7 @@ "initialize": "", "finalize": "", "libs": [], - "x": 650, + "x": 630, "y": 180, "wires": [ [] @@ -1496,7 +1570,7 @@ "checkall": "true", "repair": false, "outputs": 1, - "x": 650, + "x": 630, "y": 120, "wires": [ [ @@ -1516,7 +1590,7 @@ "septopics": true, "property": "payload", "topi": "topic", - "x": 650, + "x": 630, "y": 60, "wires": [ [ @@ -1913,12 +1987,280 @@ [] ] }, + { + "id": "7f44ba3d5f002ba6", + "type": "grpc-call", + "z": "3ba7d7d5ee4707e1", + "name": "RequestSet", + "server": "81356b1220393dc3", + "service": "VAL", + "method": "Set", + "chain": "", + "key": "", + "x": 610, + "y": 60, + "wires": [ + [ + "2e011263cb6fa367", + "8211b0d84bf813a9", + "380d9fc26a2bb333" + ] + ] + }, + { + "id": "edd6965ab2fc6f0b", + "type": "function", + "z": "3ba7d7d5ee4707e1", + "name": "SetValueRequest", + "func": "const data_type = global.get('VSSMetaData')[msg.topic]?.data_type\nlet value_type = 'string'\n\ntry {\n switch (data_type) {\n case 'INT8':\n case 'INT16':\n value_type = 'int32'\n break\n case 'UINT8':\n case 'UINT16':\n value_type = 'uint32'\n break\n case 'INT8_ARRAY':\n case 'INT16_ARRAY':\n value_type = 'int32_array'\n break\n case 'UINT8_ARRAY':\n case 'UINT16_ARRAY':\n value_type = 'uint32_array'\n break\n default:\n value_type = data_type.toLowerCase()\n }\n\n msg.payload = {\n updates: [{\n entry: {\n path: msg.topic,\n value: { [value_type]: msg.payload }\n },\n fields: [\n 2 // FIELD_VALUE\n ]\n }]\n }\n}\ncatch {\n msg.payload = null\n msg.error = `ERROR: Invalid property name: \"${msg.topic}\"`\n}\n\nreturn msg\n", + "outputs": 1, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 210, + "y": 60, + "wires": [ + [ + "8211b0d84bf813a9", + "54adb30a50182477", + "2e011263cb6fa367" + ] + ] + }, + { + "id": "2e011263cb6fa367", + "type": "function", + "z": "3ba7d7d5ee4707e1", + "name": "error", + "func": "const statusOk = { fill: \"green\", text: \"\" }\nconst statusError = { fill: \"red\", text: \"Error\" }\n\nconst error = !!( msg.error || msg.payload.error || msg.payload.errors?.length)\nconst status = error ? statusError : statusOk\n\nreturn [\n { payload: status },\n { payload: error }\n]\n", + "outputs": 2, + "timeout": "", + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 770, + "y": 140, + "wires": [ + [], + [ + "ce21f9ec137d4873" + ] + ] + }, + { + "id": "8211b0d84bf813a9", + "type": "switch", + "z": "3ba7d7d5ee4707e1", + "name": "error", + "property": "error", + "propertyType": "msg", + "rules": [ + { + "t": "nempty" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 770, + "y": 180, + "wires": [ + [ + "bf1747db2b73a08d" + ] + ] + }, + { + "id": "380d9fc26a2bb333", + "type": "switch", + "z": "3ba7d7d5ee4707e1", + "name": "payload.errors", + "property": "payload.errors", + "propertyType": "msg", + "rules": [ + { + "t": "nempty" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 800, + "y": 220, + "wires": [ + [ + "fd89b48a9faafd81" + ] + ] + }, + { + "id": "bf1747db2b73a08d", + "type": "change", + "z": "3ba7d7d5ee4707e1", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "error", + "tot": "msg" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 1000, + "y": 180, + "wires": [ + [] + ] + }, + { + "id": "fd89b48a9faafd81", + "type": "change", + "z": "3ba7d7d5ee4707e1", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "payload.errors", + "tot": "msg" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 1000, + "y": 220, + "wires": [ + [ + "bbdb4611ec77e6e3" + ] + ] + }, + { + "id": "bbdb4611ec77e6e3", + "type": "split", + "z": "3ba7d7d5ee4707e1", + "name": "", + "splt": "\\n", + "spltType": "str", + "arraySplt": 1, + "arraySpltType": "len", + "stream": false, + "addname": "key", + "x": 1150, + "y": 220, + "wires": [ + [ + "b94efb2bff0d6e7d" + ] + ] + }, + { + "id": "ce21f9ec137d4873", + "type": "switch", + "z": "3ba7d7d5ee4707e1", + "name": "payload false", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "false" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 790, + "y": 260, + "wires": [ + [ + "17e5cf57d548e040" + ] + ] + }, + { + "id": "17e5cf57d548e040", + "type": "change", + "z": "3ba7d7d5ee4707e1", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 1000, + "y": 260, + "wires": [ + [] + ] + }, + { + "id": "b94efb2bff0d6e7d", + "type": "function", + "z": "3ba7d7d5ee4707e1", + "name": "function 2", + "func": "msg.payload = `${msg.key}: ${msg.payload}`\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 1280, + "y": 220, + "wires": [ + [] + ] + }, + { + "id": "54adb30a50182477", + "type": "switch", + "z": "3ba7d7d5ee4707e1", + "name": "payload not null", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "nnull" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 420, + "y": 60, + "wires": [ + [ + "7f44ba3d5f002ba6" + ] + ] + }, { "id": "aa13d953ce65705f", "type": "subflow:f486fb61943eeb89", "z": "df00f52c34ea8f47", "name": "", - "x": 460, + "x": 480, "y": 60, "wires": [ [ @@ -1988,7 +2330,7 @@ ], "outputs": 0, "cts": false, - "x": 900, + "x": 980, "y": 60, "wires": [] }, @@ -1997,7 +2339,7 @@ "type": "subflow:d2f09c088927a668", "z": "df00f52c34ea8f47", "name": "", - "x": 680, + "x": 760, "y": 60, "wires": [ [ @@ -2100,7 +2442,7 @@ "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 660, + "x": 740, "y": 120, "wires": [] }, @@ -2109,8 +2451,8 @@ "type": "subflow:4f41580d2b2e81cf", "z": "df00f52c34ea8f47", "name": "", - "x": 300, - "y": 260, + "x": 330, + "y": 280, "wires": [ [ "4b836e3a267014ae" @@ -2135,10 +2477,10 @@ "once": true, "onceDelay": 0.1, "topic": "", - "payload": "SELECT Vehicle.Speed, Vehicle.OBD.FuelLevel, Vehicle.TravelledDistance, Vehicle.CurrentLocation.Latitude, Vehicle.CurrentLocation.Longitude, Vehicle.CurrentLocation.Heading, Vehicle.CurrentLocation.Altitude, Vehicle.Cabin.Seat.Row1.Pos1.Position", + "payload": "SELECT Vehicle.Speed, Vehicle.OBD.FuelLevel, Vehicle.TraveledDistance, Vehicle.CurrentLocation.Latitude, Vehicle.CurrentLocation.Longitude, Vehicle.CurrentLocation.Heading, Vehicle.CurrentLocation.Altitude, Vehicle.Cabin.Seat.Row1.Pos1.Position", "payloadType": "str", "x": 130, - "y": 260, + "y": 280, "wires": [ [ "aa7e9b6ee7e2af8c" @@ -2254,7 +2596,7 @@ }, { "t": "eq", - "v": "Vehicle.TravelledDistance", + "v": "Vehicle.TraveledDistance", "vt": "str" }, { @@ -2439,19 +2781,6 @@ ] ] }, - { - "id": "f220d8ca3595f40f", - "type": "subflow:2a90df8a197fb8f2", - "z": "df00f52c34ea8f47", - "name": "", - "x": 630, - "y": 640, - "wires": [ - [ - "244d32831b583804" - ] - ] - }, { "id": "bbb93784045bf6a3", "type": "inject", @@ -2471,13 +2800,13 @@ "once": false, "onceDelay": 0.1, "topic": "Vehicle.Speed", - "payload": "123.45", - "payloadType": "str", - "x": 560, + "payload": "234.5", + "payloadType": "num", + "x": 570, "y": 720, "wires": [ [ - "f220d8ca3595f40f" + "5f2b6762144657b0" ] ] }, @@ -2642,7 +2971,7 @@ "y": 640, "wires": [ [ - "f220d8ca3595f40f" + "5f2b6762144657b0" ] ] }, @@ -2663,7 +2992,7 @@ "font": "", "fontSize": 16, "color": "#000000", - "x": 800, + "x": 1000, "y": 640, "wires": [] }, @@ -2701,7 +3030,7 @@ "type": "subflow:194e312172a28c52", "z": "df00f52c34ea8f47", "name": "", - "x": 290, + "x": 320, "y": 340, "wires": [ [ @@ -3061,7 +3390,7 @@ "id": "12d793df46231615", "type": "inject", "z": "df00f52c34ea8f47", - "name": "", + "name": "Vehicle.OBD.FuelLevel Random", "props": [ { "p": "payload" @@ -3076,13 +3405,13 @@ "once": false, "onceDelay": 0.1, "topic": "Vehicle.OBD.FuelLevel", - "payload": "42.0", - "payloadType": "num", - "x": 580, - "y": 780, + "payload": "$random() * 100", + "payloadType": "jsonata", + "x": 610, + "y": 760, "wires": [ [ - "f220d8ca3595f40f" + "5f2b6762144657b0" ] ] }, @@ -3121,6 +3450,48 @@ "y": 260, "wires": [] }, + { + "id": "5f2b6762144657b0", + "type": "subflow:3ba7d7d5ee4707e1", + "z": "df00f52c34ea8f47", + "name": "", + "x": 710, + "y": 640, + "wires": [ + [ + "244d32831b583804" + ] + ] + }, + { + "id": "0724d7b8e8eb6333", + "type": "inject", + "z": "df00f52c34ea8f47", + "name": "Vehicle.CurrentLocation.Heading Random", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "Vehicle.CurrentLocation.Heading", + "payload": "$random() * 360", + "payloadType": "jsonata", + "x": 640, + "y": 800, + "wires": [ + [ + "5f2b6762144657b0" + ] + ] + }, { "id": "7b1e0e7cf64109ab", "type": "ui_slider", diff --git a/node-red/kuksa_val_v1_val.proto b/node-red/kuksa_val_v1_val.proto new file mode 100644 index 0000000..023e1be --- /dev/null +++ b/node-red/kuksa_val_v1_val.proto @@ -0,0 +1,381 @@ +/******************************************************************************** + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License 2.0 which is available at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +syntax = "proto3"; + +package kuksa.val.v1; + +import "google/protobuf/timestamp.proto"; + +// Describes a VSS entry +// When requesting an entry, the amount of information returned can +// be controlled by specifying either a `View` or a set of `Field`s. +message DataEntry { + // Defines the full VSS path of the entry. + string path = 1; // [field: FIELD_PATH] + + // The value (datapoint) + Datapoint value = 2; // [field: FIELD_VALUE] + + // Actuator target (only used if the entry is an actuator) + Datapoint actuator_target = 3; // [field: FIELD_ACTUATOR_TARGET] + + // Metadata for this entry + Metadata metadata = 10; // [field: FIELD_METADATA] +} + +message Datapoint { + google.protobuf.Timestamp timestamp = 1; + + oneof value { + string string = 11; + bool bool = 12; + sint32 int32 = 13; + sint64 int64 = 14; + uint32 uint32 = 15; + uint64 uint64 = 16; + float float = 17; + double double = 18; + StringArray string_array = 21; + BoolArray bool_array = 22; + Int32Array int32_array = 23; + Int64Array int64_array = 24; + Uint32Array uint32_array = 25; + Uint64Array uint64_array = 26; + FloatArray float_array = 27; + DoubleArray double_array = 28; + } +} + +message Metadata { + // Data type + // The VSS data type of the entry (i.e. the value, min, max etc). + // + // NOTE: protobuf doesn't have int8, int16, uint8 or uint16 which means + // that these values must be serialized as int32 and uint32 respectively. + DataType data_type = 11; // [field: FIELD_METADATA_DATA_TYPE] + + // Entry type + EntryType entry_type = 12; // [field: FIELD_METADATA_ENTRY_TYPE] + + // Description + // Describes the meaning and content of the entry. + optional string description = 13; // [field: FIELD_METADATA_DESCRIPTION] + + // Comment [optional] + // A comment can be used to provide additional informal information + // on a entry. + optional string comment = 14; // [field: FIELD_METADATA_COMMENT] + + // Deprecation [optional] + // Whether this entry is deprecated. Can contain recommendations of what + // to use instead. + optional string deprecation = 15; // [field: FIELD_METADATA_DEPRECATION] + + // Unit [optional] + // The unit of measurement + optional string unit = 16; // [field: FIELD_METADATA_UNIT] + + // Value restrictions [optional] + // Restrict which values are allowed. + // Only restrictions matching the DataType {datatype} above are valid. + ValueRestriction value_restriction = 17; // [field: FIELD_METADATA_VALUE_RESTRICTION] + + // Entry type specific metadata + oneof entry_specific { + Actuator actuator = 20; // [field: FIELD_METADATA_ACTUATOR] + Sensor sensor = 30; // [field: FIELD_METADATA_SENSOR] + Attribute attribute = 40; // [field: FIELD_METADATA_ATTRIBUTE] + } +} + +/////////////////////// +// Actuator specific fields +message Actuator { + // Nothing for now +} + +//////////////////////// +// Sensor specific +message Sensor { + // Nothing for now +} + +//////////////////////// +// Attribute specific +message Attribute { + // Nothing for now. +} + +// Value restriction +// +// One ValueRestriction{type} for each type, since +// they don't make sense unless the types match +// +message ValueRestriction { + oneof type { + ValueRestrictionString string = 21; + // For signed VSS integers + ValueRestrictionInt signed = 22; + // For unsigned VSS integers + ValueRestrictionUint unsigned = 23; + // For floating point VSS values (float and double) + ValueRestrictionFloat floating_point = 24; + } +} + +message ValueRestrictionInt { + optional sint64 min = 1; + optional sint64 max = 2; + repeated sint64 allowed_values = 3; +} + +message ValueRestrictionUint { + optional uint64 min = 1; + optional uint64 max = 2; + repeated uint64 allowed_values = 3; +} + +message ValueRestrictionFloat { + optional double min = 1; + optional double max = 2; + + // allowed for doubles/floats not recommended + repeated double allowed_values = 3; +} + +// min, max doesn't make much sense for a string +message ValueRestrictionString { + repeated string allowed_values = 3; +} + +// VSS Data type of a signal +// +// Protobuf doesn't support int8, int16, uint8 or uint16. +// These are mapped to int32 and uint32 respectively. +// +enum DataType { + DATA_TYPE_UNSPECIFIED = 0; + DATA_TYPE_STRING = 1; + DATA_TYPE_BOOLEAN = 2; + DATA_TYPE_INT8 = 3; + DATA_TYPE_INT16 = 4; + DATA_TYPE_INT32 = 5; + DATA_TYPE_INT64 = 6; + DATA_TYPE_UINT8 = 7; + DATA_TYPE_UINT16 = 8; + DATA_TYPE_UINT32 = 9; + DATA_TYPE_UINT64 = 10; + DATA_TYPE_FLOAT = 11; + DATA_TYPE_DOUBLE = 12; + DATA_TYPE_TIMESTAMP = 13; + DATA_TYPE_STRING_ARRAY = 20; + DATA_TYPE_BOOLEAN_ARRAY = 21; + DATA_TYPE_INT8_ARRAY = 22; + DATA_TYPE_INT16_ARRAY = 23; + DATA_TYPE_INT32_ARRAY = 24; + DATA_TYPE_INT64_ARRAY = 25; + DATA_TYPE_UINT8_ARRAY = 26; + DATA_TYPE_UINT16_ARRAY = 27; + DATA_TYPE_UINT32_ARRAY = 28; + DATA_TYPE_UINT64_ARRAY = 29; + DATA_TYPE_FLOAT_ARRAY = 30; + DATA_TYPE_DOUBLE_ARRAY = 31; + DATA_TYPE_TIMESTAMP_ARRAY = 32; +} + +// Entry type +enum EntryType { + ENTRY_TYPE_UNSPECIFIED = 0; + ENTRY_TYPE_ATTRIBUTE = 1; + ENTRY_TYPE_SENSOR = 2; + ENTRY_TYPE_ACTUATOR = 3; +} + +// A `View` specifies a set of fields which should +// be populated in a `DataEntry` (in a response message) +enum View { + VIEW_UNSPECIFIED = 0; // Unspecified. Equivalent to VIEW_CURRENT_VALUE unless `fields` are explicitly set. + VIEW_CURRENT_VALUE = 1; // Populate DataEntry with value. + VIEW_TARGET_VALUE = 2; // Populate DataEntry with actuator target. + VIEW_METADATA = 3; // Populate DataEntry with metadata. + VIEW_FIELDS = 10; // Populate DataEntry only with requested fields. + VIEW_ALL = 20; // Populate DataEntry with everything. +} + +// A `Field` corresponds to a specific field of a `DataEntry`. +// +// It can be used to: +// * populate only specific fields of a `DataEntry` response. +// * specify which fields of a `DataEntry` should be set as +// part of a `Set` request. +// * subscribe to only specific fields of a data entry. +// * convey which fields of an updated `DataEntry` have changed. +enum Field { + FIELD_UNSPECIFIED = 0; // "*" i.e. everything + FIELD_PATH = 1; // path + FIELD_VALUE = 2; // value + FIELD_ACTUATOR_TARGET = 3; // actuator_target + FIELD_METADATA = 10; // metadata.* + FIELD_METADATA_DATA_TYPE = 11; // metadata.data_type + FIELD_METADATA_DESCRIPTION = 12; // metadata.description + FIELD_METADATA_ENTRY_TYPE = 13; // metadata.entry_type + FIELD_METADATA_COMMENT = 14; // metadata.comment + FIELD_METADATA_DEPRECATION = 15; // metadata.deprecation + FIELD_METADATA_UNIT = 16; // metadata.unit + FIELD_METADATA_VALUE_RESTRICTION = 17; // metadata.value_restriction.* + FIELD_METADATA_ACTUATOR = 20; // metadata.actuator.* + FIELD_METADATA_SENSOR = 30; // metadata.sensor.* + FIELD_METADATA_ATTRIBUTE = 40; // metadata.attribute.* +} + +// Error response shall be an HTTP-like code. +// Should follow https://www.w3.org/TR/viss2-transport/#status-codes. +message Error { + uint32 code = 1; + string reason = 2; + string message = 3; +} + +// Used in get/set requests to report errors for specific entries +message DataEntryError { + string path = 1; // vss path + Error error = 2; +} + +message StringArray { + repeated string values = 1; +} + +message BoolArray { + repeated bool values = 1; +} + +message Int32Array { + repeated sint32 values = 1; +} + +message Int64Array { + repeated sint64 values = 1; +} + +message Uint32Array { + repeated uint32 values = 1; +} + +message Uint64Array { + repeated uint64 values = 1; +} + +message FloatArray { + repeated float values = 1; +} + +message DoubleArray { + repeated double values = 1; +} + +// Note on authorization: +// Tokens (auth-token or auth-uuid) are sent as (GRPC / http2) metadata. +// +// The auth-token is a JWT compliant token as the examples found here: +// https://github.com/eclipse/kuksa.val/tree/master/kuksa_certificates/jwt +// +// See also https://github.com/eclipse/kuksa.val/blob/master/doc/jwt.md +// +// Upon reception of auth-token, server shall generate an auth-uuid in metadata +// that the client can use instead of auth-token in subsequent calls. + +service VAL { + // Get entries + rpc Get(GetRequest) returns (GetResponse); + + // Set entries + rpc Set(SetRequest) returns (SetResponse); + + // Subscribe to a set of entries + // + // Returns a stream of notifications. + // + // InvalidArgument is returned if the request is malformed. + rpc Subscribe(SubscribeRequest) returns (stream SubscribeResponse); + + // Shall return information that allows the client to determine + // what server/server implementation/version it is talking to + // eg. kuksa-databroker 0.5.1 + rpc GetServerInfo(GetServerInfoRequest) returns (GetServerInfoResponse); +} + +// Define which data we want +message EntryRequest { + string path = 1; + View view = 2; + repeated Field fields = 3; +} + +// Request a set of entries. +message GetRequest { + repeated EntryRequest entries = 1; +} + +// Global errors are specified in `error`. +// Errors for individual entries are specified in `errors`. +message GetResponse { + repeated DataEntry entries = 1; + repeated DataEntryError errors = 2; + Error error = 3; +} + +// Define the data we want to set +message EntryUpdate { + DataEntry entry = 1; + repeated Field fields = 2; +} + +// A list of entries to be updated +message SetRequest { + repeated EntryUpdate updates = 1; +} + +// Global errors are specified in `error`. +// Errors for individual entries are specified in `errors`. +message SetResponse { + Error error = 1; + repeated DataEntryError errors = 2; +} + +// Define what to subscribe to +message SubscribeEntry { + string path = 1; + View view = 2; + repeated Field fields = 3; +} + +// Subscribe to changes in datapoints. +message SubscribeRequest { + repeated SubscribeEntry entries = 1; +} + +// A subscription response +message SubscribeResponse { + repeated EntryUpdate updates = 1; +} + +message GetServerInfoRequest { + // Nothing yet +} + +message GetServerInfoResponse { + string name = 1; + string version = 2; +} diff --git a/node-red/sdv_databroker_v1.proto b/node-red/sdv_databroker_v1.proto index 1e3def8..b812ffd 100644 --- a/node-red/sdv_databroker_v1.proto +++ b/node-red/sdv_databroker_v1.proto @@ -1,2275 +1,317 @@ -[ - { - "id": "4cf5bc897925d109", - "type": "tab", - "label": "Flow 1", - "disabled": false, - "info": "", - "env": [] - }, - { - "id": "df00f52c34ea8f47", - "type": "tab", - "label": "VSS", - "disabled": false, - "info": "", - "env": [] - }, - { - "id": "bdd28665bfe7cc3e", - "type": "tab", - "label": "Leda Status", - "disabled": false, - "info": "", - "env": [] - }, - { - "id": "8893ce2e3bcd6415", - "type": "tab", - "label": "Connections", - "disabled": false, - "info": "", - "env": [] - }, - { - "id": "544891f94013761e", - "type": "subflow", - "name": "topic filter", - "info": "## MQTT Topic filter\n\n### Description\nFilters messages based on the value of `msg.topic` using standard MQTT topic filter notation.\n* `#` match all\n* `+` match one level\n\n### example filters\n* `home/+/temperature` \n * will match `home/bedroom/temperature`\n * will match `home/livingroom/temperature`\n * will not match `home/room/1/temperature`\n * will not match `room/1/temperature`\n* `home/#` \n * will match `home/location`\n * will match `home/livingroom/temperature`\n * will match `home/livingroom/humidity`\n * will not match `room/1/temperature`\n\n\n### Outputs\n\n#### Output 1 - match\nMessages with a topic that matches the `topic filter` will be sent out this output.\n\n#### Output 2 - no match\nMessages with a topic that does not match the `topic filter` will be sent out this output to permit next level filtering\n", - "category": "function", - "in": [ - { - "x": 68, - "y": 80, - "wires": [ - { - "id": "1ef13f666f02eb22" - } - ] - } - ], - "out": [ - { - "x": 340, - "y": 48, - "wires": [ - { - "id": "1ef13f666f02eb22", - "port": 0 - } - ] - }, - { - "x": 340, - "y": 96, - "wires": [ - { - "id": "1ef13f666f02eb22", - "port": 1 - } - ] - } - ], - "env": [ - { - "name": "filter", - "type": "str", - "value": "", - "ui": { - "label": { - "en-US": "Topic filter" - }, - "type": "input", - "opts": { - "types": [ - "str" - ] - } - } - }, - { - "name": "status", - "type": "bool", - "value": "true", - "ui": { - "label": { - "en-US": "Show topic" - }, - "type": "input", - "opts": { - "types": [ - "bool" - ] - } - } - } - ], - "meta": { - "type": "mqtt-topic-filter", - "version": "1.0.0", - "author": "steve-mcl", - "desc": "A node to filter MQTT topics", - "keywords": "mqtt", - "license": "MIT" - }, - "color": "#D8BFD8", - "outputLabels": [ - "Match", - "No Match" - ], - "icon": "font-awesome/fa-filter", - "status": { - "x": 548, - "y": 160, - "wires": [ - { - "id": "a9453c972b2e207a", - "port": 0 - }, - { - "id": "3f82486996b3cfd5", - "port": 0 - } - ] - } - }, - { - "id": "b1b3032b9f41f38c", - "type": "subflow", - "name": "currentstate ⇒ ui list", - "info": "Converts a currentstate object to the UI list format", - "category": "SDV", - "in": [ - { - "x": 60, - "y": 60, - "wires": [ - { - "id": "5ca0c68c699fe867" - } - ] - } - ], - "out": [ - { - "x": 720, - "y": 60, - "wires": [ - { - "id": "e39adf356d1d037b", - "port": 0 - } - ] - } - ], - "env": [], - "meta": {}, - "color": "#E2D96E", - "icon": "node-red/swap.svg" - }, - { - "id": "f486fb61943eeb89", - "type": "subflow", - "name": "GetDatapoints", - "info": "# GetDatapoints\n\nRequest a set of datapoints (values)\n\nExpects `msg.payload` to contain an array with multiple VSS signal names or a string containing a single name.\n\nReturns an object containing all the requested datapoints as `msg.payload`.\nIf there is an error, `msg.error` is set to the error description.\n", - "category": "SDV", - "in": [ - { - "x": 60, - "y": 60, - "wires": [ - { - "id": "343d49df0601f0d6" - } - ] - } - ], - "out": [ - { - "x": 900, - "y": 60, - "wires": [ - { - "id": "e808e6b1a93017c6", - "port": 0 - } - ] - }, - { - "x": 910, - "y": 120, - "wires": [ - { - "id": "4744241463197a11", - "port": 0 - } - ] - } - ], - "env": [], - "meta": { - "module": "GetDatapoints", - "license": "Apache-2.0" - }, - "color": "#5d8dab", - "inputLabels": [ - "payload" - ], - "outputLabels": [ - "payload", - "error" - ], - "icon": "node-red-contrib-grpc/grpc.svg", - "status": { - "x": 940, - "y": 180, - "wires": [ - { - "id": "c8f806e6b667dae4", - "port": 0 - } - ] - } - }, - { - "id": "c86786afde87c8e5", - "type": "subflow", - "name": "datapoints ⇒ ui list", - "info": "", - "category": "SDV", - "in": [ - { - "x": 60, - "y": 60, - "wires": [ - { - "id": "c7cc1bdeedc97a30" - } - ] - } - ], - "out": [ - { - "x": 720, - "y": 60, - "wires": [ - { - "id": "70fd1b3003f70b0b", - "port": 0 - } - ] - } - ], - "env": [], - "meta": {}, - "color": "#E2D96E", - "icon": "node-red/swap.svg" - }, - { - "id": "d2f09c088927a668", - "type": "subflow", - "name": "datapoints ⇒ ui table", - "info": "", - "category": "SDV", - "in": [ - { - "x": 60, - "y": 60, - "wires": [ - { - "id": "9e9a00fa56add8cc" - } - ] - } - ], - "out": [ - { - "x": 720, - "y": 60, - "wires": [ - { - "id": "87c9693a47913239", - "port": 0 - } - ] - } - ], - "env": [], - "meta": { - "license": "Apache-2.0" - }, - "color": "#E2D96E", - "icon": "node-red/swap.svg" - }, - { - "id": "e6131ae387b9d6fc", - "type": "subflow", - "name": "GetDatapoints (2)", - "info": "# GetDatapoints\n\nRequest a set of datapoints (values)\n\nExpects `msg.payload` to contain an array with multiple VSS signal names or a string containing a single name.\n\nReturns an object containing all the requested datapoints as `msg.payload`.\nIf there is an error, `msg.error` is set to the error description.\n", - "category": "SDV", - "in": [ - { - "x": 60, - "y": 60, - "wires": [ - { - "id": "192efc0ccd22a33c" - } - ] - } - ], - "out": [ - { - "x": 900, - "y": 60, - "wires": [ - { - "id": "30cd6e5a1272bc18", - "port": 0 - } - ] - }, - { - "x": 910, - "y": 120, - "wires": [ - { - "id": "a0d0890849b154e2", - "port": 0 - } - ] - } - ], - "env": [], - "meta": { - "module": "GetDatapoints", - "license": "Apache-2.0" - }, - "color": "#5d8dab", - "inputLabels": [ - "payload" - ], - "outputLabels": [ - "payload", - "error" - ], - "icon": "node-red-contrib-grpc/grpc.svg", - "status": { - "x": 940, - "y": 180, - "wires": [ - { - "id": "cceb670f279aa14d", - "port": 0 - } - ] - } - }, - { - "id": "4f41580d2b2e81cf", - "type": "subflow", - "name": "Subscribe", - "info": "", - "category": "SDV", - "in": [ - { - "x": 60, - "y": 60, - "wires": [ - { - "id": "8cbaf8f7c506d7fe" - } - ] - } - ], - "out": [ - { - "x": 940, - "y": 60, - "wires": [ - { - "id": "87a5c171fc770428", - "port": 0 - } - ] - }, - { - "x": 950, - "y": 120, - "wires": [ - { - "id": "3d9927502d12ba5d", - "port": 0 - } - ] - } - ], - "env": [], - "meta": { - "module": "Subscribe", - "license": "Apache-2.0" - }, - "color": "#5d8dab", - "inputLabels": [ - "query" - ], - "outputLabels": [ - "payload", - "error" - ], - "icon": "node-red-contrib-grpc/grpc.svg", - "status": { - "x": 980, - "y": 180, - "wires": [ - { - "id": "ec790b0f84c62179", - "port": 0 - } - ] - } - }, - { - "id": "f206c2b27c248139", - "type": "subflow", - "name": "select field", - "info": "", - "category": "SDV", - "in": [ - { - "x": 60, - "y": 60, - "wires": [ - { - "id": "fdb285d6f548d25e" - } - ] - } - ], - "out": [ - { - "x": 400, - "y": 40, - "wires": [ - { - "id": "fdb285d6f548d25e", - "port": 0 - } - ] - } - ], - "env": [ - { - "name": "signal", - "type": "str", - "value": "" - } - ], - "meta": {}, - "color": "#E2D96E", - "icon": "node-red/rbe.png", - "status": { - "x": 400, - "y": 100, - "wires": [ - { - "id": "fdb285d6f548d25e", - "port": 1 - } - ] - } - }, - { - "id": "fb585e7f0dd51cfd", - "type": "mqtt-broker", - "name": "leda-mqtt", - "broker": "${MQTT_ADDR}", - "port": "${MQTT_PORT}", - "clientid": "", - "autoConnect": true, - "usetls": false, - "protocolVersion": "4", - "keepalive": "60", - "cleansession": true, - "birthTopic": "", - "birthQos": "0", - "birthPayload": "", - "birthMsg": {}, - "closeTopic": "", - "closeQos": "0", - "closePayload": "", - "closeMsg": {}, - "willTopic": "", - "willQos": "0", - "willPayload": "", - "willMsg": {}, - "userProps": "", - "sessionExpiry": "" - }, - { - "id": "6bcbe8d24d01e15f", - "type": "ui_base", - "theme": { - "name": "theme-custom", - "lightTheme": { - "default": "#0094CE", - "baseColor": "#0094CE", - "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", - "edited": true, - "reset": false - }, - "darkTheme": { - "default": "#097479", - "baseColor": "#5d8dab", - "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", - "edited": true, - "reset": false - }, - "customTheme": { - "name": "Leda", - "default": "#4B7930", - "baseColor": "#5d8dab", - "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", - "reset": false - }, - "themeState": { - "base-color": { - "default": "#4B7930", - "value": "#5d8dab", - "edited": true - }, - "page-titlebar-backgroundColor": { - "value": "#5d8dab", - "edited": false - }, - "page-backgroundColor": { - "value": "#434954", - "edited": true - }, - "page-sidebar-backgroundColor": { - "value": "#383c45", - "edited": true - }, - "group-textColor": { - "value": "#8fb0c5", - "edited": false - }, - "group-borderColor": { - "value": "#23252a", - "edited": true - }, - "group-backgroundColor": { - "value": "#383c45", - "edited": true - }, - "widget-textColor": { - "value": "#fafafa", - "edited": true - }, - "widget-backgroundColor": { - "value": "#5d8dab", - "edited": false - }, - "widget-borderColor": { - "value": "#383c45", - "edited": true - }, - "base-font": { - "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" - } - }, - "angularTheme": { - "primary": "indigo", - "accents": "blue", - "warn": "red", - "background": "grey", - "palette": "light" - } - }, - "site": { - "name": "Leda Dashboard", - "hideToolbar": "false", - "allowSwipe": "false", - "lockMenu": "true", - "allowTempTheme": "true", - "dateFormat": "DD/MM/YYYY", - "sizes": { - "sx": 48, - "sy": 48, - "gx": 6, - "gy": 6, - "cx": 6, - "cy": 6, - "px": 0, - "py": 0 - } - } - }, - { - "id": "31b9a607bd7cffa4", - "type": "ui_tab", - "name": "Update Status", - "icon": "system_update", - "order": 2, - "disabled": false, - "hidden": false - }, - { - "id": "853f626a78b514a7", - "type": "ui_group", - "name": "Self Update", - "tab": "31b9a607bd7cffa4", - "order": 1, - "disp": true, - "width": "9", - "collapse": false, - "className": "" - }, - { - "id": "a17b70ba881ce441", - "type": "ui_group", - "name": "Containers Update", - "tab": "31b9a607bd7cffa4", - "order": 2, - "disp": true, - "width": "9", - "collapse": false, - "className": "" - }, - { - "id": "5d06f71eb2d78bfd", - "type": "ui_group", - "name": "Vehicle Update", - "tab": "31b9a607bd7cffa4", - "order": 3, - "disp": true, - "width": "9", - "collapse": false, - "className": "" - }, - { - "id": "1f6dc92172f4e94b", - "type": "grpc-server", - "port": "${BROKER_PORT}", - "name": "leda-databroker", - "server": "${BROKER_ADDR}", - "protoFile": "/********************************************************************************\n * Copyright (c) 2022 Contributors to the Eclipse Foundation\n *\n * See the NOTICE file(s) distributed with this work for additional\n * information regarding copyright ownership.\n *\n * This program and the accompanying materials are made available under the\n * terms of the Apache License 2.0 which is available at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * SPDX-License-Identifier: Apache-2.0\n ********************************************************************************/\n\nsyntax = \"proto3\";\n\npackage sdv.databroker.v1;\n\nimport \"google/protobuf/timestamp.proto\";\n\n// Data type of a signal\n//\n// Protobuf doesn't support int8, int16, uint8 or uint16.\n// These are mapped to sint32 and uint32 respectively.\n//\nenum DataType {\n STRING = 0;\n BOOL = 1;\n INT8 = 2;\n INT16 = 3;\n INT32 = 4;\n INT64 = 5;\n UINT8 = 6;\n UINT16 = 7;\n UINT32 = 8;\n UINT64 = 9;\n FLOAT = 10;\n DOUBLE = 11;\n TIMESTAMP = 12;\n STRING_ARRAY = 20;\n BOOL_ARRAY = 21;\n INT8_ARRAY = 22;\n INT16_ARRAY = 23;\n INT32_ARRAY = 24;\n INT64_ARRAY = 25;\n UINT8_ARRAY = 26;\n UINT16_ARRAY = 27;\n UINT32_ARRAY = 28;\n UINT64_ARRAY = 29;\n FLOAT_ARRAY = 30;\n DOUBLE_ARRAY = 31;\n TIMESTAMP_ARRAY = 32;\n}\n\nenum DatapointError {\n UNKNOWN_DATAPOINT = 0;\n INVALID_TYPE = 1;\n ACCESS_DENIED = 2;\n INTERNAL_ERROR = 3;\n OUT_OF_BOUNDS = 4;\n}\n\nenum EntryType {\n ENTRY_TYPE_UNSPECIFIED = 0;\n ENTRY_TYPE_SENSOR = 1;\n ENTRY_TYPE_ACTUATOR = 2;\n ENTRY_TYPE_ATTRIBUTE = 3;\n}\n\nenum ChangeType {\n STATIC = 0; // Value never changes\n ON_CHANGE = 1; // Updates are provided every time the value changes (i.e.\n // window is open / closed)\n CONTINUOUS = 2; // Value is updated continuously. Broker needs to tell\n // provider the preferred (update) frequency.\n}\n\nmessage StringArray {\n repeated string values = 1;\n}\n\nmessage BoolArray {\n repeated bool values = 1;\n}\n\nmessage Int32Array {\n repeated sint32 values = 1;\n}\n\nmessage Int64Array {\n repeated sint64 values = 1;\n}\n\nmessage Uint32Array {\n repeated uint32 values = 1;\n}\n\nmessage Uint64Array {\n repeated uint64 values = 1;\n}\n\nmessage FloatArray {\n repeated float values = 1;\n}\n\nmessage DoubleArray {\n repeated double values = 1;\n}\n\nmessage Datapoint {\n // Timestamp of the value\n google.protobuf.Timestamp timestamp = 1;\n\n // values\n oneof value {\n Failure failure_value = 10;\n string string_value = 11;\n bool bool_value = 12;\n sint32 int32_value = 13;\n sint64 int64_value = 14;\n uint32 uint32_value = 15;\n uint64 uint64_value = 16;\n float float_value = 17;\n double double_value = 18;\n StringArray string_array = 21;\n BoolArray bool_array = 22;\n Int32Array int32_array = 23;\n Int64Array int64_array = 24;\n Uint32Array uint32_array = 25;\n Uint64Array uint64_array = 26;\n FloatArray float_array = 27;\n DoubleArray double_array = 28;\n }\n\n enum Failure {\n // The data point is known, but doesn't have a valid value\n INVALID_VALUE = 0;\n // The data point is known, but no value is available\n NOT_AVAILABLE = 1;\n // Unknown datapoint\n UNKNOWN_DATAPOINT = 2;\n // Access denied\n ACCESS_DENIED = 3;\n // Unexpected internal error\n INTERNAL_ERROR = 4;\n }\n}\n\nmessage Metadata {\n // Id to be used in \"get\" and \"subscribe\" requests. Ids stay valid during\n // one power cycle, only.\n int32 id = 1;\n EntryType entry_type = 2;\n string name = 4;\n DataType data_type = 5;\n ChangeType change_type = 6; // CONTINUOUS or STATIC or ON_CHANGE\n string description = 7;\n\n // int32 min_update_hz = 10; // Only for CONTINUOUS\n // int32 max_update_hz = 11; // Only for CONTINUOUS\n}\n\nservice Broker {\n // Request a set of datapoints (values)\n //\n // Returns a list of requested data points.\n //\n // InvalidArgument is returned if the request is malformed.\n rpc GetDatapoints(GetDatapointsRequest) returns (GetDatapointsReply);\n\n // Set a datapoint (values)\n rpc SetDatapoints(SetDatapointsRequest) returns (SetDatapointsReply);\n\n // Subscribe to a set of data points or conditional expressions\n // using the Data Broker Query Syntax (described in QUERY.md)\n //\n // Returns a stream of replies.\n //\n // InvalidArgument is returned if the request is malformed.\n rpc Subscribe(SubscribeRequest) returns (stream SubscribeReply);\n\n // Request the metadata of a set of datapoints\n //\n // Returns metadata of the requested data points that exist.\n rpc GetMetadata(GetMetadataRequest) returns (GetMetadataReply);\n}\n\nmessage GetDatapointsRequest {\n // A list of requested data points.\n repeated string datapoints = 1;\n}\n\nmessage GetDatapointsReply {\n // Contains the values of the requested data points.\n // If a requested data point is not available, the corresponding Datapoint\n // will have the respective failure value set.\n map datapoints = 1;\n}\n\nmessage SetDatapointsRequest {\n // A map of data points to set\n map datapoints = 1;\n}\n\nmessage SetDatapointsReply {\n // A map of errors (if any)\n map errors = 1;\n}\n\nmessage SubscribeRequest {\n // Subscribe to a set of data points (or expressions) described\n // by the provided query.\n // The query syntax is a subset of SQL and is described in more\n // detail in the QUERY.md file.\n string query = 2;\n}\n\nmessage SubscribeReply {\n // Contains the fields specified by the query.\n // If a requested data point value is not available, the corresponding\n // Datapoint will have it's respective failure value set.\n map fields = 1;\n}\n\nmessage GetMetadataRequest {\n // Request metadata for a list of data points referenced by their names.\n // e.g. \"Vehicle.Cabin.Seat.Row1.Pos1.Position\" or \"Vehicle.Speed\".\n //\n // If no names are provided, metadata for all known data points will be\n // returned.\n repeated string names = 1;\n}\n\nmessage GetMetadataReply {\n // Contains metadata of the requested data points. If a data point\n // doesn't exist (i.e. not known to the Data Broker) the corresponding\n // Metadata isn't part of the returned list.\n repeated Metadata list = 1;\n}", - "ca": "", - "chain": "", - "key": "", - "mutualTls": false, - "ssl": false, - "selfsigned": false, - "localServer": false - }, - { - "id": "6766a5d8d8a377ed", - "type": "ui_tab", - "name": "VSS", - "icon": "dashboard", - "order": 1, - "disabled": false, - "hidden": false - }, - { - "id": "0b2827de9e647c6b", - "type": "ui_group", - "name": "Query Datapoint(s)", - "tab": "6766a5d8d8a377ed", - "order": 2, - "disp": true, - "width": "12", - "collapse": false, - "className": "" - }, - { - "id": "ff74e0831e03da9e", - "type": "ui_group", - "name": "Driving Data", - "tab": "6766a5d8d8a377ed", - "order": 1, - "disp": false, - "width": "6", - "collapse": false, - "className": "" - }, - { - "id": "1ef13f666f02eb22", - "type": "function", - "z": "544891f94013761e", - "name": "filter", - "func": "var a = msg.topic;\nvar b = env.get(\"filter\") || '#';\n\nif(a===b) { return [msg, null]; }\nif(b==='#') { \n if(a) return [msg, null]; //if topic is something, OK\n return [null, msg];//otherwise, fail!\n}\nvar nameSegments = a.split('/');\nvar filterSegments = b.split('/');\nfor (var i = 0; i < filterSegments.length; i++) {\n var topicSegment = nameSegments[i];\n var patternSegment = filterSegments[i];\n var match = false;\n if(topicSegment === patternSegment) { match = true; }\n if(patternSegment === '+') { match = true; }\n if(patternSegment === '#') { return [msg, null]; }\n if(match === false) { return [null, msg]; }\n}\nif(nameSegments.length !== filterSegments.length) { return [null, msg]; }\n\nreturn [msg, null];\n", - "outputs": 2, - "noerr": 0, - "initialize": "", - "finalize": "", - "x": 192, - "y": 80, - "wires": [ - [], - [] - ] - }, - { - "id": "d55c5c0a665fdcc9", - "type": "inject", - "z": "544891f94013761e", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": true, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 148, - "y": 160, - "wires": [ - [ - "be5346c60d2899c1" - ] - ] - }, - { - "id": "a9453c972b2e207a", - "type": "function", - "z": "544891f94013761e", - "name": "", - "func": "var b = env.get(\"filter\") || '#';\nnode.status({text:b})\nmsg.payload = b;\nreturn msg;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "x": 428, - "y": 144, - "wires": [ - [] - ] - }, - { - "id": "be5346c60d2899c1", - "type": "switch", - "z": "544891f94013761e", - "name": "", - "property": "status", - "propertyType": "env", - "rules": [ - { - "t": "true" - }, - { - "t": "else" - } - ], - "checkall": "true", - "repair": false, - "outputs": 2, - "x": 290, - "y": 160, - "wires": [ - [ - "a9453c972b2e207a" - ], - [ - "3f82486996b3cfd5" - ] - ] - }, - { - "id": "3f82486996b3cfd5", - "type": "function", - "z": "544891f94013761e", - "name": "", - "func": "node.status({})\ndelete msg.payload;\nreturn msg;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "x": 428, - "y": 192, - "wires": [ - [] - ] - }, - { - "id": "5ca0c68c699fe867", - "type": "split", - "z": "b1b3032b9f41f38c", - "name": "", - "splt": "\\n", - "spltType": "str", - "arraySplt": 1, - "arraySpltType": "len", - "stream": false, - "addname": "", - "x": 170, - "y": 60, - "wires": [ - [ - "283cd6e88e8ad3d3" - ] - ] - }, - { - "id": "b029b52f6c2b4a24", - "type": "join", - "z": "b1b3032b9f41f38c", - "name": "", - "mode": "auto", - "build": "object", - "property": "payload", - "propertyType": "msg", - "key": "topic", - "joiner": "\\n", - "joinerType": "str", - "accumulate": "false", - "timeout": "", - "count": "", - "reduceRight": false, - "x": 470, - "y": 60, - "wires": [ - [ - "e39adf356d1d037b" - ] - ] - }, - { - "id": "283cd6e88e8ad3d3", - "type": "function", - "z": "b1b3032b9f41f38c", - "name": "function", - "func": "const icons = {\n IMAGE: \"fa-cube\",\n APPLICATION: \"fa-cogs\",\n CONTAINER: \"cloud\",\n _IMAGE: \"📦\",\n _APPLICATION: \"⚙️\",\n _CONTAINER: \"\"\n}\nconst payload = {\n title: msg.payload.name,\n description: `${msg.payload.id}
${msg.payload.version}`,\n icon_name: icons[msg.payload.type] || \"fa-question\",\n _id: msg.payload.id,\n _version: msg.payload.version,\n _name: msg.payload.name,\n _type: msg.payload.type\n}\nmsg.payload = payload\nreturn msg;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 320, - "y": 60, - "wires": [ - [ - "b029b52f6c2b4a24" - ] - ] - }, - { - "id": "e39adf356d1d037b", - "type": "sort", - "z": "b1b3032b9f41f38c", - "name": "", - "order": "ascending", - "as_num": false, - "target": "payload", - "targetType": "msg", - "msgKey": "$lowercase(title ? title : description)", - "msgKeyType": "jsonata", - "seqKey": "payload", - "seqKeyType": "msg", - "x": 610, - "y": 60, - "wires": [ - [] - ] - }, - { - "id": "73479712c98cf763", - "type": "grpc-call", - "z": "f486fb61943eeb89", - "name": "RequestDatapoints", - "server": "1f6dc92172f4e94b", - "service": "Broker", - "method": "GetDatapoints", - "chain": "", - "key": "", - "x": 450, - "y": 60, - "wires": [ - [ - "e808e6b1a93017c6", - "6ff421e4278d80d2", - "c8f806e6b667dae4" - ] - ] - }, - { - "id": "343d49df0601f0d6", - "type": "function", - "z": "f486fb61943eeb89", - "name": "GetDatapointsRequest", - "func": "let data = msg.payload\n\nif (typeof msg.payload === \"string\") {\n data = msg.payload.replace(/\\s+/g, ' ').split(\" \")\n}\n\nmsg.payload = {\n datapoints: data\n}\n\nreturn msg;\n", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 220, - "y": 60, - "wires": [ - [ - "73479712c98cf763" - ] - ] - }, - { - "id": "e808e6b1a93017c6", - "type": "change", - "z": "f486fb61943eeb89", - "name": "datapoints", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "payload.datapoints", - "tot": "msg" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 650, - "y": 60, - "wires": [ - [] - ] - }, - { - "id": "4744241463197a11", - "type": "change", - "z": "f486fb61943eeb89", - "name": "error", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "error", - "tot": "msg" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 770, - "y": 120, - "wires": [ - [] - ] - }, - { - "id": "c8f806e6b667dae4", - "type": "function", - "z": "f486fb61943eeb89", - "name": "status", - "func": "const statusOk = { fill: \"green\", text: \"\" }\nconst statusError = { fill: \"red\", text: \"Error\" }\n\nmsg.payload = msg.error ? statusError : statusOk\nreturn msg\n", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 630, - "y": 180, - "wires": [ - [] - ] - }, - { - "id": "6ff421e4278d80d2", - "type": "switch", - "z": "f486fb61943eeb89", - "name": "", - "property": "error", - "propertyType": "msg", - "rules": [ - { - "t": "nempty" - } - ], - "checkall": "true", - "repair": false, - "outputs": 1, - "x": 630, - "y": 120, - "wires": [ - [ - "4744241463197a11" - ] - ] - }, - { - "id": "c7cc1bdeedc97a30", - "type": "split", - "z": "c86786afde87c8e5", - "name": "", - "splt": "", - "spltType": "str", - "arraySplt": 1, - "arraySpltType": "len", - "stream": false, - "addname": "", - "x": 170, - "y": 60, - "wires": [ - [ - "32e60ccc82f53d22" - ] - ] - }, - { - "id": "758d4bc10c8eb341", - "type": "join", - "z": "c86786afde87c8e5", - "name": "", - "mode": "custom", - "build": "array", - "property": "payload", - "propertyType": "msg", - "key": "topic", - "joiner": "\\n", - "joinerType": "str", - "accumulate": false, - "timeout": "", - "count": "", - "reduceRight": false, - "reduceExp": "", - "reduceInit": "", - "reduceInitType": "num", - "reduceFixup": "", - "x": 470, - "y": 60, - "wires": [ - [ - "70fd1b3003f70b0b" - ] - ] - }, - { - "id": "32e60ccc82f53d22", - "type": "function", - "z": "c86786afde87c8e5", - "name": "function", - "func": "msg.payload = {\n title: msg.parts.key,\n description: msg.payload[msg.payload.value],\n value_type: msg.payload.value\n}\n\nreturn msg\n", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 320, - "y": 60, - "wires": [ - [ - "758d4bc10c8eb341" - ] - ] - }, - { - "id": "70fd1b3003f70b0b", - "type": "sort", - "z": "c86786afde87c8e5", - "name": "", - "order": "ascending", - "as_num": false, - "target": "payload", - "targetType": "msg", - "msgKey": "$lowercase(title ? title : description)", - "msgKeyType": "jsonata", - "seqKey": "payload", - "seqKeyType": "msg", - "x": 610, - "y": 60, - "wires": [ - [] - ] - }, - { - "id": "9e9a00fa56add8cc", - "type": "split", - "z": "d2f09c088927a668", - "name": "", - "splt": "", - "spltType": "str", - "arraySplt": 1, - "arraySpltType": "len", - "stream": false, - "addname": "", - "x": 170, - "y": 60, - "wires": [ - [ - "d9e4a315e0b5c05a" - ] - ] - }, - { - "id": "57e5508b3f4682fd", - "type": "join", - "z": "d2f09c088927a668", - "name": "", - "mode": "custom", - "build": "array", - "property": "payload", - "propertyType": "msg", - "key": "topic", - "joiner": "\\n", - "joinerType": "str", - "accumulate": false, - "timeout": "", - "count": "", - "reduceRight": false, - "reduceExp": "", - "reduceInit": "", - "reduceInitType": "num", - "reduceFixup": "", - "x": 470, - "y": 60, - "wires": [ - [ - "87c9693a47913239" - ] - ] - }, - { - "id": "d9e4a315e0b5c05a", - "type": "function", - "z": "d2f09c088927a668", - "name": "function", - "func": "const tpl_na = 'not available'\nconst tpl_ud = 'unknown datapoint'\n\nlet value = msg.payload[msg.payload.value] \n\nif (value === \"NOT_AVAILABLE\") {\n value = tpl_na\n}\nelse if (value === \"UNKNOWN_DATAPOINT\") {\n value = tpl_ud\n}\n\nmsg.payload = {\n signal: msg.parts.key,\n value: value,\n}\n\nreturn msg\n\n// icon_name: msg.payload[msg.payload.value] === \"UNKNOWN_DATAPOINT\" ? \"error\" : \"\",\n// Type: msg.payload.value\n", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 320, - "y": 60, - "wires": [ - [ - "57e5508b3f4682fd" - ] - ] - }, - { - "id": "87c9693a47913239", - "type": "sort", - "z": "d2f09c088927a668", - "name": "", - "order": "ascending", - "as_num": false, - "target": "payload", - "targetType": "msg", - "msgKey": "$lowercase(signal)", - "msgKeyType": "jsonata", - "seqKey": "payload", - "seqKeyType": "msg", - "x": 610, - "y": 60, - "wires": [ - [] - ] - }, - { - "id": "8b0b5eab647b07c4", - "type": "grpc-call", - "z": "e6131ae387b9d6fc", - "name": "RequestDatapoints", - "server": "1f6dc92172f4e94b", - "service": "Broker", - "method": "GetDatapoints", - "chain": "", - "key": "", - "x": 450, - "y": 60, - "wires": [ - [ - "30cd6e5a1272bc18", - "b001a29cc88e7e5a", - "cceb670f279aa14d" - ] - ] - }, - { - "id": "192efc0ccd22a33c", - "type": "function", - "z": "e6131ae387b9d6fc", - "name": "GetDatapointsRequest", - "func": "let data = msg.payload\n\nif (typeof msg.payload === \"string\") {\n data = msg.payload.replace(/\\s+/g, ' ').split(\" \")\n}\n\nmsg.payload = {\n datapoints: data\n}\n\nreturn msg;\n", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 220, - "y": 60, - "wires": [ - [ - "8b0b5eab647b07c4" - ] - ] - }, - { - "id": "30cd6e5a1272bc18", - "type": "change", - "z": "e6131ae387b9d6fc", - "name": "datapoints", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "payload.datapoints", - "tot": "msg" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 650, - "y": 60, - "wires": [ - [] - ] - }, - { - "id": "a0d0890849b154e2", - "type": "change", - "z": "e6131ae387b9d6fc", - "name": "error", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "error", - "tot": "msg" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 770, - "y": 120, - "wires": [ - [] - ] - }, - { - "id": "cceb670f279aa14d", - "type": "function", - "z": "e6131ae387b9d6fc", - "name": "status", - "func": "const statusOk = { fill: \"green\", text: \"\" }\nconst statusError = { fill: \"red\", text: \"Error\" }\n\nmsg.payload = msg.error ? statusError : statusOk\nreturn msg\n", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 630, - "y": 180, - "wires": [ - [] - ] - }, - { - "id": "b001a29cc88e7e5a", - "type": "switch", - "z": "e6131ae387b9d6fc", - "name": "", - "property": "error", - "propertyType": "msg", - "rules": [ - { - "t": "nempty" - } - ], - "checkall": "true", - "repair": false, - "outputs": 1, - "x": 630, - "y": 120, - "wires": [ - [ - "a0d0890849b154e2" - ] - ] - }, - { - "id": "5043d8d345687e56", - "type": "grpc-call", - "z": "4f41580d2b2e81cf", - "name": "RequestSubscription", - "server": "1f6dc92172f4e94b", - "service": "Broker", - "method": "Subscribe", - "chain": "", - "key": "", - "x": 460, - "y": 60, - "wires": [ - [ - "ad83535f4d9e9305", - "ec790b0f84c62179", - "8fa8d5f99b982f8a" - ] - ] - }, - { - "id": "8cbaf8f7c506d7fe", - "type": "function", - "z": "4f41580d2b2e81cf", - "name": "SubscribeRequest", - "func": "msg.payload = {\n query: msg.payload\n}\n\nreturn msg", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 210, - "y": 60, - "wires": [ - [ - "5043d8d345687e56" - ] - ] - }, - { - "id": "87a5c171fc770428", - "type": "change", - "z": "4f41580d2b2e81cf", - "name": "fields", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "payload.fields", - "tot": "msg" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 790, - "y": 60, - "wires": [ - [] - ] - }, - { - "id": "3d9927502d12ba5d", - "type": "change", - "z": "4f41580d2b2e81cf", - "name": "error", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "error", - "tot": "msg" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 790, - "y": 120, - "wires": [ - [] - ] - }, - { - "id": "ec790b0f84c62179", - "type": "function", - "z": "4f41580d2b2e81cf", - "name": "status", - "func": "const statusOk = { fill: \"green\", text: \"\" }\nconst statusError = { fill: \"red\", text: \"Error\" }\n\nmsg.payload = msg.error ? statusError : statusOk\nreturn msg\n", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 650, - "y": 180, - "wires": [ - [] - ] - }, - { - "id": "ad83535f4d9e9305", - "type": "switch", - "z": "4f41580d2b2e81cf", - "name": "", - "property": "error", - "propertyType": "msg", - "rules": [ - { - "t": "nempty" - } - ], - "checkall": "true", - "repair": false, - "outputs": 1, - "x": 650, - "y": 120, - "wires": [ - [ - "3d9927502d12ba5d" - ] - ] - }, - { - "id": "8fa8d5f99b982f8a", - "type": "rbe", - "z": "4f41580d2b2e81cf", - "name": "", - "func": "rbe", - "gap": "", - "start": "", - "inout": "out", - "septopics": true, - "property": "payload", - "topi": "topic", - "x": 650, - "y": 60, - "wires": [ - [ - "87a5c171fc770428" - ] - ] - }, - { - "id": "fdb285d6f548d25e", - "type": "function", - "z": "f206c2b27c248139", - "name": "select signal from fields", - "func": "const statusOk = { fill: \"green\", text: \"\" }\nconst statusError = { fill: \"red\", text: \"Error\" }\n\nconst signal = env.get(\"signal\")\nconst field = msg.payload[signal]\n\nif (field) {\n\n let value = field[field.value]\n\n if (value !== \"NOT_AVAILABLE\" && value !== \"UNKNOWN_DATAPOINT\") {\n\n msg.payload = {\n signal: signal,\n value: value,\n timestamp: field.timestamp\n }\n\n return [msg, statusOk]\n }\n}\n\nreturn [null, statusError]\n", - "outputs": 2, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [], - "x": 230, - "y": 60, - "wires": [ - [], - [] - ] - }, - { - "id": "aa13d953ce65705f", - "type": "subflow:f486fb61943eeb89", - "z": "df00f52c34ea8f47", - "name": "", - "x": 480, - "y": 60, - "wires": [ - [ - "088f0f039337cb07" - ], - [ - "ab5e03f2bf0485b2" - ] - ] - }, - { - "id": "12b647de42ef96b0", - "type": "ui_text_input", - "z": "df00f52c34ea8f47", - "name": "", - "label": "Datapoint(s)", - "tooltip": "Enter one or more datapoints, separated by space", - "group": "0b2827de9e647c6b", - "order": 1, - "width": 0, - "height": 0, - "passthru": false, - "mode": "text", - "delay": "300", - "topic": "topic", - "sendOnBlur": true, - "className": "", - "topicType": "msg", - "x": 110, - "y": 60, - "wires": [ - [ - "8fd234bb13f9acd7" - ] - ] - }, - { - "id": "e207e7372d546859", - "type": "ui_table", - "z": "df00f52c34ea8f47", - "group": "0b2827de9e647c6b", - "name": "Results Table", - "order": 3, - "width": "12", - "height": "4", - "columns": [ - { - "field": "signal", - "title": "Signal", - "width": "", - "align": "left", - "formatter": "plaintext", - "formatterParams": { - "target": "_blank" - } - }, - { - "field": "value", - "title": "Value", - "width": "", - "align": "right", - "formatter": "html", - "formatterParams": { - "target": "_blank" - } - } - ], - "outputs": 0, - "cts": false, - "x": 900, - "y": 60, - "wires": [] - }, - { - "id": "088f0f039337cb07", - "type": "subflow:d2f09c088927a668", - "z": "df00f52c34ea8f47", - "name": "", - "x": 690, - "y": 60, - "wires": [ - [ - "e207e7372d546859" - ] - ] - }, - { - "id": "9acf3786ecb93f00", - "type": "ui_button", - "z": "df00f52c34ea8f47", - "name": "", - "group": "0b2827de9e647c6b", - "order": 2, - "width": 0, - "height": 0, - "passthru": false, - "label": "Query", - "tooltip": "", - "color": "", - "bgcolor": "", - "className": "", - "icon": "search", - "payload": "", - "payloadType": "str", - "topic": "topic", - "topicType": "msg", - "x": 90, - "y": 120, - "wires": [ - [ - "e56359a3b1f73be4" - ] - ] - }, - { - "id": "8fd234bb13f9acd7", - "type": "change", - "z": "df00f52c34ea8f47", - "name": "save query", - "rules": [ - { - "t": "set", - "p": "query_payload", - "pt": "flow", - "to": "payload", - "tot": "msg" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 290, - "y": 60, - "wires": [ - [ - "aa13d953ce65705f" - ] - ] - }, - { - "id": "e56359a3b1f73be4", - "type": "change", - "z": "df00f52c34ea8f47", - "name": "repeat query", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "query_payload", - "tot": "flow" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 290, - "y": 120, - "wires": [ - [ - "aa13d953ce65705f" - ] - ] - }, - { - "id": "ab5e03f2bf0485b2", - "type": "debug", - "z": "df00f52c34ea8f47", - "name": "databroker error", - "active": false, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 680, - "y": 120, - "wires": [] - }, - { - "id": "aa7e9b6ee7e2af8c", - "type": "subflow:4f41580d2b2e81cf", - "z": "df00f52c34ea8f47", - "name": "", - "x": 360, - "y": 260, - "wires": [ - [ - "147df8ed194c901d" - ], - [ - "ab5e03f2bf0485b2" - ] - ] - }, - { - "id": "27f000aa65bd8c17", - "type": "inject", - "z": "df00f52c34ea8f47", - "name": "", - "props": [ - { - "p": "payload" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "SELECT Vehicle.Speed", - "payloadType": "str", - "x": 140, - "y": 260, - "wires": [ - [ - "aa7e9b6ee7e2af8c" - ] - ] - }, - { - "id": "147df8ed194c901d", - "type": "subflow:f206c2b27c248139", - "z": "df00f52c34ea8f47", - "name": "Vehicle.Speed", - "env": [ - { - "name": "signal", - "value": "Vehicle.Speed", - "type": "str" - } - ], - "x": 550, - "y": 260, - "wires": [ - [ - "7f356b7f1549eb7d" - ] - ] - }, - { - "id": "ed41f34f4a925a32", - "type": "ui_gauge", - "z": "df00f52c34ea8f47", - "name": "", - "group": "ff74e0831e03da9e", - "order": 1, - "width": 0, - "height": 0, - "gtype": "gage", - "title": "Current Speed", - "label": "km/h", - "format": "{{value}}", - "min": 0, - "max": "250", - "colors": [ - "#00b500", - "#e6e600", - "#ca3838" - ], - "seg1": "", - "seg2": "", - "diff": false, - "className": "", - "x": 900, - "y": 260, - "wires": [] - }, - { - "id": "7f356b7f1549eb7d", - "type": "change", - "z": "df00f52c34ea8f47", - "name": "get value", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "$round(msg.payload.value)", - "tot": "jsonata" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 720, - "y": 260, - "wires": [ - [ - "ed41f34f4a925a32", - "30d4bab6ff8fdf01" - ] - ] - }, - { - "id": "30d4bab6ff8fdf01", - "type": "ui_chart", - "z": "df00f52c34ea8f47", - "name": "", - "group": "ff74e0831e03da9e", - "order": 2, - "width": 0, - "height": 0, - "label": "", - "chartType": "line", - "legend": "false", - "xformat": "HH:mm:ss", - "interpolate": "linear", - "nodata": "", - "dot": false, - "ymin": "", - "ymax": "", - "removeOlder": "10", - "removeOlderPoints": "", - "removeOlderUnit": "60", - "cutout": 0, - "useOneColor": false, - "useUTC": false, - "colors": [ - "#1f77b4", - "#aec7e8", - "#ff7f0e", - "#2ca02c", - "#98df8a", - "#d62728", - "#ff9896", - "#9467bd", - "#c5b0d5" - ], - "outputs": 1, - "useDifferentColor": false, - "className": "", - "x": 890, - "y": 320, - "wires": [ - [] - ] - }, - { - "id": "ee9b28a56f9b8b38", - "type": "ui_list", - "z": "bdd28665bfe7cc3e", - "group": "853f626a78b514a7", - "name": "Self Update Status", - "order": 2, - "width": "0", - "height": "0", - "lineType": "three", - "actionType": "none", - "allowHTML": true, - "outputs": 0, - "topic": "", - "x": 870, - "y": 60, - "wires": [] - }, - { - "id": "bdc66b589cb4230c", - "type": "switch", - "z": "bdd28665bfe7cc3e", - "name": "", - "property": "topic", - "propertyType": "msg", - "rules": [ - { - "t": "eq", - "v": "selfupdate/currentstate", - "vt": "str" - }, - { - "t": "eq", - "v": "containersupdate/currentstate", - "vt": "str" - }, - { - "t": "eq", - "v": "vehicleupdate/currentstate", - "vt": "str" - } - ], - "checkall": "true", - "repair": false, - "outputs": 3, - "x": 250, - "y": 60, - "wires": [ - [ - "37e9b18dafd41294" - ], - [ - "0b26a6aace74d46f" - ], - [ - "d8fbf20d86790ee4" - ] - ] - }, - { - "id": "37e9b18dafd41294", - "type": "change", - "z": "bdd28665bfe7cc3e", - "name": "softwareNodes", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "payload.payload.softwareNodes", - "tot": "msg", - "dc": true - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 460, - "y": 60, - "wires": [ - [ - "9d62962c43ae669b" - ] - ] - }, - { - "id": "0b26a6aace74d46f", - "type": "change", - "z": "bdd28665bfe7cc3e", - "name": "softwareNodes", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "payload.payload.softwareNodes", - "tot": "msg", - "dc": true - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 460, - "y": 120, - "wires": [ - [ - "20e819c71a01bd69" - ] - ] - }, - { - "id": "9d62962c43ae669b", - "type": "subflow:b1b3032b9f41f38c", - "z": "bdd28665bfe7cc3e", - "name": "", - "x": 670, - "y": 60, - "wires": [ - [ - "ee9b28a56f9b8b38" - ] - ] - }, - { - "id": "ec97b77461b25d15", - "type": "ui_list", - "z": "bdd28665bfe7cc3e", - "group": "a17b70ba881ce441", - "name": "Containers Update", - "order": 2, - "width": "0", - "height": "0", - "lineType": "three", - "actionType": "none", - "allowHTML": true, - "outputs": 0, - "topic": "", - "x": 870, - "y": 120, - "wires": [] - }, - { - "id": "20e819c71a01bd69", - "type": "subflow:b1b3032b9f41f38c", - "z": "bdd28665bfe7cc3e", - "name": "", - "x": 670, - "y": 120, - "wires": [ - [ - "ec97b77461b25d15" - ] - ] - }, - { - "id": "d8fbf20d86790ee4", - "type": "change", - "z": "bdd28665bfe7cc3e", - "name": "softwareNodes", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "payload.payload.softwareNodes", - "tot": "msg", - "dc": true - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 460, - "y": 180, - "wires": [ - [ - "ce7d39da84e78b9b" - ] - ] - }, - { - "id": "1c958cc219a01f7e", - "type": "ui_list", - "z": "bdd28665bfe7cc3e", - "group": "5d06f71eb2d78bfd", - "name": "Vehicle Update", - "order": 2, - "width": "0", - "height": "0", - "lineType": "three", - "actionType": "none", - "allowHTML": true, - "outputs": 0, - "topic": "", - "x": 860, - "y": 180, - "wires": [] - }, - { - "id": "ce7d39da84e78b9b", - "type": "subflow:b1b3032b9f41f38c", - "z": "bdd28665bfe7cc3e", - "name": "", - "x": 670, - "y": 180, - "wires": [ - [ - "1c958cc219a01f7e" - ] - ] - }, - { - "id": "0ed9e753be801df6", - "type": "ui_button", - "z": "bdd28665bfe7cc3e", - "name": "Get Self Update State", - "group": "853f626a78b514a7", - "order": 1, - "width": "0", - "height": "0", - "passthru": false, - "label": "Get current state", - "tooltip": "", - "color": "", - "bgcolor": "", - "className": "", - "icon": "update", - "payload": "{}", - "payloadType": "json", - "topic": "selfupdate/currentstate/get", - "topicType": "str", - "x": 140, - "y": 260, - "wires": [ - [ - "de937bc070e0acda" - ] - ] - }, - { - "id": "de937bc070e0acda", - "type": "function", - "z": "bdd28665bfe7cc3e", - "name": "build ua msg", - "func": "msg.payload = {\n activityId: uuid.v1(),\n timestamp: Date.now(),\n payload: msg.payload || {}\n}\nreturn msg;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "libs": [ - { - "var": "uuid", - "module": "uuid" - } - ], - "x": 450, - "y": 260, - "wires": [ - [ - "54347d4ec1cab856" - ] - ] - }, - { - "id": "a6f6ef030e3d147a", - "type": "ui_button", - "z": "bdd28665bfe7cc3e", - "name": "Get Containers Update State", - "group": "a17b70ba881ce441", - "order": 1, - "width": "0", - "height": "0", - "passthru": false, - "label": "Get current state", - "tooltip": "", - "color": "", - "bgcolor": "", - "className": "", - "icon": "update", - "payload": "{}", - "payloadType": "json", - "topic": "containersupdate/currentstate/get", - "topicType": "str", - "x": 160, - "y": 320, - "wires": [ - [ - "de937bc070e0acda" - ] - ] - }, - { - "id": "06b842f31c04e61b", - "type": "ui_button", - "z": "bdd28665bfe7cc3e", - "name": "Get Vehicle Update State", - "group": "5d06f71eb2d78bfd", - "order": 1, - "width": "0", - "height": "0", - "passthru": false, - "label": "Get current state", - "tooltip": "", - "color": "", - "bgcolor": "", - "className": "", - "icon": "update", - "payload": "{}", - "payloadType": "json", - "topic": "vehicleupdate/currentstate/get", - "topicType": "str", - "x": 150, - "y": 380, - "wires": [ - [ - "de937bc070e0acda" - ] - ] - }, - { - "id": "3661535da85e7507", - "type": "link in", - "z": "bdd28665bfe7cc3e", - "name": "MQTT in 1", - "links": [ - "18110494fbac6fc6" - ], - "x": 100, - "y": 60, - "wires": [ - [ - "bdc66b589cb4230c" - ] - ], - "l": true - }, - { - "id": "54347d4ec1cab856", - "type": "link out", - "z": "bdd28665bfe7cc3e", - "name": "MQTT out 1", - "mode": "link", - "links": [ - "19655fb9c907d1e2" - ], - "x": 850, - "y": 260, - "wires": [], - "l": true - }, - { - "id": "1877c8b9e075b7d8", - "type": "mqtt in", - "z": "8893ce2e3bcd6415", - "name": "leda-mqtt in", - "topic": "#", - "qos": "2", - "datatype": "auto-detect", - "broker": "fb585e7f0dd51cfd", - "nl": false, - "rap": true, - "rh": 0, - "inputs": 0, - "x": 110, - "y": 60, - "wires": [ - [ - "18110494fbac6fc6", - "cac87b1838e0d8f6" - ] - ] - }, - { - "id": "05a4bf55c32bc4e6", - "type": "mqtt out", - "z": "8893ce2e3bcd6415", - "name": "leda-mqtt out", - "topic": "", - "qos": "", - "retain": "", - "respTopic": "", - "contentType": "", - "userProps": "", - "correl": "", - "expiry": "", - "broker": "fb585e7f0dd51cfd", - "x": 290, - "y": 200, - "wires": [] - }, - { - "id": "19655fb9c907d1e2", - "type": "link in", - "z": "8893ce2e3bcd6415", - "name": "MQTT Out", - "links": [ - "54347d4ec1cab856" - ], - "x": 55, - "y": 200, - "wires": [ - [ - "05a4bf55c32bc4e6" - ] - ] - }, - { - "id": "18110494fbac6fc6", - "type": "link out", - "z": "8893ce2e3bcd6415", - "name": "MQTT in", - "mode": "link", - "links": [ - "3661535da85e7507", - "81f02c69f246394f" - ], - "x": 235, - "y": 120, - "wires": [] - }, - { - "id": "cac87b1838e0d8f6", - "type": "debug", - "z": "8893ce2e3bcd6415", - "name": "mqtt payload", - "active": false, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 290, - "y": 60, - "wires": [] - } -] \ No newline at end of file +/******************************************************************************** + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License 2.0 which is available at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +syntax = "proto3"; + +package sdv.databroker.v1; + +import "google/protobuf/timestamp.proto"; + +// Data type of a signal +// +// Protobuf doesn't support int8, int16, uint8 or uint16. +// These are mapped to sint32 and uint32 respectively. +// +enum DataType { + STRING = 0; + BOOL = 1; + INT8 = 2; + INT16 = 3; + INT32 = 4; + INT64 = 5; + UINT8 = 6; + UINT16 = 7; + UINT32 = 8; + UINT64 = 9; + FLOAT = 10; + DOUBLE = 11; + TIMESTAMP = 12; + STRING_ARRAY = 20; + BOOL_ARRAY = 21; + INT8_ARRAY = 22; + INT16_ARRAY = 23; + INT32_ARRAY = 24; + INT64_ARRAY = 25; + UINT8_ARRAY = 26; + UINT16_ARRAY = 27; + UINT32_ARRAY = 28; + UINT64_ARRAY = 29; + FLOAT_ARRAY = 30; + DOUBLE_ARRAY = 31; + TIMESTAMP_ARRAY = 32; +} + +enum DatapointError { + UNKNOWN_DATAPOINT = 0; + INVALID_TYPE = 1; + ACCESS_DENIED = 2; + INTERNAL_ERROR = 3; + OUT_OF_BOUNDS = 4; +} + +enum EntryType { + ENTRY_TYPE_UNSPECIFIED = 0; + ENTRY_TYPE_SENSOR = 1; + ENTRY_TYPE_ACTUATOR = 2; + ENTRY_TYPE_ATTRIBUTE = 3; +} + +enum ChangeType { + STATIC = 0; // Value never changes + ON_CHANGE = 1; // Updates are provided every time the value changes (i.e. + // window is open / closed) + CONTINUOUS = 2; // Value is updated continuously. Broker needs to tell + // provider the preferred (update) frequency. +} + +message StringArray { + repeated string values = 1; +} + +message BoolArray { + repeated bool values = 1; +} + +message Int32Array { + repeated sint32 values = 1; +} + +message Int64Array { + repeated sint64 values = 1; +} + +message Uint32Array { + repeated uint32 values = 1; +} + +message Uint64Array { + repeated uint64 values = 1; +} + +message FloatArray { + repeated float values = 1; +} + +message DoubleArray { + repeated double values = 1; +} + +message Datapoint { + // Timestamp of the value + google.protobuf.Timestamp timestamp = 1; + + // values + oneof value { + Failure failure_value = 10; + string string_value = 11; + bool bool_value = 12; + sint32 int32_value = 13; + sint64 int64_value = 14; + uint32 uint32_value = 15; + uint64 uint64_value = 16; + float float_value = 17; + double double_value = 18; + StringArray string_array = 21; + BoolArray bool_array = 22; + Int32Array int32_array = 23; + Int64Array int64_array = 24; + Uint32Array uint32_array = 25; + Uint64Array uint64_array = 26; + FloatArray float_array = 27; + DoubleArray double_array = 28; + } + + enum Failure { + // The data point is known, but doesn't have a valid value + INVALID_VALUE = 0; + // The data point is known, but no value is available + NOT_AVAILABLE = 1; + // Unknown datapoint + UNKNOWN_DATAPOINT = 2; + // Access denied + ACCESS_DENIED = 3; + // Unexpected internal error + INTERNAL_ERROR = 4; + } +} + +message Metadata { + // Id to be used in "get" and "subscribe" requests. Ids stay valid during + // one power cycle, only. + int32 id = 1; + EntryType entry_type = 2; + string name = 4; + DataType data_type = 5; + ChangeType change_type = 6; // CONTINUOUS or STATIC or ON_CHANGE + string description = 7; + + // int32 min_update_hz = 10; // Only for CONTINUOUS + // int32 max_update_hz = 11; // Only for CONTINUOUS +} + +service Broker { + // Request a set of datapoints (values) + // + // Returns a list of requested data points. + // + // InvalidArgument is returned if the request is malformed. + rpc GetDatapoints(GetDatapointsRequest) returns (GetDatapointsReply); + + // Set a datapoint (values) + rpc SetDatapoints(SetDatapointsRequest) returns (SetDatapointsReply); + + // Subscribe to a set of data points or conditional expressions + // using the Data Broker Query Syntax (described in QUERY.md) + // + // Returns a stream of replies. + // + // InvalidArgument is returned if the request is malformed. + rpc Subscribe(SubscribeRequest) returns (stream SubscribeReply); + + // Request the metadata of a set of datapoints + // + // Returns metadata of the requested data points that exist. + rpc GetMetadata(GetMetadataRequest) returns (GetMetadataReply); +} + +message GetDatapointsRequest { + // A list of requested data points. + repeated string datapoints = 1; +} + +message GetDatapointsReply { + // Contains the values of the requested data points. + // If a requested data point is not available, the corresponding Datapoint + // will have the respective failure value set. + map datapoints = 1; +} + +message SetDatapointsRequest { + // A map of data points to set + map datapoints = 1; +} + +message SetDatapointsReply { + // A map of errors (if any) + map errors = 1; +} + +message SubscribeRequest { + // Subscribe to a set of data points (or expressions) described + // by the provided query. + // The query syntax is a subset of SQL and is described in more + // detail in the QUERY.md file. + string query = 2; +} + +message SubscribeReply { + // Contains the fields specified by the query. + // If a requested data point value is not available, the corresponding + // Datapoint will have it's respective failure value set. + map fields = 1; +} + +message GetMetadataRequest { + // Request metadata for a list of data points referenced by their names. + // e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed". + // + // If no names are provided, metadata for all known data points will be + // returned. + repeated string names = 1; +} + +message GetMetadataReply { + // Contains metadata of the requested data points. If a data point + // doesn't exist (i.e. not known to the Data Broker) the corresponding + // Metadata isn't part of the returned list. + repeated Metadata list = 1; +} + +service Collector { + // Register new datapoint (metadata) + // + // If the registration of at least one of the passed data point fails, the overall registration + // is rejected and the gRPC status code ABORTED is returned (to indicate the "aborted" registration). + // The details, which data point(s) caused the failure and the reason, is passed in back in human- + // readable form in the status message. Possible failure resaons are: + // * PERMISSION_DENIED - Not allowed to register this name + // * ALREADY_REGISTERED - The data point is already registered by some other feeder + // * RE_REGISTRATION_MISMATCH - Already registered by this feeder but with differing metadata + // * INVALID_NAME - The passed name of the datapoint has an invalid structure + // * INVALID_VALUE_TYPE - The passed ValueType is not supported + // * INVALID_CHANGE_TYPE - The passed ChangeType is not supported + rpc RegisterDatapoints(RegisterDatapointsRequest) returns (RegisterDatapointsReply); + + // Provide a set of updated datapoint values to the broker. + // This is the unary equivalent of `StreamDatapoints` below and is better suited for cases + // where the frequency of updates is rather low. + // + // NOTE: The values provided in a single request are handled as a single update in the + // data broker. This ensures that any clients requesting (or subscribing to) a set of + // datapoints will get a consistent update, i.e. that either all values are updated or + // none are. + // + // Returns: any errors encountered updating the datapoints + // + rpc UpdateDatapoints(UpdateDatapointsRequest) returns (UpdateDatapointsReply); + + // Provide a stream with updated datapoint values to the broker. + // This is the streaming equivalent of `UpdateDatapoints` above and is better suited for + // cases where the frequency of updates is high. + // + // NOTE: The values provided in a single request are handled as a single update in the + // data broker. This ensures that any clients requesting (or subscribing to) a set of + // datapoints will get a consistent update, i.e. that either all values are updated or + // none are. + // + // Returns: any errors encountered updating the datapoints + // + rpc StreamDatapoints(stream StreamDatapointsRequest) returns (stream StreamDatapointsReply); +} + +message UpdateDatapointsRequest { + map datapoints = 1; +} + +message UpdateDatapointsReply { + map errors = 1; // If empty, everything went well +} + +message StreamDatapointsRequest { + map datapoints = 1; +} + +message StreamDatapointsReply { + map errors = 1; // If empty, everything went well +} + +message RegisterDatapointsRequest { + repeated RegistrationMetadata list = 1; +} + +message RegistrationMetadata { + // Name of the data point + // (e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed") + string name = 1; + DataType data_type = 2; + string description = 3; + ChangeType change_type = 4; + + // int32 min_update_hz = 10; // Only for CONTINUOUS + // int32 max_update_hz = 11; // Only for CONTINUOUS +}; + +message RegisterDatapointsReply { + // Maps each data point name passed in RegisterDatapointsRequest to a data point id + map results = 1; +} diff --git a/node-red/sdv_databroker_v1_broker.proto b/node-red/sdv_databroker_v1_broker.proto new file mode 100644 index 0000000..a015f87 --- /dev/null +++ b/node-red/sdv_databroker_v1_broker.proto @@ -0,0 +1,238 @@ +/******************************************************************************** + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License 2.0 which is available at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +syntax = "proto3"; + +package sdv.databroker.v1; + +import "google/protobuf/timestamp.proto"; + +// Data type of a signal +// +// Protobuf doesn't support int8, int16, uint8 or uint16. +// These are mapped to sint32 and uint32 respectively. +// +enum DataType { + STRING = 0; + BOOL = 1; + INT8 = 2; + INT16 = 3; + INT32 = 4; + INT64 = 5; + UINT8 = 6; + UINT16 = 7; + UINT32 = 8; + UINT64 = 9; + FLOAT = 10; + DOUBLE = 11; + TIMESTAMP = 12; + STRING_ARRAY = 20; + BOOL_ARRAY = 21; + INT8_ARRAY = 22; + INT16_ARRAY = 23; + INT32_ARRAY = 24; + INT64_ARRAY = 25; + UINT8_ARRAY = 26; + UINT16_ARRAY = 27; + UINT32_ARRAY = 28; + UINT64_ARRAY = 29; + FLOAT_ARRAY = 30; + DOUBLE_ARRAY = 31; + TIMESTAMP_ARRAY = 32; +} + +enum DatapointError { + UNKNOWN_DATAPOINT = 0; + INVALID_TYPE = 1; + ACCESS_DENIED = 2; + INTERNAL_ERROR = 3; + OUT_OF_BOUNDS = 4; +} + +enum EntryType { + ENTRY_TYPE_UNSPECIFIED = 0; + ENTRY_TYPE_SENSOR = 1; + ENTRY_TYPE_ACTUATOR = 2; + ENTRY_TYPE_ATTRIBUTE = 3; +} + +enum ChangeType { + STATIC = 0; // Value never changes + ON_CHANGE = 1; // Updates are provided every time the value changes (i.e. + // window is open / closed) + CONTINUOUS = 2; // Value is updated continuously. Broker needs to tell + // provider the preferred (update) frequency. +} + +message StringArray { + repeated string values = 1; +} + +message BoolArray { + repeated bool values = 1; +} + +message Int32Array { + repeated sint32 values = 1; +} + +message Int64Array { + repeated sint64 values = 1; +} + +message Uint32Array { + repeated uint32 values = 1; +} + +message Uint64Array { + repeated uint64 values = 1; +} + +message FloatArray { + repeated float values = 1; +} + +message DoubleArray { + repeated double values = 1; +} + +message Datapoint { + // Timestamp of the value + google.protobuf.Timestamp timestamp = 1; + + // values + oneof value { + Failure failure_value = 10; + string string_value = 11; + bool bool_value = 12; + sint32 int32_value = 13; + sint64 int64_value = 14; + uint32 uint32_value = 15; + uint64 uint64_value = 16; + float float_value = 17; + double double_value = 18; + StringArray string_array = 21; + BoolArray bool_array = 22; + Int32Array int32_array = 23; + Int64Array int64_array = 24; + Uint32Array uint32_array = 25; + Uint64Array uint64_array = 26; + FloatArray float_array = 27; + DoubleArray double_array = 28; + } + + enum Failure { + // The data point is known, but doesn't have a valid value + INVALID_VALUE = 0; + // The data point is known, but no value is available + NOT_AVAILABLE = 1; + // Unknown datapoint + UNKNOWN_DATAPOINT = 2; + // Access denied + ACCESS_DENIED = 3; + // Unexpected internal error + INTERNAL_ERROR = 4; + } +} + +message Metadata { + // Id to be used in "get" and "subscribe" requests. Ids stay valid during + // one power cycle, only. + int32 id = 1; + EntryType entry_type = 2; + string name = 4; + DataType data_type = 5; + ChangeType change_type = 6; // CONTINUOUS or STATIC or ON_CHANGE + string description = 7; + + // int32 min_update_hz = 10; // Only for CONTINUOUS + // int32 max_update_hz = 11; // Only for CONTINUOUS +} + +service Broker { + // Request a set of datapoints (values) + // + // Returns a list of requested data points. + // + // InvalidArgument is returned if the request is malformed. + rpc GetDatapoints(GetDatapointsRequest) returns (GetDatapointsReply); + + // Set a datapoint (values) + rpc SetDatapoints(SetDatapointsRequest) returns (SetDatapointsReply); + + // Subscribe to a set of data points or conditional expressions + // using the Data Broker Query Syntax (described in QUERY.md) + // + // Returns a stream of replies. + // + // InvalidArgument is returned if the request is malformed. + rpc Subscribe(SubscribeRequest) returns (stream SubscribeReply); + + // Request the metadata of a set of datapoints + // + // Returns metadata of the requested data points that exist. + rpc GetMetadata(GetMetadataRequest) returns (GetMetadataReply); +} + +message GetDatapointsRequest { + // A list of requested data points. + repeated string datapoints = 1; +} + +message GetDatapointsReply { + // Contains the values of the requested data points. + // If a requested data point is not available, the corresponding Datapoint + // will have the respective failure value set. + map datapoints = 1; +} + +message SetDatapointsRequest { + // A map of data points to set + map datapoints = 1; +} + +message SetDatapointsReply { + // A map of errors (if any) + map errors = 1; +} + +message SubscribeRequest { + // Subscribe to a set of data points (or expressions) described + // by the provided query. + // The query syntax is a subset of SQL and is described in more + // detail in the QUERY.md file. + string query = 2; +} + +message SubscribeReply { + // Contains the fields specified by the query. + // If a requested data point value is not available, the corresponding + // Datapoint will have it's respective failure value set. + map fields = 1; +} + +message GetMetadataRequest { + // Request metadata for a list of data points referenced by their names. + // e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed". + // + // If no names are provided, metadata for all known data points will be + // returned. + repeated string names = 1; +} + +message GetMetadataReply { + // Contains metadata of the requested data points. If a data point + // doesn't exist (i.e. not known to the Data Broker) the corresponding + // Metadata isn't part of the returned list. + repeated Metadata list = 1; +} diff --git a/node-red/sdv_databroker_v1_collector.proto b/node-red/sdv_databroker_v1_collector.proto new file mode 100644 index 0000000..03f3323 --- /dev/null +++ b/node-red/sdv_databroker_v1_collector.proto @@ -0,0 +1,237 @@ +/******************************************************************************** + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License 2.0 which is available at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +syntax = "proto3"; + +package sdv.databroker.v1; + +import "google/protobuf/timestamp.proto"; + +// Data type of a signal +// +// Protobuf doesn't support int8, int16, uint8 or uint16. +// These are mapped to sint32 and uint32 respectively. +// +enum DataType { + STRING = 0; + BOOL = 1; + INT8 = 2; + INT16 = 3; + INT32 = 4; + INT64 = 5; + UINT8 = 6; + UINT16 = 7; + UINT32 = 8; + UINT64 = 9; + FLOAT = 10; + DOUBLE = 11; + STRING_ARRAY = 20; + BOOL_ARRAY = 21; + INT8_ARRAY = 22; + INT16_ARRAY = 23; + INT32_ARRAY = 24; + INT64_ARRAY = 25; + UINT8_ARRAY = 26; + UINT16_ARRAY = 27; + UINT32_ARRAY = 28; + UINT64_ARRAY = 29; + FLOAT_ARRAY = 30; + DOUBLE_ARRAY = 31; +} + +enum DatapointError { + UNKNOWN_DATAPOINT = 0; + INVALID_TYPE = 1; + ACCESS_DENIED = 2; + INTERNAL_ERROR = 3; + OUT_OF_BOUNDS = 4; +} + +enum EntryType { + ENTRY_TYPE_UNSPECIFIED = 0; + ENTRY_TYPE_SENSOR = 1; + ENTRY_TYPE_ACTUATOR = 2; + ENTRY_TYPE_ATTRIBUTE = 3; +} + +enum ChangeType { + STATIC = 0; // Value never changes + ON_CHANGE = 1; // Updates are provided every time the value changes (i.e. + // window is open / closed) + CONTINUOUS = 2; // Value is updated continuously. Broker needs to tell + // provider the preferred (update) frequency. +} + +message StringArray { + repeated string values = 1; +} + +message BoolArray { + repeated bool values = 1; +} + +message Int32Array { + repeated sint32 values = 1; +} + +message Int64Array { + repeated sint64 values = 1; +} + +message Uint32Array { + repeated uint32 values = 1; +} + +message Uint64Array { + repeated uint64 values = 1; +} + +message FloatArray { + repeated float values = 1; +} + +message DoubleArray { + repeated double values = 1; +} + +message Datapoint { + // Timestamp of the value + google.protobuf.Timestamp timestamp = 1; + + // values + oneof value { + Failure failure_value = 10; + string string_value = 11; + bool bool_value = 12; + sint32 int32_value = 13; + sint64 int64_value = 14; + uint32 uint32_value = 15; + uint64 uint64_value = 16; + float float_value = 17; + double double_value = 18; + StringArray string_array = 21; + BoolArray bool_array = 22; + Int32Array int32_array = 23; + Int64Array int64_array = 24; + Uint32Array uint32_array = 25; + Uint64Array uint64_array = 26; + FloatArray float_array = 27; + DoubleArray double_array = 28; + } + + enum Failure { + // The data point is known, but doesn't have a valid value + INVALID_VALUE = 0; + // The data point is known, but no value is available + NOT_AVAILABLE = 1; + // Unknown datapoint + UNKNOWN_DATAPOINT = 2; + // Access denied + ACCESS_DENIED = 3; + // Unexpected internal error + INTERNAL_ERROR = 4; + } +} + +message Metadata { + // Id to be used in "get" and "subscribe" requests. Ids stay valid during + // one power cycle, only. + int32 id = 1; + EntryType entry_type = 2; + string name = 4; + DataType data_type = 5; + ChangeType change_type = 6; // CONTINUOUS or STATIC or ON_CHANGE + string description = 7; + + // int32 min_update_hz = 10; // Only for CONTINUOUS + // int32 max_update_hz = 11; // Only for CONTINUOUS +} + +service Collector { + // Register new datapoint (metadata) + // + // If the registration of at least one of the passed data point fails, the overall registration + // is rejected and the gRPC status code ABORTED is returned (to indicate the "aborted" registration). + // The details, which data point(s) caused the failure and the reason, is passed in back in human- + // readable form in the status message. Possible failure resaons are: + // * PERMISSION_DENIED - Not allowed to register this name + // * ALREADY_REGISTERED - The data point is already registered by some other feeder + // * RE_REGISTRATION_MISMATCH - Already registered by this feeder but with differing metadata + // * INVALID_NAME - The passed name of the datapoint has an invalid structure + // * INVALID_VALUE_TYPE - The passed ValueType is not supported + // * INVALID_CHANGE_TYPE - The passed ChangeType is not supported + rpc RegisterDatapoints(RegisterDatapointsRequest) returns (RegisterDatapointsReply); + + // Provide a set of updated datapoint values to the broker. + // This is the unary equivalent of `StreamDatapoints` below and is better suited for cases + // where the frequency of updates is rather low. + // + // NOTE: The values provided in a single request are handled as a single update in the + // data broker. This ensures that any clients requesting (or subscribing to) a set of + // datapoints will get a consistent update, i.e. that either all values are updated or + // none are. + // + // Returns: any errors encountered updating the datapoints + // + rpc UpdateDatapoints(UpdateDatapointsRequest) returns (UpdateDatapointsReply); + + // Provide a stream with updated datapoint values to the broker. + // This is the streaming equivalent of `UpdateDatapoints` above and is better suited for + // cases where the frequency of updates is high. + // + // NOTE: The values provided in a single request are handled as a single update in the + // data broker. This ensures that any clients requesting (or subscribing to) a set of + // datapoints will get a consistent update, i.e. that either all values are updated or + // none are. + // + // Returns: any errors encountered updating the datapoints + // + rpc StreamDatapoints(stream StreamDatapointsRequest) returns (stream StreamDatapointsReply); +} + +message UpdateDatapointsRequest { + map datapoints = 1; +} + +message UpdateDatapointsReply { + map errors = 1; // If empty, everything went well +} + +message StreamDatapointsRequest { + map datapoints = 1; +} + +message StreamDatapointsReply { + map errors = 1; // If empty, everything went well +} + +message RegisterDatapointsRequest { + repeated RegistrationMetadata list = 1; +} + +message RegistrationMetadata { + // Name of the data point + // (e.g. "Vehicle.Cabin.Seat.Row1.Pos1.Position" or "Vehicle.Speed") + string name = 1; + DataType data_type = 2; + string description = 3; + ChangeType change_type = 4; + + // int32 min_update_hz = 10; // Only for CONTINUOUS + // int32 max_update_hz = 11; // Only for CONTINUOUS +}; + +message RegisterDatapointsReply { + // Maps each data point name passed in RegisterDatapointsRequest to a data point id + map results = 1; +}