SemlerPDX icon

AVCS Voice Calculator & Unit Conversions (v1.1) Inline Function in VB.net for VoiceAttack

SemlerPDX | PRO | 02/11/22 04:27:38 PM UTC (Edited) | 0 ⭐ | 1760 👁️ | Never ⏰ | []
VB.NET |

31.7 KB

|

None

|

0 👍

/

0 👎

'AVCS Voice Calculator & Conversions (v1.1) -- Calculate expressions with up to 2 operations and 3 values "Any way you say it"
'by SemlerPDX Mar2020/Mar2021
'VETERANS-GAMING.COM
 
'demonstration video on youtube:  https://youtu.be/bCjuYh92M-I
 
'When I say: *[divided by;divide by;divided that;divide that;divided;divide;multiply;times;multiplied by;multiply that;multiplied that;times that;add;add that;added that;plus;subtract;subtracted;minus;% of;to the power of;percent of;swear;square;square root of;square of;squared;cubed;cube;cuban;° c;° f;kelvin;celcius;celsius;centigrade;fahrenheit;kelvin;calvin;cm;centimeters;centimetres;inches;foot;feet;meters;metres;yards;yours;miles;km;kilometers;kilometres;feet per second;mph;miles per hour;kph;kilometers per hour;kilometres per hour;knots;mach;mock;mark]*;*[-1;-2;-3;-4;-5;-6;-7;-8;-9]*
 
'Set text [~avcs_cmd] to '{TXTWORDTONUM:"{CMD}"}' (Trim) (Lower Case)
' (and the next action is to run this inline, after that, just says the result with TTS)
 
Imports Microsoft.VisualBasic
Imports System
Imports System.Linq
Imports System.Math
Imports System.Globalization
 
