Beispiel #1
0
        private void CharacteristicEditorControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (NC == null)
            {
                return;
            }
            var vps = ValueParserSplit.ParseLine(NC.Type);

            if (vps.Count != 1)
            {
                return;                 // if there are multiple values, give up and let the user struggle with HEX
            }
            DisplayFormat          = vps[0].DisplayFormatPrimary;
            DisplayFormatSecondary = vps[0].Get(1, 1);

            foreach (var item in uiConvertType.Items)
            {
                var tag = (item as ComboBoxItem).Tag as string;
                if (tag == DisplayFormat)
                {
                    uiConvertType.SelectedItem = item;
                }
            }

            if (DisplayFormat == "ASCII" && DisplayFormatSecondary == "LONG")
            {
                var h = uiEditBox.ActualHeight;
                uiEditBox.MinHeight     = h * 3;
                uiEditBox.MinWidth      = 300;
                uiEditBox.AcceptsReturn = true;
            }
        }
        public static IList <ValueParserSplit> ParseLine(string value)
        {
            var Retval = new List <ValueParserSplit>();
            var split  = value.Split(Space);

            for (int i = 0; i < split.Length; i++)
            {
                var item = new ValueParserSplit(split[i]);
                Retval.Add(item);
            }
            return(Retval);
        }
Beispiel #3
0
        private void AddButtons()
        {
            // e.g. the WilliamWeillerEngineering Skoobot has "buttons" -- there are enums
            // defined for specific commands like forward and reverse. When buttonType is set to
            // "standard" then we add buttons for those specific commands.
            //OTHERWISE we just have the string command to send.
            if (NC?.UI?.buttonType != "standard")
            {
                return;
            }
            uiButtons.Children.Clear();

            // What's the name of the value?
            var parse = ValueParserSplit.ParseLine(NC.Type);

            if (parse.Count != 1)
            {
                return;                   // We can only do buttons when there is only one choice.
            }
            var value1 = parse[0];

            if (value1.ByteFormatPrimary != "U8")
            {
                return;                                   // can only handle enums for now
            }
            var name = value1.NamePrimary;

            // Get the corresponding enums
            if (!NC.EnumValues.ContainsKey(name))
            {
                return;                                   // no enums means no buttons.
            }
            var enums  = NC.EnumValues[name];
            var margin = new Thickness(2);

            foreach (var(enumName, enumValue) in enums)
            {
                var b = new Button()
                {
                    Content  = enumName,
                    Tag      = enumValue,
                    MinWidth = 120,
                    Margin   = margin,
                };
                b.Click += OnEnum_Click;
                uiButtons.Children.Add(b);
                if (enumName.Length >= 15)
                {
                    // Spill to two columns
                    VariableSizedWrapGrid.SetColumnSpan(b, 2);
                }
            }
        }
