Пример #1
0
        private void CheckForValidityAndThrowIfSomethingIsWrong(ClientPropertyMetadata creationOptions)
        {
            var errorList   = new ArrayList();
            var warningList = new ArrayList();

            var isMetadataOk = creationOptions.ValidateAndFix(ref errorList, ref warningList);

            if (isMetadataOk == false)
            {
                var errorMessage = $"Provided metadata is incorrect. Errors: ";
                foreach (var problem in errorList)
                {
                    errorMessage += problem + " ";
                }
                throw new ArgumentException(errorMessage);
            }
        }
Пример #2
0
        private static bool TryParseProperties(ref ArrayList unparsedTopicList, ref ArrayList parsedTopicList, string baseTopic, ref ClientDeviceMetadata candidateDevice, ref ArrayList errorList, ref ArrayList warningList)
        {
            var isParseSuccessful = false;

            for (var n = 0; n < candidateDevice.Nodes.Length; n++)
            {
                var candidatePropertyIds = ((string)candidateDevice.Nodes[n].AllAttributes["$properties"]).Split(',');
                var goodProperties       = new ArrayList();

                for (var p = 0; p < candidatePropertyIds.Length; p++)
                {
                    var candidateProperty = new ClientPropertyMetadata()
                    {
                        NodeId = candidateDevice.Nodes[n].Id, PropertyId = candidatePropertyIds[p]
                    };

                    // Parsing property attributes and value.
                    var attributeRegex = new Regex($@"^({baseTopic})\/({candidateDevice.Id})\/({candidateDevice.Nodes[n].Id})\/({candidateProperty.PropertyId})(\/\$[a-z0-9][a-z0-9-]+)?(:)(.+)$");
                    var setRegex       = new Regex($@"^({baseTopic})\/({candidateDevice.Id})\/({candidateDevice.Nodes[n].Id})\/({candidateProperty.PropertyId})(\/set)(:)(.+)$");
                    var valueRegex     = new Regex($@"^({baseTopic})\/({candidateDevice.Id})\/({candidateDevice.Nodes[n].Id})\/({candidateProperty.PropertyId})()(:)(.+)$");

                    var isSettable         = false;
                    var isRetained         = false;
                    var isNameReceived     = false;
                    var isDataTypeReceived = false;
                    var isSettableReceived = false;
                    var isRetainedReceived = false;

                    foreach (string inputString in unparsedTopicList)
                    {
                        var attributeMatch = attributeRegex.Match(inputString);
                        if (attributeMatch.Success)
                        {
                            var key   = attributeMatch.Groups[5].Value;
                            var value = attributeMatch.Groups[7].Value;

                            if (key == "/$name")
                            {
                                candidateProperty.Name = value;
                                isNameReceived         = true;
                            }
                            if (key == "/$datatype")
                            {
                                if (Helpers.TryParseHomieDataType(value, out var parsedType))
                                {
                                    candidateProperty.DataType = parsedType;
                                    isDataTypeReceived         = true;
                                }
                                ;
                            }
                            if (key == "/$format")
                            {
                                candidateProperty.Format = value;
                            }
                            if (key == "/$settable")
                            {
                                if (Helpers.TryParseBool(value, out isSettable))
                                {
                                    isSettableReceived = true;
                                }
                            }
                            if (key == "/$retained")
                            {
                                if (Helpers.TryParseBool(value, out isRetained))
                                {
                                    isRetainedReceived = true;
                                }
                                ;
                            }

                            if (key == "/$unit")
                            {
                                candidateProperty.Unit = value;
                            }

                            parsedTopicList.Add(inputString);
                        }

                        var setMatch = setRegex.Match(inputString);
                        if (setMatch.Success)
                        {
                            // Discarding this one. This is a historically cached command, and these should not execute during initialization. Besides, it shouldn't be retained,
                            // so the fact that we're here means something is wrong with the host side.
                            warningList.Add($"{candidateDevice.Id}/{candidateProperty} has /set topic assigned, which means /set message is published as retained. This is against Homie convention.");
                        }

                        var valueMatch = valueRegex.Match(inputString);
                        if (valueMatch.Success)
                        {
                            var value = attributeMatch.Groups[7].Value;
                            candidateProperty.InitialValue = value;
                        }
                    }

                    foreach (var parsedTopic in parsedTopicList)
                    {
                        unparsedTopicList.Remove(parsedTopic);
                    }

                    // Basic data extraction is done. Now we'll validate if values of the fields are compatible with each other and also YAHI itself.
                    var isOk = isNameReceived & isDataTypeReceived & isSettableReceived & isRetainedReceived;
                    if (isOk)
                    {
                        // Setting property type.
                        if ((isSettable == false) && (isRetained == true))
                        {
                            candidateProperty.PropertyType = PropertyType.State;
                        }
                        if ((isSettable == true) && (isRetained == false))
                        {
                            candidateProperty.PropertyType = PropertyType.Command;
                        }
                        if ((isSettable == true) && (isRetained == true))
                        {
                            candidateProperty.PropertyType = PropertyType.Parameter;
                        }
                        if ((isSettable == false) && (isRetained == false))
                        {
                            errorList.Add($"{candidateDevice.Id}/{candidateProperty.NodeId}/{candidateProperty.PropertyId} has all mandatory fields set, but this retainability and settability configuration is not supported by YAHI. Skipping this property entirely.");
                            isOk = false;
                        }
                    }
                    else
                    {
                        // Some of the mandatory topic were not received. Can't let this property through.
                        errorList.Add($"{candidateDevice.Id}/{candidateProperty.NodeId}/{candidateProperty.PropertyId} is defined, but mandatory attributes are missing. Skipping this property entirely.");
                        isOk = false;
                    }

                    // Validating by property data type, because rules are very different for each of those.
                    if (isOk)
                    {
                        var tempErrorList   = new ArrayList();
                        var tempWarningList = new ArrayList();

                        // ValidateAndFix method does not know anythin about device it is parsing, thus doing some wrapping around error and warning lists (because I want device info in those).
                        isOk = candidateProperty.ValidateAndFix(ref tempErrorList, ref tempWarningList);

                        foreach (var error in tempErrorList)
                        {
                            errorList.Add($"{candidateDevice.Id}/{error}");
                        }
                        foreach (var warning in tempWarningList)
                        {
                            warningList.Add($"{candidateDevice.Id}/{warning}");
                        }
                    }

                    if (isOk)
                    {
                        goodProperties.Add(candidateProperty);
                    }
                }

                // Converting local temporary property lists to final arrays.
                candidateDevice.Nodes[n].Properties = new ClientPropertyMetadata[goodProperties.Count];
                for (var i = 0; i < goodProperties.Count; i++)
                {
                    candidateDevice.Nodes[n].Properties[i] = (ClientPropertyMetadata)goodProperties[i];
                }
            }

            // Converting local temporary node lists to final arrays and returning.
            foreach (var node in candidateDevice.Nodes)
            {
                if (node.Properties.Length > 0)
                {
                    isParseSuccessful = true;
                }
            }

            return(isParseSuccessful);
        }