Public Class VAInline
    dim decimalSeparator as string = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator
    dim decimalPlaces as Int32 = 3   'Default = 3   |  rounds results -- change via voice command 'Set Decimal Places [to;] [0..16]'
    
    'Operational Keyword Arrays
    dim OperatorsArray() as string = {
        "divided",
        "divide",
        "times",
        "multiplied",
        "multiply",
        "add",
        "plus",
        "subtract",
        "subtracted",
        "minus",
        "-",
        "%",
        "percent",
        "power",
        "square",
        "squared",
        "squareroot",
        "cubed",
        "cubes",
        "cube",
        "cuban",
        "cuba"
    }
    dim ConvertorsArray() as string = {
        "c",
        "f",
        "k",
        "cm",
        "celsius",
        "celcius",
        "centigrade",
        "fahrenheit",
        "kelvin",
        "calvin",
        "inches",
        "centimeters",
        "centimetres",
        "foot",
        "feet",
        "m",
        "meters",
        "metres",
        "yards",
        "yours",
        "miles",
        "km",
        "kilometers",
        "kilometres",
        "fps",
        "feetpersecond",
        "mps",
        "meterspersecond",
        "metrespersecond",
        "mph",
        "milesperhour",
        "kph",
        "kilometersperhour",
        "kilometresperhour",
        "knots",
        "mach",
        "mark",
        "mock"
    }
    dim OpsArray() as string = {
        "sum",
        "sub",
        "prd",
        "dif",
        "exp",
        "per",
        "sqr"
    }
    dim DoubleOpsArray() as string = {
        "sum",
        "sub",
        "prd",
        "dif"
    }
    dim SymbolsArray() as string = {
        "°",
        "%",
        "^",
        "-",
        ":",
        "$",
        "£",
        "€",
        "¢",
        "¥",
        "ƒ"
    }
    dim PiArray() as string = {
        "pi",
        "pie",
        "tie",
        "high",
        "bye",
        "eye",
        "espy",
        "pine",
        "pied",
        "ply",
        "tie"
    }
    
    'Speech Parsing Vars
    dim cmd as string = ""
    dim charArray() as char
    dim cmdSegments() as string
    'Values Decimal Vars
    dim operand1 as decimal?
    dim operand2 as decimal?
    dim operand3 as decimal?
    dim operandLast as decimal?
    dim result as decimal?
    dim resultLast as decimal?
    'Operations String Vars
    dim operator1 as string
    dim operator2 as string
    dim operatorText1 as string = ""
    dim operatorText2 as string = ""
    dim equationText as string = ""
    'Calc Systems Vars
    dim operationsOrder as integer = 0
    dim countHistory as integer = 1
    dim countFaults as integer = 0
    dim randomText as string
    dim subReverse as boolean
    dim calcActive as boolean
    
    
    'Function to remove spaces from keywords, handle homophones & edge case words
    Private Function ReplaceEdgeCaseWords(ByRef cmd as string)
        if (cmd.StartsWith("ad "))
            cmd = cmd.Replace("ad ","add ")
        end if
        
        cmd = cmd.Replace(
                " too "," to "
            ).Replace(
                "swear ","square "
            ).Replace(
                "square root","squareroot"
            ).Replace(
                "to the power","power"
            ).Replace(
                "to value"," value"
            ).Replace(
                "2 value"," value"
            ).Replace(
                "value ","value"
            ).Replace(
                "to that"," that"
            ).Replace(
                "2 that"," that"
            ).Replace(
                " tell them"," kelvin"
            ).Replace(
                " per ","per"
            ).Replace(
                " ad "," add "
            ).Replace(
                "added to","add "
            ).Replace(
                "added 2","add "
            ).Replace(
                "added ","add "
            ).Replace(
                "add that 2","add that "
            ).Replace(
                "add that to","add that "
            ).Replace(
                "÷"," divided by "
            ).Replace(
                "¼","0.25"
            ).Replace(
                "½","0.5"
            ).Replace(
                "¾","0.75"
        )
    end function
    
    
    'Function to fix large number separators by culture
    Private Function FixLargeNumberSeparator(ByRef cmd as string)
        'ex. (7,000.32 into 7000.32)  [or]  (7.000,32 into 7000,32)
        if ((cmd.Contains(".")) or (cmd.Contains(",")))
            if (decimalSeparator <> ".")
                if (cmd.Contains("."))
                    cmd = cmd.Replace(".","")
                end if
            elseif (decimalSeparator <> ",")
                if (cmd.Contains(","))
                    cmd = cmd.Replace(",","")
                end if
            end if
        end if
    end function
    
    
    'Function to transform large number words and concat with the number said just before
    Private Function FixLargeWordNum(ByRef largeType as string, ByRef lastNum as decimal?)
        select case largeType
            case "million"
                lastNum *= 1000000
            case "billion"
                lastNum *= 1000000000
            case "trillion"
                lastNum *= 1000000000000
            case "quadrillion"
                lastNum *= 1000000000000000
            case else
                largeType = "AVCS Calculation Error - unable to resolve '" + lastNum.ToString() + " " + largeType + "' as an opperand"
        end select
    end function
    
    
    'Function to test for "by" as Pi in specific cases
    Private Function HasPi(ByVal operatorName as string)
        if (not((operatorName.Contains("root")) or (operatorName.StartsWith("square")) or (operatorName.StartsWith("cub"))))
            cmd = cmd.Replace("   "," ").Replace("  "," ").Replace("divided by","").Replace("divide by","").Replace("multiplied by","").Replace("multiply by","").Replace("that by","")
            if ((cmd.StartsWith("by ")) or (cmd.Contains(" by ")) or (cmd.EndsWith(" by")))
                Return true
            end if
        end if
    end function
    
    
    'Function to set keyword and readable text/symbol for Operators
    Private Function SetOperator(ByRef operatorName as string, ByRef operatorText as string)
        if ((operatorName.Contains("%")) or (operatorName.Contains("percent")))
            operatorText = "% of"
            operatorName = "per"
        elseif (operatorName.StartsWith("divide"))
            operatorText = "/"
            operatorName = "dif"
        elseif ((operatorName.Contains("times")) or (operatorName.StartsWith("multipl")))
            operatorText = "x"
            operatorName = "prd"
        elseif ((operatorName.Contains("plus")) or (operatorName.StartsWith("add")))
            operatorText = "+"
            operatorName = "sum"
        elseif ((operatorName.StartsWith("subtract")) or (operatorName.Contains("minus")) or (operatorName.Contains("-")))
            operatorText = "-"
            operatorName = "sub"
        elseif (operatorName.Contains("power"))
            operatorText = "ⁿ" 'to the power of
            operatorName = "exp"
        elseif (operatorName.Contains("squareroot"))
            operatorText = "√" 'the square root of
            operatorName = "sqr"
        elseif (operatorName.StartsWith("square"))
            operatorText = "²" 'squared
            operatorName = "exp"
        elseif (operatorName.StartsWith("cub"))
            operatorText = "³" 'cubed
            operatorName = "exp"
        end if
    end function
    
    
    'Function to set keyword and readable text/symbol for Conversions
    Private Function SetConvertor(ByRef operatorName as string, ByRef operatorText as string)
        'VELOCITY CONVERSIONS==============
        if ((operatorName.Contains("fps")) or (operatorName.Contains("feetpersecond")))
            operatorText = "feet per second"
            operatorName = "fps2"
        elseif ((operatorName.Contains("mps")) or (operatorName.Contains("meterspersecond")) or (operatorName.Contains("metrespersecond")))
            operatorText = "meters per second"
            operatorName = "mps2"
        elseif ((operatorName.Contains("mph")) or (operatorName.Contains("milesperhour")))
            operatorText = "miles per hour"
            operatorName = "mph2"
        elseif ((operatorName.Contains("kph")) or (operatorName.Contains("kilometersperhour")) or (operatorName.Contains("kilometresperhour")))
            operatorText = "kilometers per hour"
            operatorName = "kph2"
        elseif (operatorName.Contains("knots"))
            operatorText = "knots"
            operatorName = "kts2"
        elseif ((operatorName.Contains("mach")) or (operatorName.Contains("mock")) or (operatorName.Contains("mark")))
            operatorText = "mach"
            operatorName = "mch2"
        'DISTANCE CONVERSIONS==============
        elseif (operatorName.Contains("inches"))
            operatorText = "inches"
            operatorName = "in2"
        elseif ((operatorName.Contains("cm")) or (operatorName.Contains("centimeters")) or (operatorName.Contains("centimetres")))
            operatorText = "centimeters"
            operatorName = "cm2"
        elseif ((operatorName.Contains("feet")) or (operatorName.Contains("foot")))
            operatorText = "feet"
            operatorName = "ft2"
        elseif ((operatorName.Contains("yards")) or (operatorName.Contains("yours")))
            operatorText = "yards"
            operatorName = "yd2"
        elseif ((operatorName.Contains("km")) or (operatorName.Contains("kilometers")) or (operatorName.Contains("kilometres")))
            operatorText = "kilometers"
            operatorName = "km2"
        elseif ((operatorName.Contains("mi")) or (operatorName.Contains("miles")))
            operatorText = "miles"
            operatorName = "mi2"
        elseif ((operatorName.Contains("meters")) or (operatorName.Contains("metres")))
            operatorText = "meters"
            operatorName = "mt2"
        'TEMPERATURE CONVERSIONS==============
        elseif (operatorName.Contains("fahrenheit"))
            operatorText = "degrees fahrenheit"
            operatorName = "f2"
        elseif ((operatorName.Contains("celcius")) or (operatorName.Contains("celsius")))
            operatorText = "degrees celcius"
            operatorName = "c2"
        elseif (operatorName.Contains("centigrade"))
            operatorText = "degrees centigrade"
            operatorName = "c2"
        elseif ((operatorName.Contains("kelvin")) or (operatorName.Contains("calvin")))
            operatorText = "kelvin"
            operatorName = "k2"
        end if
    end function
    
    
    'Main Maths Function
    Private Function GetResult(ByVal ops as string, ByVal val1 as decimal?, Optional ByVal val2 as decimal? = 0)
        select case ops
            case "sum"
                val1 += val2
            case "sub"
                val1 -= val2
            case "prd"
                val1 *=  val2
            case "dif"
                val1 /= val2
            case "per"
                val1 *= (val2 / 100)
            case "exp"
                val1 = Pow(val1, val2)
            case "sqr"
                val1 = Sqrt(val1)
            case else
                val1 = nothing
        end select
        Return val1
    end function
    
    
    'Main Conversions Function
    Private Function GetConversion(ByVal ops as string, ByVal val as decimal?)
        select case ops
            '==DISTANCE===========
            'inches
            case "in2cm"
                val *= 0.393701
            case "in2ft"
                val *= 0.0833333
            case "in2mt"
                val *= 0.0254
            case "in2yd"
                val *= 0.0277778
            case "in2km"
                val /= 39370
            case "in2mi"
                val /= 63360
            'cm
            case "cm2in"
                val *= 0.393701
            case "cm2ft"
                val *= 0.0328084
            case "cm2mt"
                val /= 100
            case "cm2yd"
                val *= 0.0109361
            case "cm2km"
                val /= 100000
            case "cm2mi"
                val /= 160934
            'ft
            case "ft2in"
                val *= 12
            case "ft2cm"
                val *= 30.48
            case "ft2mt"
                val *= 0.3048
            case "ft2yd"
                val *= 0.333333
            case "ft2km"
                val /= 3280.84
            case "ft2mi"
                val /= 5280
            'mt
            case "mt2in"
                val *= 39.3701
            case "mt2cm"
                val *= 100
            case "mt2ft"
                val *= 3.28084
            case "mt2yd"
                val *= 1.09361
            case "mt2km"
                val /= 1000
            case "mt2mi"
                val /= 1609
            'yd
            case "yd2in"
                val *= 36
            case "yd2cm"
                val *= 91.44
            case "yd2ft"
                val *= 3
            case "yd2mt"
                val *= 0.9144
            case "yd2km"
                val /= 1093.61
            case "yd2mi"
                val /= 1760
            'km
            case "km2in"
                val *= 39370.1
            case "km2cm"
                val *= 100000
            case "km2ft"
                val *= 3280.84
            case "km2mt"
                val *= 1000
            case "km2yd"
                val *= 1093.61
            case "km2mi"
                val *= 0.621371
            'miles
            case "mi2in"
                val *= 63360
            case "mi2cm"
                val *= 160934.4
            case "mi2ft"
                val *= 5280
            case "mi2mt"
                val *= 1609.34
            case "mi2yd"
                val *= 1760
            case "mi2km"
                val *= 1.60934
            '==VELOCITY===========
            'fps
            case "fps2mps"
                val *= 0.3048
            case "fps2kph"
                val *= 1.09728
            case "fps2mph"
                val *= 0.681818
            case "fps2kts"
                val *= 0.592484
            case "fps2mch"
                val *= 0.00088863
            'mps
            case "mps2fps"
                val *= 3.28084
            case "mps2kph"
                val *= 3.6
            case "mps2mph"
                val *= 2.23694
            case "mps2kts"
                val *= 1.94384
            case "mps2mch"
                val *= 0.00291545
            'kph
            case "kph2fps"
                val *= 0.911344
            case "kph2mps"
                val *= 0.277778
            case "kph2mph"
                val *= 0.6214
            case "kph2kts"
                val *= 1.852
            case "kph2mch"
                val *= 1234.8
            'mph
            case "mph2fps"
                val *= 1.46667
            case "mph2mps"
                val *= 0.44704
            case "mph2kph"
                val *= 1.60934
            case "mph2kts"
                val *= 0.868976
            case "mph2mch"
                val *= 0.00130332
            'knots
            case "kts2fps"
                val *= 1.68781
            case "kts2mps"
                val *= 0.514444
            case "kts2mph"
                val *= 1.15078
            case "kts2kph"
                val *= 1.852
            case "kts2mch"
                val *= 0.0015
            'mach
            case "mch2fps"
                val *= 1125.33
            case "mch2mps"
                val *= 343
            case "mch2mph"
                val *= 767.269
            case "mch2kph"
                val *= 1234.8
            case "mch2kts"
                val *= 666.73866
            '==TEMPERATURE===========
            'f
            case "f2c"
                val = ((val - 32) * 5) / 9
            case "f2k"
                val = ((val + 459.67) * 5) / 9
            'c
            case "c2f"
                val = ((val * 9) / 5) + 32
            case "c2k"
                val = ((val * 9) / 5) + 32
                val = ((val + 459.67) * 5) / 9
            'kelvin
            case "k2f"
                val = ((val * 9) / 5) - 459.67
            case "k2c"
                val = ((val * 9) / 5) - 459.67
                val = ((val - 32) * 5) / 9
            case else
                val = nothing
        end select
        Return val
    end function
    
    
    Public Sub Main()
        VA.SetText("avcs_calc_return", nothing)
        
        'Debugging and Testing Check (bypassed if called via spoken command)
        dim cmdTest as string = vaProxy.Utility.ParseTokens("{CMD}")
        if (cmdTest = "")
            VA.SetText("~avcs_cmd", "by the way what is 3 plus 3 times by")
            VA.SetInt("AVCS_CALC_ResultsDecimalPlaces", 2)
        else
            'Check Calc Mode state, exit if off
            if ((VA.GetBoolean("AVCS_CALC_MODE_ON")) isNot nothing)
                calcActive = VA.GetBoolean("AVCS_CALC_MODE_ON")
            end if
            
            'Provide Message for wildcard recognition when Calculator Mode OFF (infrequent prompt in case user does not know)
            if (not(calcActive))
                if ((VA.GetInt("AVCS_CALC_FAULT")) isNot nothing)
                    countFaults = VA.GetInt("AVCS_CALC_FAULT") + 1
                    if (countFaults >= 3)
                        if (countFaults >= 15)
                            VA.WriteToLog("Calculation recognized, but won't be computed  (this is common when calculator is OFF)", "purple")
                            VA.SetInt("AVCS_CALC_FAULT", nothing)
                            countFaults = 0
                        end if
                    else
                        randomText = "Calculator is OFF.  To enable, say, '" + vaProxy.Utility.ParseTokens("{TXTRANDOM:Enable;Begin;Turn On;Start}") + " Voice Calculator'"
                        VA.WriteToLog(randomText, "purple")
                    end if
                    VA.SetInt("AVCS_CALC_FAULT", countFaults)
                else
                    VA.SetInt("AVCS_CALC_FAULT", 1)
                end if
                'Exit Calc Function when Mode OFF
                Exit Sub
            end if
        end if
        
        'Get Custom Decimal Place Settings (if exists)
        if ((VA.GetInt("AVCS_CALC_ResultsDecimalPlaces")) isNot nothing)
            decimalPlaces = Convert.ToInt32(VA.GetInt("AVCS_CALC_ResultsDecimalPlaces"))
        end if
        
        'Parse Spoken Command for Equation(s)
        if ((VA.GetText("~avcs_cmd")) isNot nothing)
            cmd = VA.GetText("~avcs_cmd")
            
            'Process spoken command formatting and keywords
            ReplaceEdgeCaseWords(cmd)
            FixLargeNumberSeparator(cmd)
            
            if (cmd.StartsWith("fore to"))
                cmd = cmd.Substring(7, cmd.Length - 7)
            elseif (cmd.StartsWith("for to"))
                cmd = cmd.Substring(6, cmd.Length - 6)
            elseif (cmd.StartsWith("or to"))
                cmd = cmd.Substring(5, cmd.Length - 5)
            elseif (cmd.StartsWith("1 is"))
                cmd = cmd.Substring(4, cmd.Length - 4)
            end if
            
            'Evaluate spoken command split at each character and space out symbols
            charArray = cmd.ToCharArray
            cmd = ""
            for each character as string in charArray
                if (SymbolsArray.Contains(character))
                    cmd += " " + character.ToString() + " "
                else
                    cmd += character.ToString()
                end if
            next
            
            'Evaluate all segments of speech split at {SPACE} and parse for operands and operators
            cmdSegments = cmd.Split(New String() {" "},StringSplitOptions.None)
            for each segment as string in cmdSegments
                if (segment <> "")
                    
                    'Handle numbers recognized as homophone words
                    select case segment
                        case "too"
                            segment = "2"
                        case "to"
                            segment = "2"
                        case "trees"
                            segment = "3"
                        case "tree"
                            segment = "3"
                        case "fore"
                            segment = "4"
                        case "for"
                            segment = "4"
                        case "ate"
                            segment = "8"
                        case "ape"
                            segment = "8"
                        case "valuetoo"
                            segment = "value2"
                        case "valueto"
                            segment = "value2"
                        case "valuetrees"
                            segment = "value3"
                        case "valuetree"
                            segment = "value3"
                        case "valuefore"
                            segment = "value4"
                        case "valuefor"
                            segment = "value4"
                        case "valueate"
                            segment = "value8"
                    end select
                    
                    'Handle short form of convertors
                    select case segment
                        case "c"
                            segment = "celcius"
                        case "f"
                            segment = "fahrenheit"
                        case "k"
                            segment = "kelvin"
                        case "m"
                            segment = "meters"
                    end select
                    
                    'Handle "subtract A from B" order of operations error
                    if (segment.Contains("from"))
                        if ((operand2 is nothing) and(operand1 isNot nothing) and ((operator1 isNot nothing) and (operator1.Contains("subtract"))))
                            subReverse = true
                        end if
                    end if
                    
                    'Handle "that" reference to last calculation result
                    if (segment.Contains("that"))
                        if ((VA.GetDecimal("AVCS_CALC_LastResult")) isNot nothing)
                            segment = VA.GetDecimal("AVCS_CALC_LastResult").ToString()
                        end if
                    end if
                    
                    'Handle "value" reference to previous calculation result
                    if ((segment.StartsWith("value")) and (IsNumeric(segment.Substring(segment.Length - 1, 1))))
                        if ((VA.GetDecimal(segment)) isNot nothing)
                            segment = VA.GetDecimal(segment).ToString()
                        else
                            VA.WriteToLog(segment + " does not exist in memory", "red")
                            Exit Sub
                        end if
                    end if
                    
                    'Handle negative temperatures
                    if ((operator2 isNot nothing) and (operand1 isNot nothing))
                        if ((operationsOrder = 2) and (ConvertorsArray.Contains(operator2)))
                            operator1 = operator2
                            operator2 = nothing
                            operand1 -= (operand1 * 2)
                            operationsOrder = 5
                        end if
                    end if
                    
                    'Handle "pi" and homophone variants 
                    if (PiArray.Contains(segment))
                        if (operand1 is nothing)
                            operand1 = Pi
                        elseif (operand2 is nothing)
                            operand2 = Pi
                        elseif (operand3 is nothing)
                            operand3 = Pi
                        end if
                        
                    'Set Operands -------
                    elseif ((IsNumeric(segment)) and (operand1 is nothing))
                        try
                            operand1 = Convert.ToDecimal(segment)
                            operandLast = operand1
                            if (operationsOrder = 0)
                                operationsOrder = 1 'for edge case:  (b = Pi)
                            end if
                        catch
                            VA.WriteToLog("AVCS Calculation Error at Operand 1", "red")
                        end try
                    elseif ((IsNumeric(segment)) and (operand2 is nothing))
                        try
                            operand2 = Convert.ToDecimal(segment)
                            operandLast = operand2
                        catch
                            VA.WriteToLog("AVCS Calculation Error at Operand 2", "red")
                        end try
                    elseif ((IsNumeric(segment)) and (operand3 is nothing))
                        try
                            operand3 = Convert.ToDecimal(segment)
                            operandLast = operand3
                        catch
                            VA.WriteToLog("AVCS Calculation Error at Operand 3", "red")
                        end try
                        
                    'Set Operators -------
                    elseif ((OperatorsArray.Contains(segment)) and (operator1 is nothing))
                        operator1 = segment
                        if (operationsOrder = 0)
                            operationsOrder = 2 'for edge case:  (a = Pi)
                        end if
                    elseif ((OperatorsArray.Contains(segment)) and (operator2 is nothing))
                        operator2 = segment
                        if ((operationsOrder = 1) and (operand2 isNot nothing))
                            operationsOrder = 3 'for edge case:  (c = b and b = Pi)
                        elseif ((operationsOrder = 1) and (operand2 is nothing))
                            operationsOrder = 4 'for edge case:  (c = Pi)
                        end if
                        
                    'Set Convertors -------
                    elseif ((ConvertorsArray.Contains(segment)) and (operator1 is nothing))
                        operator1 = segment
                        operationsOrder = 5 'for edge case null:  (no way Pi)
                    elseif ((ConvertorsArray.Contains(segment)) and (operator2 is nothing))
                        operator2 = segment
                        
                    'Handle "..illions" -------
                    elseif (segment.EndsWith("illion"))
                        if ((operand1 isNot nothing) and (operandLast = operand1))
                            FixLargeWordNum(segment,operand1)
                        elseif ((operand2 isNot nothing) and (operandLast = operand2))
                            FixLargeWordNum(segment,operand2)
                        elseif ((operand3 isNot nothing) and (operandLast = operand3))
                            FixLargeWordNum(segment,operand3)
                        end if
                        if (segment.StartsWith("AVCS Calculation Error"))
                            VA.WriteToLog(segment, "red")
                            Exit Sub
                        end if
                    end if
                    
                end if
            next
            
            
            'Evaluate for Pi recognized as "by" (edge cases)
            if (operationsOrder < 5)
                '-- 1 operation catch
                if ((operator2 is nothing) and (operand2 is nothing))
                    if (HasPi(operator1))
                        if (operationsOrder = 1)
                            operand2 = Pi
                        elseif (operationsOrder = 2)
                            operand2 = operand1
                            operand1 = Pi
                        end if
                    end if
                '-- 2 operations catch
                elseif ((operator2 isNot nothing) and (operand3 is nothing))
                    if ((HasPi(operator1)) or (HasPi(operator2)))
                        if (operationsOrder = 4)
                            operand3 = operand2
                            operand2 = Pi
                        elseif (operationsOrder = 3)
                            operand3 = Pi
                        elseif (operationsOrder = 2)
                            operand3 = operand2
                            operand2 = operand1
                            operand1 = Pi
                        end if
                    end if
                end if
            end if
            
            'Evaluate Operator 1 ======
            if (operator1 isNot nothing)
                'Handle null operands on Squared/Cubed operators
                if ((operator1.StartsWith("square")) and (not(operator1.Contains("root"))))
                    operand2 = 2
                elseif (operator1.StartsWith("cub"))
                    operand2 = 3
                'Handle Reverse order of operations on Subtract
                elseif ((operator1.Contains("subtract")) and (subReverse))
                    dim operand4 as decimal? = operand2
                    operand2 = operand1
                    operand1 = operand4
                end if
                
                'Set Operator 1 keyword and get text form of Operator/Convertor
                if (not(OperatorsArray.Contains(operator1)))
                    SetConvertor(operator1,operatorText1)
                else
                    SetOperator(operator1,operatorText1)
                end if
            else
                VA.WriteToLog("AVCS Calculation Error on Null Operator 1", "red")
                Exit Sub
            end if
            
            'Evaluate Operator 2 ======
            if (operator2 isNot nothing)
                'Handle null operands on Squared/Cubed operators
                if ((operator2.StartsWith("square")) and (not(operator2.Contains("root"))))
                    operand3 = 2
                elseif (operator2.Contains("cub"))
                    operand3 = 3
                end if
                
                'Set Operator 2 keyword and get text form of Operator/Convertor
                if (not(OpsArray.Contains(operator1)))
                    SetConvertor(operator2,operatorText2)
                    operator1 = operator1 + operator2.Substring(0, operator2.Length - 1)
                    operator2 = nothing
                else
                    SetOperator(operator2,operatorText2)
                    'Calculate Square Root on Multi-Part Equation to avoid errors on null operand3
                    'ex. 200 subtract square root of 128 = 188.69 = a-(Sqrt(b))
                    if ((operator2 = "sqr") and (operand3 is nothing))
                        equationText = operand1.ToString() + " " + operatorText1 + " " + operatorText2 + " " + operand2.ToString() + " "
                        operand2 = Sqrt(operand2)
                        operator2 = nothing
                    
                    'Calculate Percentage before to avoid order of operations and text display errors
                    'ex. 200 subtract 20 % = 160 = a-(b*(a/100))
                    elseif ((operator2 = "per") and (operand3 is nothing))
                        equationText = operand1.ToString() + " " + operatorText1 + " " + operand2.ToString() + " " + operatorText2 + " " + operand1.ToString() + " "
                        operand3 = operand1
                        operand2 = operand2*(operand3 / 100)
                        operand3 = nothing
                        operator2 = nothing
                    'ex. 200 subtract 20 % of 100 = 180 = a-(b*(c/100)
                    elseif ((operator2 = "per") and (operand3 isNot nothing))
                        equationText = operand1.ToString() + " " + operatorText1 + " " + operand2.ToString() + " " + operatorText2 + " " + operand3.ToString() + " "
                        operand2 = operand2*(operand3 / 100)
                        operand3 = nothing
                        operator2 = nothing
                    
                    'Calculate Exponents before to avoid order of operations and text display errors
                    'ex. 4 plus 7 to the power of 3 = 347 = a+(b^c)
                    elseif (((operator2 = "exp") and (operatorText2.StartsWith("to"))) and (operand3 isNot nothing))
                        equationText = operand1.ToString() + " " + operatorText1 + " " + operand2.ToString() + " " + operatorText2 + " " + operand3.ToString() + " "
                        operand2 = Pow(operand2, operand3)
                        operand3 = nothing
                        operator2 = nothing
                    'ex. 4 plus 7 cubed = 347 = a+b³   [or]   4 plus 7 squared = 53 = a+b²
                    elseif (((operator2 = "exp") and (not(operatorText2.StartsWith("to")))) and (operand3 isNot nothing))
                        equationText = operand1.ToString() + " " + operatorText1 + " " + operand2.ToString() + " " + operatorText2 + " "
                        operand2 = Pow(operand2, operand3)
                        operand3 = nothing
                        operator2 = nothing
                    end if
                end if
            end if
            
            'Final Evaluation for "8" recognized as "a"
            if ((DoubleOpsArray.Contains(operator1)) and (operator2 is nothing) and (operand1 isNot nothing) and (operand2 is nothing))
                if ((cmd.Contains(" a ")) or (cmd.EndsWith(" a")))
                    if (operationsOrder = 1)
                        operand2 = 8
                    else
                        operand2 = operand1
                        operand1 = 8
                    end if
                end if
            end if
            
            
            '==CALCULATE RESULT==
            if (OpsArray.Contains(operator1))
                'Build Equation Text for display in VA Event log
                if (equationText = "")
                    if (operand1 isNot nothing)
                        equationText += operand1.ToString() + " "
                    end if
                    if (operator1 isNot nothing)
                        equationText += operatorText1 + " "
                    end if
                    if (operand2 isNot nothing)
                        equationText += operand2.ToString() + " "
                    end if
                    if (operator2 isNot nothing)
                        equationText += operatorText2 + " "
                    end if
                    if (operand3 isNot nothing)
                        equationText += operand3.ToString() + " "
                    end if
                    
                    'Calculate Square Root on Multi-Part Equation to avoid incorrect operations math and display text
                    'ex. square root of 144 times 7 = 84 = b*(Sqrt(a))
                    if ((operator1 = "sqr") and ((operator2 isNot nothing) and (operand2 isNot nothing)))
                        equationText = operatorText1 + " " + operand1.ToString() + " " + operatorText2 + " " + operand2.ToString() + " "
                    end if
                    
                    'Remove any whitespace between % and number(s)
                    if (equationText.Contains(" %"))
                        equationText = equationText.Replace(" %","%")
                    end if
                end if
                
                'MATHS BEGIN--------------------------------
                'If there are 2 operations
                if (operator2 isNot nothing)
                    'If there are 2 operations with 3 values
                    if (operand3 isNot nothing)
                        if ((operand1 isNot nothing) and (operand2 isNot nothing))
                            result = GetResult(operator1,operand1,operand2)
                        end if
                        if (result isNot nothing)
                            result = GetResult(operator2,result,operand3)
                        end if
                    'Else if there are 2 operations with 2 values
                    elseif (operand2 isNot nothing)
                        'Handle Square Root on Second Operand
                        if ((operator1 = "sqr") and (operand1 isNot nothing))
                            result = Sqrt(operand1)
                        else
                            result = nothing
                        end if
                        
                        if ((operator1 = "sqr") and (result isNot nothing))
                            result = GetResult(operator2,result,operand2)
                        else
                            if ((result isNot nothing) and (operand1 isNot nothing))
                                result = GetResult(operator1,operand1,result)
                            elseif (operand1 isNot nothing)
                                result = GetResult(operator2,operand1,operand2)
                            end if
                        end if
                    end if
                
                'Else if there is 1 operation with up to 2 values
                elseif (operator1 isNot nothing)
                    if ((operand1 isNot nothing) and  (operand2 isNot nothing))
                        result = GetResult(operator1,operand1,operand2)
                        if ((operatorText1 = "cubed") or (operatorText1 = "squared"))
                            equationText = equationText.Replace(" " + operand2.ToString() + " "," ")
                        end if
                    elseif ((operand1 isNot nothing) and (not(DoubleOpsArray.Contains(operator1))))
                        result = GetResult(operator1,operand1)
                        equationText = equationText.Replace(operand1.ToString() + " ","") + operand1.ToString() + " "
                    end if
                end if
            else
                'Else this is a Unit Conversion with 1 value
                if (operator1 isNot nothing)
                    if (operand1 isNot nothing)
                        result = GetConversion(operator1,operand1)
                        equationText = operand1.ToString() + " " + operatorText1 + " = "
                    end if
                else
                    VA.WriteToLog("AVCS Calculation Error on Null Operator 1", "red")
                end if
            end if
            
            
            'Evaluate Result ======
            if (result isNot nothing)
                try
                    'Try to compare decimal with integer version of itself to discover decimal places, then round
                    dim checkResult as Int32 = Convert.ToInt32(result)
                    if (result = checkResult)
                        'For TTS brevity use the one that couldn't end with .000
                        result = checkResult
                    elseif (checkResult <> result)
                        dim roundResult as Double = result
                        result = Round(roundResult, decimalPlaces)
                    end if
                catch
                    'Result is a whole number already, does not need rounding for TTS brevity
                end try
                
                'Correct Pi in equation text string to 2 decimal places for TTS brevity
                if (equationText.Contains(Pi.ToString()))
                    equationText = equationText.Replace(Pi.ToString(), "3.14")
                end if
                
                'Get/Increment/Set Current Operations History Count
                if ((VA.GetInt("AVCS_CALC_CountOperations")) isNot nothing)
                    countHistory = VA.GetInt("AVCS_CALC_CountOperations")
                    countHistory += 1
                    if (countHistory > 99)
                        countHistory = 1
                    end if
                end if
                VA.SetInt("AVCS_CALC_CountOperations", countHistory)
                
                'Save to Calc History & Display Last complete equation with result in Event Log
                if (OpsArray.Contains(operator1))
                    equationText = equationText.Replace(" ²","²").Replace(" ³","³").Replace(" ⁿ ","ⁿ").Replace("√ ","√")
                    VA.SetText("AVCS_CALC_LastOperation", equationText + "= " + result.ToString())
                    VA.SetText("AVCS_CALC_LastOperation_" + countHistory.ToString(), equationText + "= " + result.ToString())
                    VA.WriteToLog("Value " + countHistory.ToString() + " = " + equationText + "= " + result.ToString(), "green")
                else
                    VA.SetText("AVCS_CALC_LastOperation", equationText + result.ToString() + " " + operatorText2)
                    VA.SetText("AVCS_CALC_LastOperation_" + countHistory.ToString(), equationText + result.ToString() + " " + operatorText2)
                    VA.WriteToLog("Value " + countHistory.ToString() + " = " + equationText + result.ToString() + " " + operatorText2, "green")                 
                end if
                
                'Save to Memory, Set Last for "that" reference, and Return Result for TTS
                VA.SetDecimal("value" + countHistory.ToString(), result)
                VA.SetDecimal("AVCS_CALC_LastResult", result)
                VA.SetText("avcs_calc_return", result.ToString())               
            end if
            
        end if
        
    End Sub
    
End Class

Comments

  • SemlerPDX icon
    SemlerPDX PRO
    04/12/23 06:54:15 PM UTC
    text |

    0 B

    |

    0 👍

    /

    0 👎

    // Error at line 375 - equation is for cm to inches... should be: val *= 2.54