Beispiel #4
0
        // Sets up all of the flags, values, etc.
        private async Task SetupAsync(NameDevice device, GattDeviceService service, GattCharacteristic characteristic)
        {
            Service        = service;
            Characteristic = characteristic;
            NC             = BleNames.Get(device, service, characteristic);

#if NEVER_EVER_DEFINED
            // Setting it up differently now via the UILIst + uiUI
            Commands.Clear();
            foreach (var(_, command) in NC.Commands)
            {
                command.WriteCharacteristic = this;
                Commands.Add(command);
            }
#endif

            var props = characteristic.CharacteristicProperties;
            uiNotifyFlag.Visibility                     = props.HasFlag(GattCharacteristicProperties.Notify) ? Visibility.Visible : Visibility.Collapsed;
            uiNotifyOnFlag.Visibility                   = Visibility.Collapsed;
            uiReadFlag.Visibility                       = props.HasFlag(GattCharacteristicProperties.Read) ? Visibility.Visible : Visibility.Collapsed;
            uiWriteFlag.Visibility                      = props.HasFlag(GattCharacteristicProperties.WriteWithoutResponse) ? Visibility.Visible : Visibility.Collapsed;
            uiIncrementWriteFlag.Visibility             = uiWriteFlag.Visibility;
            uiWriteWithResponseFlag.Visibility          = props.HasFlag(GattCharacteristicProperties.Write) ? Visibility.Visible : Visibility.Collapsed;
            uiIncrementWriteWithResponseFlag.Visibility = uiWriteWithResponseFlag.Visibility;
            uiIndicateFlag.Visibility                   = props.HasFlag(GattCharacteristicProperties.Indicate) ? Visibility.Visible : Visibility.Collapsed;
            uiIndicateOnFlag.Visibility                 = Visibility.Collapsed;

            // The less common flags
            uiBroadcastFlag.Visibility = props.HasFlag(GattCharacteristicProperties.Broadcast) ? Visibility.Visible : Visibility.Collapsed;
            uiAuthenticatedSignedWritesFlag.Visibility = props.HasFlag(GattCharacteristicProperties.AuthenticatedSignedWrites) ? Visibility.Visible : Visibility.Collapsed;
            uiExtendedPropertiesFlag.Visibility        = props.HasFlag(GattCharacteristicProperties.ExtendedProperties) ? Visibility.Visible : Visibility.Collapsed;
            uiReliableWritesFlag.Visibility            = props.HasFlag(GattCharacteristicProperties.ReliableWrites) ? Visibility.Visible : Visibility.Collapsed;
            uiWritableAuxilariesFlag.Visibility        = props.HasFlag(GattCharacteristicProperties.WritableAuxiliaries) ? Visibility.Visible : Visibility.Collapsed;

            // Set the little edit marker
            uiEditFlag.Visibility = (props.HasFlag(GattCharacteristicProperties.Write) ||
                                     props.HasFlag(GattCharacteristicProperties.WriteWithoutResponse))
                 ? Visibility.Visible : Visibility.Collapsed;

            await DoReadAsync(true); // update the value field even if it's not readable

            var namestr = "";
            if (!String.IsNullOrEmpty(NC?.Name))
            {
                namestr = NC.Name;
            }
            else
            {
                var uidname = service.Uuid.ToString().GetCommon(characteristic.Uuid.ToString());
                if (uidname.Length < 8)
                {
                    namestr = uidname;
                    if (namestr == "ffa2")
                    {
                        ; // something handy to put a debugger on!
                    }
                    if (namestr == "ffa3")
                    {
                        ; // a handy line to put a debugger on!
                    }
                    if (!String.IsNullOrWhiteSpace(characteristic.UserDescription))
                    {
                        namestr += " " + characteristic.UserDescription;
                    }
                    else
                    {
                        ;
                    }
                }
            }



            // Either we have a good name (yay!) or we use the alternate route of using the
            // uuid info area.
            if (namestr != "")
            {
                // Don't need the UUID area or the line break
                uiName.Text       = namestr;
                uiInfo.Visibility = Visibility.Collapsed;
            }
            else
            {
                var infostr = "";
                if (!string.IsNullOrWhiteSpace(NC?.Name))
                {
                    infostr = NC.Name + " ";
                }
                infostr          += characteristic.Uuid.ToString();
                uiInfo.Text       = infostr;
                uiInfo.Visibility = Visibility.Visible;
            }

            // If there's a readable value with displayFormatPrimary=ASCII and secondary of LONG (ASCII^LONG)
            // then make the text area multi-line
            var vps = ValueParserSplit.ParseLine(NC?.Type ?? "");
            if (vps.Count == 1)
            {
                var displayFormat          = vps[0].DisplayFormatPrimary;
                var displayFormatSecondary = vps[0].Get(1, 1);
                switch (displayFormat)
                {
                case "ASCII":
                    switch (displayFormatSecondary)
                    {
                    case "LONG":
                        var h = uiValueShow.ActualHeight;
                        uiValueShowScroll.MinHeight = h * 3;
                        uiValueShowScroll.MaxHeight = h * 7;
                        CurrValueShowMethod         = ValueShowMethod.Append;
                        break;
                    }
                    break;
                }
            }
            try
            {
                SetupUI();
            }
            catch (Exception)
            {
                ;
            }

            if (NC != null && NC.Commands != null)
            {
                foreach (var(_, command) in NC.Commands)
                {
                    command.InitVariables();
                    command.WriteCharacteristic = this;
                }
            }
        }
        private static ValueParserResult ConvertHelper(DataReader dr, string decodeCommands)
        {
            var  str        = "";
            var  vps        = ValueParserSplit.ParseLine(decodeCommands);
            var  valueList  = new BCBasic.BCValueList();
            bool isOptional = false;

            for (int i = 0; i < vps.Count; i++)
            {
                var stritem = "";

                var command                = vps[i];
                var readcmd                = command.ByteFormatPrimary;
                var readindicator          = readcmd[0];
                var displayFormat          = command.DisplayFormatPrimary;
                var displayFormatSecondary = command.Get(1, 1);

                var name = command.NamePrimary;
                if (string.IsNullOrEmpty(name))
                {
                    name = $"param{i}";
                }
                var units = command.UnitsPrimary;

                var    resultState = ResultState.IsDouble; // the most common result
                double dvalue      = double.NaN;
                try
                {
                    switch (readindicator)
                    {
                    case 'F':     // FLOAT
                        if (dr.UnconsumedBufferLength == 0)
                        {
                            if (isOptional)
                            {
                                dvalue  = double.NaN;
                                stritem = "";
                                break;
                            }
                            else
                            {
                                return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i+1}"));
                            }
                        }
                        switch (readcmd)
                        {
                        case "F32":
                        {
                            dvalue = dr.ReadSingle();
                            switch (displayFormat)
                            {
                            case "":
                            case "FIXED":
                                displayFormat = (displayFormatSecondary == "") ? "N3" : displayFormatSecondary;
                                break;

                            case "DEC":
                                displayFormat = (displayFormatSecondary == "") ? "N0" : displayFormatSecondary;
                                break;

                            case "HEX":
                                return(ValueParserResult.CreateError(decodeCommands, $"Float displayFormat unrecognized; should be FIXED {displayFormat}"));
                            }
                            stritem = dvalue.ToString(displayFormat);             // e.g. N3 for 3 digits
                        }
                        break;

                        case "F64":
                        {
                            dvalue = dr.ReadDouble();
                            switch (displayFormat)
                            {
                            case "":
                            case "FIXED":
                                displayFormat = (displayFormatSecondary == "") ? "N3" : displayFormatSecondary;
                                break;

                            case "DEC":
                                displayFormat = (displayFormatSecondary == "") ? "N0" : displayFormatSecondary;
                                break;

                            case "HEX":
                                return(ValueParserResult.CreateError(decodeCommands, $"Float displayFormat unrecognized; should be FIXED {displayFormat}"));
                            }
                            stritem = dvalue.ToString(displayFormat);             // e.g. N3 for 3 digits
                        }
                        break;

                        default:
                            return(ValueParserResult.CreateError(decodeCommands, $"Float command unrecognized; should be F32 or F64 not {readcmd}"));
                        }
                        break;

                    case 'I':
                        if (dr.UnconsumedBufferLength == 0)
                        {
                            if (isOptional)
                            {
                                dvalue  = double.NaN;
                                stritem = "";
                                break;
                            }
                            else
                            {
                                return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i + 1}"));
                            }
                        }
                        switch (readcmd)
                        {
                        case "I8":
                        case "I16":
                        case "I24":
                        case "I32":
                        {
                            string floatFormat = "F2";
                            string intFormat   = "X6";
                            switch (readcmd)
                            {
                            case "I8":
                            {
                                var value = (sbyte)dr.ReadByte();
                                dvalue = (double)value;
                            }
                            break;

                            case "I16":
                            {
                                var value = dr.ReadInt16();
                                dvalue = (double)value;
                            }
                            break;

                            case "I24":
                            {
                                var b0    = dr.ReadByte();
                                var b1    = dr.ReadByte();
                                var b2    = dr.ReadByte();
                                var msb   = (sbyte)(dr.ByteOrder == ByteOrder.BigEndian ? b0 : b2);
                                var lsb   = dr.ByteOrder == ByteOrder.BigEndian ? b2 : b0;
                                int value = (int)(msb << 16) + (b1 << 8) + (lsb);
                                dvalue = (double)value;
                            }
                            break;

                            case "I32":
                            {
                                var value = dr.ReadInt32();
                                dvalue    = (double)value;
                                intFormat = "X8";
                            }
                            break;
                            }
                            string calculateCommand = command.Get(0, 1);             // e.g. for I24^100_/ for TI 1350 barometer values
                            if (!string.IsNullOrEmpty(calculateCommand))
                            {
                                dvalue = ValueCalculate.Calculate(calculateCommand, dvalue).D;
                                if (double.IsNaN(dvalue))
                                {
                                    return(ValueParserResult.CreateError(decodeCommands, $"Calculation failed for {calculateCommand} in {readcmd}"));
                                }
                                else
                                {
                                    // Everything worked and got a value
                                    stritem = DoubleToString(dvalue, displayFormat, displayFormatSecondary);
                                    if (stritem == null)
                                    {
                                        return(ValueParserResult.CreateError(decodeCommands, $"Integer display format command unrecognized; should be FIXED or HEX or DEC not {displayFormat} in {readcmd}"));
                                    }
                                }
                            }
                            else
                            {
                                if (displayFormat == "")
                                {
                                    displayFormat = "HEX";
                                }
                                stritem = DoubleToString(dvalue, displayFormat, displayFormatSecondary, floatFormat, intFormat);
                                if (stritem == null)
                                {
                                    return(ValueParserResult.CreateError(decodeCommands, $"Integer display format command unrecognized; should be FIXED or HEX or DEC not {displayFormat} in {readcmd}"));
                                }
                            }
                        }
                        break;

                        default:
                            return(ValueParserResult.CreateError(decodeCommands, $"Integer command unrecognized; should be I8/16/24/32 not {readcmd}"));
                        }
                        break;

                    case 'O':
                        switch (readcmd)
                        {
                        case "OEB":
                            resultState  = ResultState.NoResult;
                            dr.ByteOrder = ByteOrder.LittleEndian;
                            break;

                        case "OEL":
                            resultState  = ResultState.NoResult;
                            dr.ByteOrder = ByteOrder.LittleEndian;
                            break;

                        case "OOPT":
                            isOptional = true;
                            break;

                        default:
                            return(ValueParserResult.CreateError(decodeCommands, $"Optional command unrecognized; should be OEL or OEB not {readcmd}"));
                        }
                        break;

                    case 'Q':
                        if (dr.UnconsumedBufferLength == 0)
                        {
                            if (isOptional)
                            {
                                dvalue  = double.NaN;
                                stritem = "";
                                break;
                            }
                            else
                            {
                                return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i + 1}"));
                            }
                        }
                        // e.g. Q12Q4.Fixed
                        {
                            var subtypes = readcmd.Split(new char[] { 'Q' });
                            if (subtypes.Length != 3)     // Actually 2, but first is blank
                            {
                                return(ValueParserResult.CreateError(decodeCommands, $"Q command unrecognized; wrong number of Qs {readcmd}"));
                            }
                            stritem = FixedQuotientHelper(dr, subtypes[1], subtypes[2], displayFormat, out dvalue);
                            //NOTE: fail on failure
                        }
                        break;

                    case 'U':
                        if (dr.UnconsumedBufferLength == 0)
                        {
                            if (isOptional)
                            {
                                dvalue  = double.NaN;
                                stritem = "";
                                break;
                            }
                            else
                            {
                                return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i + 1}"));
                            }
                        }
                        switch (readcmd)
                        {
                        case "U8":
                        case "U16":
                        case "U24":
                        case "U32":
                            string xfmt = "X2";
                            switch (readcmd)
                            {
                            case "U8":
                            {
                                var value = dr.ReadByte();
                                dvalue = (double)value;
                                xfmt   = "X2";
                            }
                            break;

                            case "U16":
                            {
                                var value = dr.ReadUInt16();
                                dvalue = (double)value;
                                xfmt   = "X4";
                            }
                            break;

                            case "U24":
                            {
                                var b0    = dr.ReadByte();
                                var b1    = dr.ReadByte();
                                var b2    = dr.ReadByte();
                                var msb   = (byte)(dr.ByteOrder == ByteOrder.BigEndian ? b0 : b2);
                                var lsb   = dr.ByteOrder == ByteOrder.BigEndian ? b2 : b0;
                                int value = (int)(msb << 16) + (b1 << 8) + (lsb);
                                dvalue = (double)value;
                            }
                            break;

                            case "U32":
                            {
                                var value = dr.ReadUInt32();
                                dvalue = (double)value;
                                xfmt   = "X8";
                            }
                            break;
                            }
                            string calculateCommand = command.Get(0, 1);         // e.g. for I24^100_/ for TI 1350 barometer values
                            if (!string.IsNullOrEmpty(calculateCommand))
                            {
                                dvalue = ValueCalculate.Calculate(calculateCommand, dvalue).D;
                                if (double.IsNaN(dvalue))
                                {
                                    return(ValueParserResult.CreateError(decodeCommands, $"Calculation failed for {calculateCommand} in {readcmd}"));
                                }
                                else
                                {
                                    stritem = DoubleToString(dvalue, displayFormat, displayFormatSecondary);
                                    if (stritem == null)
                                    {
                                        return(ValueParserResult.CreateError(decodeCommands, $"Integer display format command unrecognized; should be FIXED or HEX or DEC not {displayFormat} in {readcmd}"));
                                    }
                                }
                            }
                            else
                            {
                                if (displayFormat == "")
                                {
                                    displayFormat = "HEX";
                                }
                                stritem = DoubleToString(dvalue, displayFormat, displayFormatSecondary, "F2", xfmt);
                                if (stritem == null)
                                {
                                    return(ValueParserResult.CreateError(decodeCommands, $"Integer display format command unrecognized;\nshould be FIXED or HEX or DEC not {displayFormat} in {readcmd}"));
                                }
                            }
                            break;

                        default:
                            return(ValueParserResult.CreateError(decodeCommands, $"UInteger command unrecognized;\nshould be U8/U16/U24/U32 not {readcmd}"));
                        }
                        break;

                    case '/':
                        // e.g. /U8/I16|Fixed
                        if (dr.UnconsumedBufferLength == 0)
                        {
                            if (isOptional)
                            {
                                dvalue  = double.NaN;
                                stritem = "";
                                break;
                            }
                            else
                            {
                                return(ValueParserResult.CreateError(decodeCommands, $"Missing data with {readcmd} field {i + 1}"));
                            }
                        }
                        {
                            var subtypes = readcmd.Split(new char[] { '/' });
                            if (subtypes.Length != 3)     // Actually 2, but first is blank
                            {
                                return(ValueParserResult.CreateError(decodeCommands, $"/ command unrecognized; wrong number of slashes {readcmd}"));
                            }
                            stritem = FixedFractionHelper(dr, subtypes[1], subtypes[2], displayFormat, out dvalue);
                            // NOTE: fail on failure
                        }
                        break;

                    default:
                        if (readcmd != readcmd.ToUpper())
                        {
                            System.Diagnostics.Debug.WriteLine("ERROR: readcmd {readcmd} but should be uppercase");
                        }
                        switch (readcmd.ToUpper())     //NOTE: should be all-uppercase; any lowercase is wrong
                        {
                        case "STRING":
                        {
                            try
                            {
                                stritem = dr.ReadString(dr.UnconsumedBufferLength);
                                switch (displayFormat)
                                {
                                case "":
                                case "ASCII":
                                    stritem = EscapeString(stritem, displayFormatSecondary);
                                    break;

                                case "Eddystone":
                                    stritem = BluetoothDefinitionLanguage.Eddystone.EddystoneUrlToString(stritem);
                                    break;

                                default:
                                    return(ValueParserResult.CreateError(decodeCommands, $"Unknown string format type {displayFormat}"));
                                }
                            }
                            catch (Exception)
                            {
                                // The string can't be read. Let's try reading as a buffer instead.
                                IBuffer buffer = dr.ReadBuffer(dr.UnconsumedBufferLength);
                                for (uint ii = 0; ii < buffer.Length; ii++)
                                {
                                    if (ii != 0)
                                    {
                                        stritem += " ";
                                    }
                                    stritem += buffer.GetByte(ii).ToString("X2");
                                }
                            }
                            resultState = ResultState.IsString;
                        }
                        break;

                        case "BYTES":
                        {
                            IBuffer buffer = dr.ReadBuffer(dr.UnconsumedBufferLength);
                            for (uint ii = 0; ii < buffer.Length; ii++)
                            {
                                if (ii != 0)
                                {
                                    stritem += " ";
                                }
                                stritem += buffer.GetByte(ii).ToString("X2");
                            }
                            resultState = ResultState.IsString;
                        }
                        break;

                        default:
                            return(ValueParserResult.CreateError(decodeCommands, $"Other command unrecognized; should be String or Bytes {readcmd}"));
                        }
                        break;
                    }
                }
                catch (Exception e)
                {
                    stritem = $"EXCEPTION reading data {e} index {i} command {command} len {dr.UnconsumedBufferLength}";
                    return(ValueParserResult.CreateError(str + stritem, stritem));
                }
                switch (resultState)
                {
                case ResultState.IsDouble:
                    valueList.SetProperty(name, new BCBasic.BCValue(dvalue));
                    break;

                case ResultState.IsString:
                    valueList.SetProperty(name, new BCBasic.BCValue(stritem));
                    break;
                }

                if (str != "")
                {
                    str += " ";
                }
                str += stritem;

                if (dr.UnconsumedBufferLength <= 0)
                {
                    break;
                }
            }
            return(ValueParserResult.CreateOk(str, valueList));
        }