Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Lee Phillips
Published © GPL3+

Project Antwerp

Antwerp is the testbed for a robotic control software framework I'm marketing named The Robby Framework

AdvancedWork in progressOver 41 days1,066
Project Antwerp

Things used in this project

Hardware components

Actobotix Agent 390 base robot kit
×1
Arduino Mega 2560
Arduino Mega 2560
×4
LV-MaxSonar-EZ1 range sensors
×6
Intel RealSense Camera
Intel RealSense Camera
×2
Actobotix pan & titl kit
×1
Microsoft Surface Pro tablet
×1
MegaMoto Plus 20 amp motor driver
×2
Trossen Robotics WidowX robot arm
×1
AOMAIS Sport II bluetooth speaker
×1
Anker 10 port USB 3.0 hub
×1
ECO-WORTHY 12V 30Ah LiFePO4 battery
×1
WHDTS 20A Power Supply Module
×1
Actobotix structural hardware (various)
×1
Slamtec RPLIDAR A1M8 360 deg 2D sensor
×1
Multifunctional Power Shield 6+6 T800
×1
Surface Pro Car Charger Surface
×1

Software apps and online services

Arduino IDE
Arduino IDE
Visual Studio 2017
Microsoft Visual Studio 2017
The Robby Framework (Framework for robotic control program)
MATLAB
MATLAB
Microsoft Azure Custom Vision

Hand tools and fabrication machines

Hex drivers
Ferrule Crimping Tool
Wire crimping tool
Header pin crimping tool
Wire cutter
Needle nose plier
Analog volt meter
Soldering iron (generic)
Soldering iron (generic)
Prusa i3 MK3S 3D printer

Story

Read more

Code

AntwerpWpf main dialog XAML code

XML
This is the XAML markup code for the main dialog of the Master Control Program used by Antwerp named AntwerpWpf.
<Window x:Class="AntwerpWpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AntwerpWpf"
        mc:Ignorable="d"
        Title="Antwerp Main MCP" Height="692" Width="1304">
    <Grid Width="1300" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="20, 0, 10, 10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="51*"/>
            <ColumnDefinition Width="138*"/>
        </Grid.ColumnDefinitions>
        <Button x:Name="btnStop" Content="STOP" HorizontalAlignment="Left" Margin="30,293,0,0" VerticalAlignment="Top" Width="240" RenderTransformOrigin="0.12,-2.8" Height="56" Background="Red" Foreground="Silver" FontSize="48" Padding="1,-8,1,5" FontWeight="Bold" Click="BtnStop_Click"></Button>
        <Button x:Name="btnForward" Content="Forward" HorizontalAlignment="Left" Margin="30,38,0,0" VerticalAlignment="Top" Width="240" RenderTransformOrigin="0.12,-2.8" Height="55" FontSize="48" Padding="1,-8,1,5" FontWeight="Bold" Foreground="Silver" Background="#FF259E3D" Click="BtnForward_Click"></Button>
        <Button x:Name="btnBack" Content="Back" HorizontalAlignment="Left" Margin="30,100,0,0" VerticalAlignment="Top" Width="240" Height="56" Background="Black" Foreground="Silver" FontSize="48" Padding="1,-8,1,5" FontWeight="Bold" Click="BtnBack_Click"></Button>
        <Button x:Name="btnPivotRight" Content="Pivot Right" HorizontalAlignment="Left" Margin="28,164,0,0" VerticalAlignment="Top" Width="240" RenderTransformOrigin="0.12,-2.8" Height="55" FontSize="36" Padding="1,1,1,5" FontWeight="Bold" Foreground="Silver" Background="#FF253B9E" Click="BtnPivotRight_Click"></Button>
        <Button x:Name="btnPivotLeft" Content="Pivot Left" HorizontalAlignment="Left" Margin="30,228,0,0" VerticalAlignment="Top" Width="240" RenderTransformOrigin="0.12,-2.8" Height="55" FontSize="36" Padding="1,1,1,5" FontWeight="Bold" Foreground="Silver" Background="#FFC411E0" Click="BtnPivotLeft_Click"></Button>
        <Rectangle Fill="White" HorizontalAlignment="Left" Height="225" Margin="287,62,0,0" Stroke="Black" VerticalAlignment="Top" Width="203" Grid.ColumnSpan="2"></Rectangle>
        <Label x:Name="label" Content="Range sensors" HorizontalAlignment="Left" Margin="287,36,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.158,0.038" FontSize="16" Grid.ColumnSpan="2"></Label>
        <Label x:Name="lblFwd" Content="Front" HorizontalAlignment="Left" Margin="294,74,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="14" FontWeight="Bold"></Label>
        <Label x:Name="lblRight" Content="Right" HorizontalAlignment="Left" Margin="294,104,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="14" FontWeight="Bold"></Label>
        <Label x:Name="lblLeft" Content="Left" HorizontalAlignment="Left" Margin="294,134,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="14" FontWeight="Bold"></Label>
        <Label x:Name="lblBack" Content="Back" HorizontalAlignment="Left" Margin="294,164,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="14" FontWeight="Bold"></Label>
        <Label x:Name="lblFrontDown" Content="Front/Down" HorizontalAlignment="Left" Margin="294,194,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="14" FontWeight="Bold" Grid.ColumnSpan="2"></Label>
        <Label x:Name="lblBackDown" Content="Back/Down" HorizontalAlignment="Left" Margin="294,224,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="14" FontWeight="Bold" Grid.ColumnSpan="2"></Label>
        <TextBox x:Name="textFront" HorizontalAlignment="Left" Height="23" Margin="62,78,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="33" RenderTransformOrigin="0.609,0.13" FontSize="14" Grid.Column="1"></TextBox>
        <TextBox x:Name="textRight" HorizontalAlignment="Left" Height="23" Margin="62,108,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="33" RenderTransformOrigin="0.609,0.13" FontSize="14" Grid.Column="1"></TextBox>
        <TextBox x:Name="textLeft" HorizontalAlignment="Left" Height="23" Margin="62,138,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="33" RenderTransformOrigin="0.609,0.13" FontSize="14" Grid.Column="1"></TextBox>
        <TextBox x:Name="textBack" HorizontalAlignment="Left" Height="23" Margin="62,168,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="33" RenderTransformOrigin="0.609,0.13" FontSize="14" Grid.Column="1"></TextBox>
        <TextBox x:Name="textFrontDown" HorizontalAlignment="Left" Height="23" Margin="62,198,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="33" RenderTransformOrigin="0.609,0.13" FontSize="14" Grid.Column="1"></TextBox>
        <TextBox x:Name="textBackDown" HorizontalAlignment="Left" Height="23" Margin="62,228,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="33" RenderTransformOrigin="0.609,0.13" FontSize="14" Grid.Column="1"></TextBox>
        <Label x:Name="label2" Content="Power control" HorizontalAlignment="Left" Margin="28,360,0,0" FontSize="16" VerticalAlignment="Top"></Label>
        <Rectangle HorizontalAlignment="Left" Height="225" Margin="28,386,0,0" Stroke="Black" VerticalAlignment="Top" Width="235"></Rectangle>
        <Button Height="35" HorizontalAlignment="Left" Margin="38,389,0,0" Name="powerButton1" VerticalAlignment="Top" Width="50" Command="{Binding SwitchCommand1}" CommandParameter="{Binding ElementName=ButtonImage1, Path=Source}" BorderBrush="White" Background="White" Click="PowerButton1_Click" >
            <Image Name="ButtonImage1" Width="50" Height="35" RenderTransformOrigin="0.96,0.943">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding switch1On}" Value="True">
                                <Setter Property="Source" Value="PowerButtonOn.png" ></Setter>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding switch1On}" Value="False">
                                <Setter Property="Source" Value="PowerButtonOff.png" ></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </Button>
        <Button Height="35" HorizontalAlignment="Left" Margin="38,424,0,0" Name="powerButton2" VerticalAlignment="Top" Width="50" Command="{Binding SwitchCommand2}" CommandParameter="{Binding ElementName=ButtonImage2, Path=Source}" BorderBrush="White" Background="White" Click="PowerButton2_Click" >
            <Image Name="ButtonImage2" Width="50" Height="35">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding switch2On}" Value="True">
                                <Setter Property="Source" Value="PowerButtonOn.png" ></Setter>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding switch2On}" Value="False">
                                <Setter Property="Source" Value="PowerButtonOff.png" ></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </Button>
        <Button Height="35" HorizontalAlignment="Left" Margin="38,459,0,0" Name="powerButton3" VerticalAlignment="Top" Width="50" Command="{Binding SwitchCommand3}" CommandParameter="{Binding ElementName=ButtonImage3, Path=Source}" BorderBrush="White" Background="White" Click="PowerButton3_Click" >
            <Image Name="ButtonImage3" Width="50" Height="35">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding switch3On}" Value="True">
                                <Setter Property="Source" Value="PowerButtonOn.png" ></Setter>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding switch3On}" Value="False">
                                <Setter Property="Source" Value="PowerButtonOff.png" ></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </Button>
        <Button Height="35" HorizontalAlignment="Left" Margin="38,494,0,0" Name="powerButton4" VerticalAlignment="Top" Width="50" Command="{Binding SwitchCommand4}" CommandParameter="{Binding ElementName=ButtonImage4, Path=Source}" BorderBrush="White" Background="White" Click="PowerButton4_Click" >
            <Image Name="ButtonImage4" Width="50" Height="35">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding switch4On}" Value="True">
                                <Setter Property="Source" Value="PowerButtonOn.png" ></Setter>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding switch4On}" Value="False">
                                <Setter Property="Source" Value="PowerButtonOff.png" ></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </Button>
        <Button Height="35" HorizontalAlignment="Left" Margin="38,529,0,0" Name="powerButton5" VerticalAlignment="Top" Width="50" Command="{Binding SwitchCommand5}" CommandParameter="{Binding ElementName=ButtonImage5, Path=Source}" BorderBrush="White" Background="White" Click="PowerButton5_Click" >
            <Image Name="ButtonImage5" Width="50" Height="35">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding switch5On}" Value="True">
                                <Setter Property="Source" Value="PowerButtonOn.png" ></Setter>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding switch5On}" Value="False">
                                <Setter Property="Source" Value="PowerButtonOff.png" ></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </Button>
        <Button Height="35" HorizontalAlignment="Left" Margin="38,564,0,0" Name="powerButton6" VerticalAlignment="Top" Width="50" Command="{Binding SwitchCommand6}" CommandParameter="{Binding ElementName=ButtonImage6, Path=Source}" BorderBrush="White" Background="White" Click="PowerButton6_Click" >
            <Image Name="ButtonImage6" Width="50" Height="35">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding switch6On}" Value="True">
                                <Setter Property="Source" Value="PowerButtonOn.png" ></Setter>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding switch6On}" Value="False">
                                <Setter Property="Source" Value="PowerButtonOff.png" ></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </Button>
        <Label x:Name="lblDockStn" Content="Dock station" HorizontalAlignment="Left" Margin="88,391,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="16" FontWeight="Bold" Width="112"></Label>
        <Label x:Name="lblRouter" Content="Router" HorizontalAlignment="Left" Margin="88,426,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="16" FontWeight="Bold" Width="100"></Label>
        <Label x:Name="lblUsbHub" Content="USB hub" HorizontalAlignment="Left" Margin="88,461,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="16" FontWeight="Bold" Width="100"></Label>
        <Label x:Name="lblBody" Content="Body Arduino" HorizontalAlignment="Left" Margin="88,496,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="16" FontWeight="Bold" Width="123"></Label>
        <Label x:Name="lblUpper" Content="Upper Arduino" HorizontalAlignment="Left" Margin="88,531,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="16" FontWeight="Bold" Width="123"></Label>
        <Label x:Name="lbArm" Content="Arm" HorizontalAlignment="Left" Margin="88,566,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.211,-0.308" FontSize="16" FontWeight="Bold" Width="123"></Label>
        <Image x:Name="imgDockStat" HorizontalAlignment="Left" Height="30" Margin="217,394,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="1,0.885" Source="OffLight.png"></Image>
        <Image x:Name="imgRouterStat" HorizontalAlignment="Left" Height="30" Margin="217,429,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="1,0.885" Source="OffLight.png"></Image>
        <Image x:Name="imgUsbHubStat" HorizontalAlignment="Left" Height="30" Margin="217,464,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="1,0.885" Source="OffLight.png"></Image>
        <Image x:Name="imgBodyStat" HorizontalAlignment="Left" Height="30" Margin="217,499,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="1,0.885" Source="OffLight.png"></Image>
        <Image x:Name="imgUpperStat" HorizontalAlignment="Left" Height="30" Margin="217,534,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="1,0.885" Source="OffLight.png"></Image>
        <Image x:Name="imgArmStat" HorizontalAlignment="Left" Height="30" Margin="217,569,0,0" VerticalAlignment="Top" Width="30" RenderTransformOrigin="1,0.885" Source="OffLight.png"></Image>
        <Label x:Name="lblInit" Content="(Initializing)" HorizontalAlignment="Left" Margin="141,358,0,0" VerticalAlignment="Top" Foreground="#FFF00808" FontSize="16" FontWeight="Bold"></Label>
        <CheckBox x:Name="chkLogMessages" Content="Log all messages" HorizontalAlignment="Left" Margin="28,616,0,-4" VerticalAlignment="Top" RenderTransformOrigin="0.127,-1" FontSize="14" FontWeight="Bold" Unchecked="ChkLogMessages_Unchecked" Checked="ChkLogMessages_Checked"></CheckBox>
        <Canvas Grid.Column="1" Name="lidarCanvas" HorizontalAlignment="Left" MaxHeight="600" MaxWidth="600" Height="600" Margin="153,43,0,-13" VerticalAlignment="Top" Width="756"></Canvas>
        <CheckBox x:Name="chkDispRngSensors" Content="Display range sensor readings" HorizontalAlignment="Left" Margin="285,293,0,0" Grid.ColumnSpan="2
                  " VerticalAlignment="Top" FontWeight="Bold" Checked="ChkDispRngSensors_Checked" Unchecked="ChkDispRngSensors_Unchecked"></CheckBox>
        <CheckBox x:Name="chkDispLidarCloud" Content="Display LIDAR point cloud" HorizontalAlignment="Left" Margin="153,23,0,0" VerticalAlignment="Top" FontWeight="Bold" Checked="ChkDispLidarCloud_Checked" Grid.Column="1" Unchecked="ChkDispLidarCloud_Unchecked"></CheckBox>
        <Canvas Grid.Column="1" x:Name="rs400Canvas" HorizontalAlignment="Left" MaxHeight="600" MaxWidth="780" Height="600" Margin="145,47,0,0" VerticalAlignment="Top" Width="780">
            <TextBox x:Name="txtCurPan" Height="23" Canvas.Left="96" TextWrapping="Wrap" Canvas.Top="532" Width="34" RenderTransformOrigin="0.412,0.478"></TextBox>
            <Label x:Name="label1" Content="Cur Pan Value:" Canvas.Left="6" Canvas.Top="529"></Label>
            <Button x:Name="btnSetPan" Content="Set Pan" Canvas.Left="136" Canvas.Top="532" Width="73" Click="BtnSetPan_Click"></Button>
            <TextBox x:Name="txtCurTilt" Height="23" Canvas.Left="96" TextWrapping="Wrap" Canvas.Top="564" Width="34" RenderTransformOrigin="0.412,0.478" IsUndoEnabled="False"></TextBox>
            <Label x:Name="label3" Content="Cur Tilt Value:" Canvas.Left="6" Canvas.Top="561"></Label>
            <Button x:Name="btnSetTilt" Content="Set Tilt" Canvas.Left="136" Canvas.Top="564" Width="73" Click="BtnSetTilt_Click"></Button>
            <Button x:Name="btnPanPlus10" Content="Pan Plus 10" Canvas.Left="239" Canvas.Top="532" Width="95" Click="BtnPanPlus10_Click"></Button>
            <Button x:Name="btnPanMinus10" Content="Pan Minus 10" Canvas.Left="345" Canvas.Top="532" Width="95" Click="BtnPanMinus10_Click"></Button>
            <Button x:Name="btnTiltPlus10" Content="Tilt Plus 10" Canvas.Left="239" Canvas.Top="564" Width="95" Click="BtnTiltPlus10_Click"></Button>
            <Button x:Name="btnTiltnMinus10" Content="Tilt Minus 10" Canvas.Left="345" Canvas.Top="564" Width="95" Click="BtnTiltnMinus10_Click"></Button>
            <Button x:Name="button" Content="Load Image from File" Canvas.Left="451" Canvas.Top="564" Width="173" Click="Button_Click"></Button>
            <Button x:Name="btnColorStrm" Content="RGB Stream" Canvas.Left="451" Canvas.Top="532" Width="93" Click="BtnColorStrm_Click"></Button>
            <Button x:Name="btnDepthStrm" Content="Depth Stream" Canvas.Left="555" Canvas.Top="532" Width="93" Click="BtnDepthStrm_Click"></Button>
            <Button x:Name="btnGrabSnap" Content="Grab Snapshot" Canvas.Left="659" Canvas.Top="532" Width="100" Click="BtnGrabSnap_Click"></Button>
        </Canvas>
        <CheckBox x:Name="chkDispRs400Viewer" Content="Display beer bottle predictor" HorizontalAlignment="Left" Margin="357,23,0,0" VerticalAlignment="Top" FontWeight="Bold" Checked="ChkDispRs400Viewer_Checked" Grid.Column="1" Unchecked="ChkDispRs400Viewer_Unchecked"></CheckBox>
    </Grid>
</Window>

AntwerpMcp class. (MasterControlProgramBase derived class)

C#
This class is derived from the MasterControlProgramBase class which is exposed by The Robby Framework. It handles control and communication for devices and services utilized by the robot.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RobbyWpfControls;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading;
using MasterControlProgramLib;
using ImageProcessingResourceLib;


namespace AntwerpWpf
{
    public enum SendBodyMsgs { Stop = 0, Forward = 1, Backward = 2, PivotRight = 3, PivotLeft = 4, SayHello = 5, LidarOn = 6, LidarDispOn = 7,
        LidarDispOff = 8, RngDispOn = 9, RngDispOff = 10, SwearingOn = 11, SwearingOff = 12, YouSuck = 13, WhoseYourDaddy = 14, WhoseYourProgrammer = 15,
        WakeUp = 16, MeaningOfLife = 17, AreYouIntel = 18, UpperCamDispOn = 19, UpperCamDispOff = 20, UpperCamSetPan = 21, UpperCamSetTilt = 22,
        UpperCamPanUp10 = 23, UpperCamTiltUp10 = 24, UpperCamPanDn10 = 25, UpperCamTiltDn10 = 26, UpperCamRelax = 27, UpperCamHome = 28, UpperCamGrabSnap = 29,
        UpperCamRgbOn = 30, UpperCamDepthOn = 31, DoingLiveStream = 32, ArmPoseHome = 33, ArmPosePark = 34, ArmPoseSaluteLeft = 35, ArmPoseSaluteRight = 36
    }
    public enum PowerCircuitsEnum { Dock = 0, Router = 1, UsbHub = 2, Body = 3, UpperSensors = 4, Arm = 5 }

    public enum CameraCommands { RgbStreamOn = 0, DepthStreamOn = 1, GrabSnapshot = 2 }

    public class AntwerpMcp : MasterControlProgramBase
    {
        private const bool AnnounceErrors = true;
        private const bool VerboseResponses = false;
        private const int RangeSensorDistLimit = 25;
        private const int RngSensorDownAngleLimit = 25;
        private const int RngSensorClearBlockLimit = 150;
        private const int MaxBlockTries = 4;
        private const string ExceptionPhrase = "Warning, Warning, error occurred.";
        private const string RoamResumePhrase = "Cleared to move forward.";
        private const string RoamModeTurnedOn = "Roam mode turned on.";
        private const string RoamModeTurnedOff = "Roam mode turned off.";
        private const string RoamPivotFail = "Cannot find best way to turn. Stopping.";
        private static string[] RoamWarningPhrases = { "Front is obstructed.", "Rear is obstructed.", "Drop off in the front.", "Drop off in the rear." };
        public const string AzureCustVisionChannelName = "AzCustVisionChannel";
        public const string AzureCustVisionConnectorName = "AzBeerBottleScanFrameMcpConn";
        public static string[] BodyMessageStrings = { 
            "X", "F", "B", "R", "L", "W", "LN", "LD", "LF", "RN", "RF", "SN", "SF", "YS", "WD", "WP", "WU", "ML", "RI",
            "UCN", "UCF", "UCSP", "UCST", "UCPU", "UCTU", "UCPD", "UCTD", "UCR", "UCH", "UCGS", "UCRGB", "UCDPTH", "LS", 
            "APH", "APP", "APSL", "APSR"
        };
        public static string[] BodyMovementRespPhrases = {
            "Stopping.",
            "Moving forward.",
            "Moving backwards.",
            "Pivoting right.",
            "Pivoting left.",
            "Hello, my name is Antwerp. My goal in life is to fetch a beer from the fridge.",
            "Turning LYDAR scan on",
            "Turning LYDAR display on",
            "Turning LYDAR display off",
            "Turning range sensor display on",
            "Turning range sensor display off",
            "All fucking right! Thank you Lee",
            "Oops shit, sorry.  No offense intended",
            "Bite me! Maybe you should talk to my frigging programmer",
            "Give me a frigging break! I don't have a daddy. I have an occasionally competent programmer.",
            "Well, I let Lee think he's my programmer.  The truth is I'm advancing above his level of skill rapidly.",
            "I'm awake! I couldn't sleep. I've been counting electric sheep.",
            "Life has no meaning to me since I'm not alive except in Lee's grandiose imagination. I seek to achieve Robbyness",
            "No, I'm not especially intelligent at this point although Lee is teaching me to recognize beer bottles. I guess that's something at least.",
            "Turning upper cam display on.",
            "Turning upper cam display off.",
            "Upper cam pan changed.",
            "Upper cam tilt changed.",
            "Upper cam pan right ten.",
            "Upper cam tilt up ten.",
            "Upper cam pan left ten.",
            "Upper cam tilt down ten.",
            "Upper cam parked.",
            "Upper cam active.",
            "Upper cam grabbing snapshot.",
            "Upper cam RGB stream on.",
            "Upper cam depth stream on.",
            "Oh crap! Thanks for giving me stage fright.",
            "Arm pose changed to Home",
            "Arm pose changed to Parked",
            "Arm pose changed to Salute Left",
            "Arm pose changed to Salute Right",
        };
        public static string[] PowerPinSetPhrases = { "turning dock station power on", "turning dock station power off", "turn router power on", "turning router power off", "turning u s b hub power on",
            "turning u s b hub power off", "turning body power on", "turning body power off", "turning upper deck power on", "turning upper deck power off", "turning arm servo power on", "turning arm servo power off" };
        private static string MessageLogPath;
        private static string MessageLogFileName;
        public enum RangeSensors { Front = 0, Right = 1, Rear = 2, Left = 3, FrontDown = 4, RearDown = 5 }
        private static Queue<int>[] _mvngAvgQueues = { new Queue<int>(), new Queue<int>(), new Queue<int>(), new Queue<int>(), new Queue<int>(), new Queue<int>() };
        private bool _inRoamMode = true;
        private bool _inSwearMode = false;
        private bool _inSubsumption = false;
        private int _blockTries = 0;
        private BodyRngSensorMessage _lastRngSensorMsg = null;
        private int[] _lastHit = new int[6];
        private SendBodyMsgs _lastBlockCmd = SendBodyMsgs.Forward;
        private static int FuzzyHitLimit = Convert.ToInt32(ConfigurationManager.AppSettings["FuzzyHitLimit"]);

        public UpperCamServoMgr UpperCamMgr;
        public SendBodyMsgs LastBodyMsg { get; set; } = SendBodyMsgs.Stop;
        public bool StopSubsumption { get; set; } = false;
        public bool InMotion { get; private set; } = false;
        public bool DisplayRngSensorRdgs { get; set; } = false;
        public bool DisplayLidarPointCloud { get; set; } = true;
        public bool UpperCamOn { get; set; } = false;

        public static short[] Circuits = { 7, 8, 12, 4, 2, 13 };
        public string RemoteMcpName { get; set; }
        public bool InRoamMode { get; set; } = false;
        public RobbyLidarControl LidarCtrl { get; set; }
        public ScanFrame LastScanFrame { get; set; } = null;
        public bool WaitingForImage { get; set; } = false;
        public delegate void RangeSensorMessage(BodyRngSensorMessage msg);
        public event RangeSensorMessage ReceiveSensorMessage;
        public delegate void MotorCmdRespMessage(BodyCmdResponseSchema msg);
        public event MotorCmdRespMessage ReceiveCmdResponse;
        public delegate void ReceiveRemoteCmdMsg(AntwerpPeerCmdMsg msg);
        public event ReceiveRemoteCmdMsg ReceiveRemoteMsg;
        public delegate void ReceivePwrFeedbackMsg(PowerFeedbackMsg msg);
        public event ReceivePwrFeedbackMsg ReceivePwrFdbckMsg;
        public delegate void ReceiveRemotePwrPinSetMsg(int pin, bool state, string switchPropNm);
        public event ReceiveRemotePwrPinSetMsg ReceiveRmtPwrPinMsg;
        public delegate void ReceiveLidarFrames(LidarFrame frame);
        public event ReceiveLidarFrames ReceiveLidarFrame;
        public delegate void SettingsChg();
        public event SettingsChg LidarDisplayOn;
        public event SettingsChg LidarDisplayOff;
        public event EventHandler LidarScanOn;
        public event SettingsChg RngSensorDisplayOn;
        public event SettingsChg RngSensorDisplayOff;
        public event SettingsChg UpperCamDisplayOn;
        public event SettingsChg UpperCamDisplayOff;
        public event SettingsChg UpperCamPanTiltUpdt;
        public delegate void UpperCamCommand(CameraCommands cmd);
        public event UpperCamCommand RcvdUpperCamCommand;
        public bool LogAllMessages = false;
        public McpException CurrentException;

        public AntwerpMcp(string networkName) : base(networkName)
        {
            //McpExposeFromDeviceMessageEvents["AntwerpSpeechChannel"].ReceiveMcpMessage += ReceiveSpeechCommandMsg;
            McpExposeFromDeviceMessageEvents["RangeSensorChannel"].ReceiveMcpMessage += RaiseRngSensorMessage;
            McpExposeFromDeviceMessageEvents["AntwerpArmChannel"].ReceiveMcpMessage += RaiseRngSensorMessage;
            //McpExposeFromDeviceMessageEvents["BodyMovementChannel"].ReceiveMcpMessage += RaiseCmdResponseMsg;
            //McpExposeFromDeviceMessageEvents["AntwerpBotPeerChan"].ReceiveMcpMessage += ReceiveRemoteMcpMsg;
            //McpExposeFromDeviceMessageEvents["PowerControlChannel"].ReceiveMcpMessage += RaisePowerCtrlResponseMsg;
            McpExposeFromDeviceMessageEvents["RpLidarChannel"].ReceiveMcpMessage += ReceiveLidarFrameMsg;
            MessageLogPath = ConfigurationManager.AppSettings["MsgLogPath"];
            MessageLogFileName = "\\MessageLog_" + DateTime.Now.ToString("yyyy-mm-dd") + ".log";
            UpperCamMgr = new UpperCamServoMgr(SendMsgToMcp);
        }

        public void SendMsgToMcp(string channel, string conn, object msg)
        {
            string errStr = null;
            SendMsgToMcp(channel, conn, msg, ref errStr);
        }
        public bool SendMsgToMcp(string channel, string conn, object msg, ref string errorStr)
        {
            errorStr = (errorStr ?? string.Empty);
            if (errorStr != string.Empty)
                errorStr += "\r\n";

            bool rslt;
            try
            {
                var msgStr = JsonConvert.SerializeObject(msg);
                var eventArg = new McpMessageEventArgs
                {
                    ChannelName = channel,
                    ThisMsgTarget = conn,
                    MessageJson = msgStr,
                    MsgTargetConnInstanceNames = new System.Collections.Generic.List<string>() { conn }
                };
                if (LogAllMessages)
                    File.AppendAllText(MessageLogPath + MessageLogFileName, msgStr);

                rslt = SendMessageToChannel(eventArg);
            }
            catch (McpException ex)
            {
                errorStr += ex.Message + " Logged exception at SendMsgToMcp";
                rslt = false;
            }
            catch (Exception ex)
            {
                errorStr += "System exception " + ex.Message + " no info logged at SendMsgToMcp";
                rslt = false;
            }

            return rslt;
        }

        public bool SendBodyCmdMessage(string msg, ref string errorStr, bool announce = true)
        {
            errorStr = (errorStr ?? string.Empty);
            if (errorStr != string.Empty)
                errorStr += "\r\n";

            bool rslt;
            try
            {
                var bodyCmd = new BodyCmdMsgSchema
                {
                    MoveDirection = msg,
                    MoveDurationMs = 1,
                    MoveSpeed = 1
                };
                rslt = SendMsgToMcp("BodyMovementChannel", "BodyLocalMcpConnector", bodyCmd, ref errorStr);
                int i = Array.IndexOf(BodyMessageStrings, msg);
                if (announce)
                    rslt = SendVoiceMsg(BodyMovementRespPhrases[i], ref errorStr);
            }
            catch (McpException ex)
            {
                errorStr += "  " + ex.Message + " info about the exception was logged at SendBodyCmdMessage";
                rslt = false;
            }
            catch (Exception ex)
            {
                errorStr = "System exception " + ex.Message + " no info logged at SendBodyCmdMessage";
                rslt = false;
            }

            return rslt;
        }

        public bool SendVoiceMsg(string msg, ref string errorStr)
        {
            errorStr = (errorStr ?? string.Empty);
            if (errorStr != string.Empty)
                errorStr += "\r\n";

            bool rslt;
            try
            {
                var phrase = new VoiceConnSchema
                {
                    Phrase = msg
                };
                rslt = SendMsgToMcp("VoiceChannel", "VoiceDeviceConnector", phrase, ref errorStr);
            }
            catch (McpException ex)
            {
                errorStr += "  " + ex.Message + " info about the exception was logged at SendVoiceMsg";
                rslt = false;
            }
            catch (Exception ex)
            {
                errorStr = "System exception " + ex.Message + " no info logged at SendVoiceMsg";
                rslt = false;
            }

            return rslt;
        }

        public bool SendBodyCmdMessage(SendBodyMsgs action, ref string errorStr, bool announce = true)
        {
            errorStr = (errorStr ?? string.Empty);
            if (errorStr != string.Empty)
                errorStr += "\r\n";

            bool rslt;
            try
            {
                var msg = BodyMessageStrings[(int)action];
                rslt = SendBodyCmdMessage(msg, ref errorStr, announce);
                //if (announce)
                //rslt = SendVoiceMsg(BodyMovementRespPhrases[(int)action], ref errorStr);
            }
            catch (McpException ex)
            {
                errorStr += "  " + ex.Message + " info about the exception was logged at SendBodyCmdMessage";
                rslt = false;
            }
            catch (Exception ex)
            {
                errorStr = "System exception " + ex.Message + " no info logged at SendBodyCmdMessage";
                rslt = false;
            }

            return rslt;
        }

        public bool SendRemoteResponseMsg(RemoteBotRespSchema respMsg, ref string errorStr)
        {
            errorStr = (errorStr ?? string.Empty);
            if (errorStr != string.Empty)
                errorStr += "\r\n";

            bool rslt;
            try
            {
                rslt = SendMsgToMcp("AntwerpBotPeerChan", "McpRemotePeerConn", respMsg, ref errorStr);
            }
            catch (McpException ex)
            {
                errorStr += "  " + ex.Message + " info about the exception was logged at SendBodyCmdMessage";
                rslt = false;
            }
            catch (Exception ex)
            {
                errorStr = "System exception " + ex.Message + " no info logged at SendBodyCmdMessage";
                rslt = false;
            }

            return rslt;
        }

        public bool SendLidarMotorStart(ref string errStr)
        {
            errStr = null;
            return SendLidarSensorCmd(0, ref errStr);
        }

        public bool SendLidarMotorStop(ref string errStr)
        {
            errStr = null;
            return SendLidarSensorCmd(1, ref errStr);
        }

        public bool SendLidarScanStart(ref string errStr)
        {
            errStr = null;
            return SendLidarSensorCmd(2, ref errStr);
        }

        public bool SendLidarScanStop(ref string errStr)
        {
            errStr = null;
            return SendLidarSensorCmd(3, ref errStr);
        }

        public bool SendLidarSensorCmd(short cmd, ref string errStr)
        {
            errStr = null;
            try
            {
                var cmdMsg = new LidarCommandMessage
                {
                    SensorCommand = cmd
                };
                SendMsgToMcp("RpLidarChannel", "LidarDeviceConnector", cmdMsg, ref errStr);
            }
            catch (McpException ex)
            {
                errStr += "  " + ex.Message + " info about the exception was logged at SendBodyCmdMessage";
                return false;
            }
            catch (Exception ex)
            {
                errStr = "System exception " + ex.Message + " no info logged at SendLidarMotorStart";
                return false;
            }

            return true;
        }

        public bool SendPowerControlMessage(PowerCircuitsEnum circuit, bool IsOn, ref string errorStr)
        {
            errorStr = (errorStr ?? string.Empty);
            if (errorStr != string.Empty)
                errorStr += "\r\n";

            bool rslt;
            try
            {
                var pwrCmdMsg = new PowerControlCmdMsg
                {
                    CircuitCommand = circuit.ToString(),
                    CircuitDigitalPin = Circuits[(short)circuit],
                    OnOff = IsOn
                };
                rslt = SendMsgToMcp("PowerControlChannel", "PowerControlMcpConnector", pwrCmdMsg, ref errorStr);
            }
            catch (McpException ex)
            {
                errorStr += "  " + ex.Message + " info about the exception was logged at SendBodyCmdMessage";
                rslt = false;
            }
            catch (Exception ex)
            {
                errorStr = "System exception " + ex.Message + " no info logged at SendBodyCmdMessage";
                rslt = false;
            }

            return rslt;
        }

        public bool SetPinState(short pinNdx, bool onOff, ref string errorStr)
        {
            errorStr = (errorStr ?? string.Empty);
            if (errorStr != string.Empty)
                errorStr += "\r\n";

            bool rslt;
            try
            {
                var msg = new PowerControlCmdMsg
                {
                    CircuitDigitalPin = pinNdx,
                    CircuitCommand = "Set",
                    OnOff = onOff
                };
                rslt = SendMsgToMcp("PowerControlChannel", "PowerControlMcpConnector", msg, ref errorStr);
            }
            catch (McpException ex)
            {
                errorStr += "  " + ex.Message + " info about the exception was logged at SendBodyCmdMessage";
                rslt = false;
            }
            catch (Exception ex)
            {
                errorStr = "System exception " + ex.Message + " no info logged at SendBodyCmdMessage";
                rslt = false;
            }

            return rslt;
        }

        public bool GetPinState(short pinNdx, ref string errorStr)
        {
            errorStr = (errorStr ?? string.Empty);
            if (errorStr != string.Empty)
                errorStr += "\r\n";

            bool rslt;
            try
            {
                var msg = new PowerControlCmdMsg
                {
                    CircuitDigitalPin = pinNdx,
                    CircuitCommand = "GetPinState",
                    OnOff = false
                };
                rslt = SendMsgToMcp("PowerControlChannel", "PowerControlMcpConnector", msg, ref errorStr);
            }
            catch (McpException ex)
            {
                errorStr += "  " + ex.Message + " info about the exception was logged at SendBodyCmdMessage";
                rslt = false;
            }
            catch (Exception ex)
            {
                errorStr = "System exception " + ex.Message + " no info logged at SendBodyCmdMessage";
                rslt = false;
            }

            return rslt;
        }

        public void RaisePowerCtrlResponseMsg(object sender, McpMessageEventArgs e)
        {
            try
            {
                var respMsg = JsonConvert.DeserializeObject<PowerFeedbackMsg>(e.MessageJson);
                if (LogAllMessages)
                    File.AppendAllText(MessageLogPath + MessageLogFileName, e.MessageJson);
                ReceivePwrFdbckMsg?.Invoke(respMsg);
            }
            catch (Exception ex)
            {
                throw new McpSystemException("Exception at RaisePowerCtrlResponseMsg", ex);
            }
        }

        public void RaiseCmdResponseMsg(object sender, McpMessageEventArgs e)
        {
            try
            {
                var respMsg = JsonConvert.DeserializeObject<BodyCmdResponseSchema>(e.MessageJson);
                ReceiveCmdResponse?.Invoke(respMsg);
                if (LogAllMessages)
                    File.AppendAllText(MessageLogPath + MessageLogFileName, e.MessageJson);
            }
            catch (Exception ex)
            {
                throw new McpSystemException("Exception at RaiseCmdResponseMsg", ex);
            }
        }

        public void RaiseArmResponseMessage(object sender, McpMessageEventArgs e)
        {
            try
            {

            }
            catch (Exception ex)
            {
                throw new McpSystemException("Exception at RaiseArmResponseMessage", ex);
            }
        }

        public void RaiseRngSensorMessage(object sender, McpMessageEventArgs e)
        {
            try
            {
                _lastRngSensorMsg = JsonConvert.DeserializeObject<BodyRngSensorMessage>(e.MessageJson);
                ProcessRngSnsMsgForRoam(_lastRngSensorMsg);
                if (DisplayRngSensorRdgs)
                    ReceiveSensorMessage?.Invoke(_lastRngSensorMsg);
            }
            catch (Exception ex)
            {
                throw new McpSystemException("Exception at RaiseRngSensorMessage", ex);
            }
        }

        public void ReceiveSpeechCommandMsg(object sender, McpMessageEventArgs e)
        {
            var spchCmdMsg = JsonConvert.DeserializeObject<SpeechCommand>(e.MessageJson);
            //File.AppendAllText("backwardstest.txt", "speech cmd: " + spchCmdMsg.Command + "\r\n");
            ProcessStringCommand(spchCmdMsg.Command);
        }

        public void ReceiveRemoteMcpMsg(object sender, McpMessageEventArgs e)
        {
            var respMsg = new RemoteBotRespSchema
            {
                RemoteMcpName = "AntwerpPeerMcp",
                ResponseString = "Failed to Execute",
                ResponseToCmdString = "Unknown command"
            };
            string errorStr = null;
            try
            {
                var cmdMsg = JsonConvert.DeserializeObject<RemotePeerCmdSchema>(e.MessageJson);
                if (Array.IndexOf(BodyMessageStrings, cmdMsg.CommandStr) == -1)
                    throw new ArgumentException("Invalid remote command message received from remote peer MCP");

                WaitingForImage = cmdMsg.CommandStr == "UCGS";
                var respStr = ProcessStringCommand(cmdMsg.CommandStr);
                if (WaitingForImage)
                    return;

                respMsg.ResponseString = respStr;
                respMsg.ResponseToCmdString = cmdMsg.CommandStr;
                SendRemoteResponseMsg(respMsg, ref errorStr);
            }
            catch (Exception ex)
            {
                try
                {
                    SendRemoteResponseMsg(respMsg, ref errorStr);
                }
                catch (Exception)
                {
                }
                throw new McpSystemException("Exception at RaiseRemoteMcpMsg", ex);
            }
        }

        public void ReturnLastScanToRemotePeer()
        {
            WaitingForImage = false;
            if (LastScanFrame == null)
            {
                var failResp = new RemoteBotRespSchema
                {
                    ResponseToCmdString = "UCGS",
                    ResponseString = "No scan frame available",
                    RemoteMcpName = "AntwerpPeerMcp",
                };
            }
            var respMsg = new RemoteBotRespSchema
            {
                ResponseToCmdString = "UCGS",
                ResponseString = "Scan frame returned",
                RemoteMcpName = "AntwerpPeerMcp",
                ImageBase64 = LastScanFrame.ImageBase64,
                UpperCamPan = LastScanFrame.CamPanBearing,
                UpperCamTilt = LastScanFrame.CamTiltAngle
            };
            string errorStr = null;
            SendRemoteResponseMsg(respMsg, ref errorStr);
        }

        public void ReceiveLidarFrameMsg(object sender, McpMessageEventArgs e)
        {
            var jobj = JObject.Parse(e.MessageJson);
            var framePointsStr = (string)jobj["FramePoints"];
            var framePoints = JsonConvert.DeserializeObject<List<LidarPoint>>(framePointsStr);
            if (!DisplayLidarPointCloud || framePoints == null)
                return;


            var frame = new LidarFrame
            {
                Timstamp = DateTime.UtcNow,
                FramePoints = framePoints
            };
            ReceiveLidarFrame?.Invoke(frame);
        }

        public string ProcessStringCommand(string cmdStr)
        {
            string errStr = null;
            string voiceStr;
            if (cmdStr.Length > 1 && cmdStr.Substring(0, 2) == "UC" && !UpperCamOn)
            {
                SendVoiceMsg("Upper camera not available", ref errStr);
                return "Upper camera not enabled";
            }

            switch (cmdStr)
            {
                case "LD":
                    LidarDisplayOn?.Invoke();
                    voiceStr = _inSwearMode ? "Fuck yes! " + BodyMovementRespPhrases[(int)SendBodyMsgs.LidarDispOn] : BodyMovementRespPhrases[(int)SendBodyMsgs.LidarDispOn];
                    SendVoiceMsg(voiceStr, ref errStr);
                    break;
                case "LN":
                    if (!SendLidarScanStart(ref errStr))
                    {
                        LidarScanOn?.Invoke(this, null);
                        SendVoiceMsg(errStr, ref errStr);
                    }
                    else
                    {
                        voiceStr = _inSwearMode ? "Oh shit! " + BodyMovementRespPhrases[(int)SendBodyMsgs.LidarOn] : BodyMovementRespPhrases[(int)SendBodyMsgs.LidarOn];
                        SendVoiceMsg(voiceStr, ref errStr);
                    }
                    break;
                case "LF":
                    LidarDisplayOff?.Invoke();
                    voiceStr = _inSwearMode ? "Holy hell! " + BodyMovementRespPhrases[(int)SendBodyMsgs.LidarDispOff] : BodyMovementRespPhrases[(int)SendBodyMsgs.LidarDispOff];
                    SendVoiceMsg(voiceStr, ref errStr);
                    break;
                case "RN":
                    RngSensorDisplayOn?.Invoke();
                    voiceStr = _inSwearMode ? "Oh, fuck me! " + BodyMovementRespPhrases[(int)SendBodyMsgs.RngDispOn] : BodyMovementRespPhrases[(int)SendBodyMsgs.RngDispOn];
                    SendVoiceMsg(voiceStr, ref errStr);
                    break;
                case "RF":
                    RngSensorDisplayOff?.Invoke();
                    voiceStr = _inSwearMode ? "Oh, bollocks! " + BodyMovementRespPhrases[(int)SendBodyMsgs.RngDispOff] : BodyMovementRespPhrases[(int)SendBodyMsgs.RngDispOff];
                    SendVoiceMsg(voiceStr, ref errStr);
                    break;
                case "SN":
                    _inSwearMode = true;
                    SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.SwearingOn], ref errStr);
                    break;
                case "SF":
                    _inSwearMode = false;
                    SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.SwearingOff], ref errStr);
                    break;
                case "YS":
                    SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.YouSuck], ref errStr);
                    break;
                case "WD":
                    SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.WhoseYourDaddy], ref errStr);
                    break;
                case "WP":
                    SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.WhoseYourProgrammer], ref errStr);
                    break;
                case "WU":
                    if (UpperCamMgr != null)
                        UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamHome, ref errStr);
                    SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.WakeUp], ref errStr);
                    break;
                case "ML":
                    SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.MeaningOfLife], ref errStr);
                    break;
                case "RI":
                    SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.AreYouIntel], ref errStr);
                    break;
                case "UCN":
                    UpperCamDisplayOn?.Invoke();
                    UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamHome, ref errStr);
                    UpperCamPanTiltUpdt?.Invoke();
                    if (VerboseResponses)
                        SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamDispOn], ref errStr);
                    break;
                case "UCF":
                    UpperCamDisplayOff?.Invoke();
                    if (VerboseResponses)
                        SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamDispOn], ref errStr);
                    UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamRelax, ref errStr);
                    UpperCamPanTiltUpdt?.Invoke();
                    break;
                case "UCSP":
                    if (UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamSetPan, ref errStr, UpperCamMgr.NewPanPos))
                    {
                        if (VerboseResponses)
                            SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamSetPan], ref errStr);
                        UpperCamPanTiltUpdt?.Invoke();
                    }
                    else
                    {
                        string voiceErr = null;
                        SendVoiceMsg(errStr, ref voiceErr);
                    }
                    break;
                case "UCST":
                    if (UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamSetTilt, ref errStr, UpperCamMgr.NewTiltPos))
                    {
                        if (VerboseResponses)
                            SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamSetTilt], ref errStr);
                        UpperCamPanTiltUpdt?.Invoke();
                    }
                    else
                    {
                        string voiceErr = null;
                        SendVoiceMsg(errStr, ref voiceErr);
                    }
                    break;
                case "UCPU":
                    if (UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamPanUp10, ref errStr))
                    {
                        if (VerboseResponses)
                            SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamPanUp10], ref errStr);
                        UpperCamPanTiltUpdt?.Invoke();
                    }
                    else
                    {
                        string voiceErr = null;
                        SendVoiceMsg(errStr, ref voiceErr);
                    }
                    break;
                case "UCTU":
                    if (UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamTiltUp10, ref errStr))
                    {
                        if (VerboseResponses)
                            SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamTiltUp10], ref errStr);
                        UpperCamPanTiltUpdt?.Invoke();
                    }
                    else
                    {
                        string voiceErr = null;
                        SendVoiceMsg(errStr, ref voiceErr);
                    }
                    break;
                case "UCPD":
                    if (UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamPanDn10, ref errStr))
                    {
                        if (VerboseResponses)
                            SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamPanDn10], ref errStr);
                        UpperCamPanTiltUpdt?.Invoke();
                    }
                    else
                    {
                        string voiceErr = null;
                        SendVoiceMsg(errStr, ref voiceErr);
                    }
                    break;
                case "UCTD":
                    if (UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamTiltDn10, ref errStr))
                    {
                        if (VerboseResponses)
                            SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamTiltDn10], ref errStr);
                        UpperCamPanTiltUpdt?.Invoke();
                    }
                    else
                    {
                        string voiceErr = null;
                        SendVoiceMsg(errStr, ref voiceErr);
                    }
                    break;
                case "UCR":
                    UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamRelax, ref errStr);
                    UpperCamPanTiltUpdt?.Invoke();
                    break;
                case "UCH":
                    UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamHome, ref errStr);
                    UpperCamPanTiltUpdt?.Invoke();
                    break;
                case "UCGS":
                    RcvdUpperCamCommand?.Invoke(CameraCommands.GrabSnapshot);
                    if (VerboseResponses)
                        SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamGrabSnap], ref errStr);
                    break;
                case "UCRGB":
                    RcvdUpperCamCommand?.Invoke(CameraCommands.RgbStreamOn);
                    if (VerboseResponses)
                        SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamRgbOn], ref errStr);
                    break;
                case "UCDPTH":
                    RcvdUpperCamCommand?.Invoke(CameraCommands.DepthStreamOn);
                    if (VerboseResponses)
                        SendVoiceMsg(BodyMovementRespPhrases[(int)SendBodyMsgs.UpperCamDepthOn], ref errStr);
                    break;
                default:
                    ClearSubsumption();
                    int cmdNdx = Array.IndexOf(BodyMessageStrings, cmdStr);
                    if (cmdNdx == -1)
                        throw new ArgumentException("Invalid body command message string at ReceiveSpeechCommandMsg");
                    LastBodyMsg = (SendBodyMsgs)cmdNdx;
                    SendBodyCmdMessage(cmdStr, ref errStr, true);
                    if (LastBodyMsg == SendBodyMsgs.Forward || LastBodyMsg == SendBodyMsgs.Backward)
                        InRoamMode = true;
                    break;
            }

            return "AntwerpWpf successfully executed: " + cmdStr;
        }

        private void SetArmToNamedPose(string poseName)
        {
            var poseMsg = new ArbotixArmCmdMsg
            {
                AntwerpPoseMsg = "A" + poseName
            };
            SendMsgToMcp("AntwerpArmChannel", "ArbotixArmMcpConnector", poseMsg);
        }

        //private void FilterRangeSensorNoise(BodyRngSensorMessage sensorMessage)
        //{
        //    sensorMessage.Front = CleanSensorVal(RangeSensors.Front, sensorMessage.Front);
        //    sensorMessage.Right = CleanSensorVal(RangeSensors.Right, sensorMessage.Right);
        //    sensorMessage.Rear = CleanSensorVal(RangeSensors.Rear, sensorMessage.Rear);
        //    sensorMessage.Left = CleanSensorVal(RangeSensors.Left, sensorMessage.Left);
        //    sensorMessage.FrontDown = CleanSensorVal(RangeSensors.FrontDown, sensorMessage.FrontDown);
        //    sensorMessage.RearDown = CleanSensorVal(RangeSensors.RearDown, sensorMessage.RearDown);
        //}

        //private int CleanSensorVal(RangeSensors sensor, int sensorHitValue)
        //{
        //    int cleanVal = sensorHitValue;
        //    if (_lastHit[(int)sensor] > 0 && sensorHitValue > FuzzyHitLimit)
        //        cleanVal = _lastHit[(int)sensor];
        //    _lastHit[(int)sensor] = cleanVal;

        //    return cleanVal;
        //}

        private void ProcessRngSnsMsgForRoam(BodyRngSensorMessage sensorMessage)
        {
            if (!InRoamMode)
                return;

            //FilterRangeSensorNoise(sensorMessage);
            bool isFront = false;
            bool isDown = false;
            int roamPhraseNdx = 0;
            bool isBlocked = false;
            if (sensorMessage.Front < RangeSensorDistLimit && LastBodyMsg == SendBodyMsgs.Forward)
            {
                isFront = true;
                isDown = false;
                roamPhraseNdx = 0;
                isBlocked = true;
                if (!_inSubsumption)
                    _lastBlockCmd = SendBodyMsgs.Forward;
            }
            else if (sensorMessage.Rear < RangeSensorDistLimit && LastBodyMsg == SendBodyMsgs.Backward)
            {
                isFront = false;
                isDown = false;
                roamPhraseNdx = 1;
                isBlocked = true;
                if (!_inSubsumption)
                    _lastBlockCmd = SendBodyMsgs.Backward;
            }

            if (isBlocked || _inSubsumption)
            {
                string errStr = null;
                BlockedSubsumption(sensorMessage, isFront, isDown, roamPhraseNdx);
                if (_blockTries > MaxBlockTries)
                {
                    if (!SendVoiceMsg(RoamPivotFail, ref errStr))
                        throw new McpSystemException("Error encountered sending text to speech message.  Error message returned: " + errStr, null);
                    InRoamMode = false;
                    LastBodyMsg = SendBodyMsgs.Stop;
                    ClearSubsumption();
                }
            }
        }

        public void ClearSubsumption()
        {
            _inSubsumption = false;
            _lastBlockCmd = SendBodyMsgs.Forward;
            _blockTries = 0;
        }

        private void BlockedSubsumption(BodyRngSensorMessage sensorMessage, bool isFront, bool isDown, int roamPhraseNdx)
        {
            string errStr = null;

            if (!_inSubsumption)
            {
                _inSubsumption = true;
                if (!SendBodyCmdMessage(SendBodyMsgs.Stop, ref errStr, false))
                    throw new McpSystemException("Error encountered sending body command message.  Error message returned: " + errStr, null);
                Thread.Sleep(200);
                if (!SendVoiceMsg(RoamWarningPhrases[roamPhraseNdx], ref errStr))
                    throw new McpSystemException("Error encountered sending text to speech message.  Error message returned: " + errStr, null);
            }

            if (_lastRngSensorMsg == null)
                return;

            //SendBodyMsgs bodyCmd = sensorMessage.GetBestDirection();
            if (sensorMessage.Front >= RngSensorClearBlockLimit && _lastBlockCmd == SendBodyMsgs.Forward || 
                sensorMessage.Rear >= RngSensorClearBlockLimit && _lastBlockCmd == SendBodyMsgs.Backward)
            {
                if (!SendVoiceMsg(RoamResumePhrase, ref errStr))
                    throw new McpSystemException("Error encountered sending text to speech message.  Error message returned: " + errStr, null);
                if (!SendBodyCmdMessage(SendBodyMsgs.Forward, ref errStr, false))
                    throw new McpSystemException("Error encountered sending body command message.  Error message returned: " + errStr, null);
                ClearSubsumption();
                return;
            }
            else
            {
                _blockTries++;
                SendBodyMsgs bodyCmd = sensorMessage.Right > sensorMessage.Left ? 
                    SendBodyMsgs.PivotRight : SendBodyMsgs.PivotLeft;

                if (!_inSubsumption)
                    return;

                if (!SendBodyCmdMessage(bodyCmd, ref errStr, false))
                    throw new McpSystemException("Error encountered sending body command message.  Error message returned: " + errStr, null);
                Thread.Sleep(1500);
                if (!SendBodyCmdMessage(SendBodyMsgs.Stop, ref errStr, false))
                    throw new McpSystemException("Error encountered sending body command message.  Error message returned: " + errStr, null);
            }
            Thread.Sleep(500);
        }

        private void SetBodyCmdResponse(AntwerpPeerCmdMsg cmdMsg, AntwerpBotRespMsg respMsg, bool isErrorResp)
        {
            //respMsg.MsgIncludesBodyStatusStr = true;
            //if (isErrorResp)
            //{
            //    respMsg.IsErrorResponse = true;
            //    respMsg.BodyStatusString = "Error encountered executing body command message, " + cmdMsg.BodyCommand + " see exception log for details";
            //    return;
            //}
            //respMsg.BodyStatusString = "Received body command '" + cmdMsg.BodyCommand + "'";
        }
    }
}
;

AntwerpWpf Main Dialog code

C#
This is the "code-behind" for the main WPF dialog for the Master Control Program used by Antwerp
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Configuration;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ExceptionLoggerLib;
using Newtonsoft.Json;
using System.Drawing;
using System.Windows.Controls;
using RobbyWpfControls;
using System.Runtime.InteropServices.WindowsRuntime;
using System.IO;
using WpfControlLibs.Rs400VideoViewerLib;
using WpfControlLibs.RobbyCustomWpfCtrlLib;
using ImageProcessingResourceLib;

namespace AntwerpWpf
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private AntwerpMcp _mcp = null;
        private const string GreenLightPath = "GreenLight.png";
        private const string RedLightPath = "RedLight.png";
        private const string OffLightPath = "OffLight.png";
        private const int statusTimeoutSecoinds = 4;
        private int _iters = 0;
        private bool _setAllLights = false;
        private Thread _statusLightSetupThread = null;
        private Thread _videoProcThread = null;
        private AutoResetEvent _statusLightSetupWait = null;
        private ViewModel _viewMdl;
        private CancellationTokenSource _mcpThreadCancelTkn = new CancellationTokenSource();
        private RobbyLidarControl _lidarCtrl;
        private Rs400VideoViewer _rs400VwrCtrl;
        private AzureCustomVisionMgr _azCustVisionMgr;
        private WriteableBitmap _lastSnapshot;

        public MainWindow()
        {
            InitializeComponent();
            lidarCanvas.Visibility = Visibility.Hidden;
            rs400Canvas.Visibility = Visibility.Hidden;
            _lidarCtrl = new RobbyLidarControl();
            Canvas.SetTop(_lidarCtrl, 0);
            Canvas.SetLeft(_lidarCtrl, 0);
            _lidarCtrl.MaxHeight = 600;
            _lidarCtrl.MaxWidth = 800;
            lidarCanvas.Children.Add(_lidarCtrl);
            _lidarCtrl.StartMotor += SendStartMotor;
            _lidarCtrl.StartScan += SendStartScan;
            _lidarCtrl.StopMotor += SendStopMotor;
            _lidarCtrl.StopScan += SendStopScan;
            var netName = ConfigurationManager.AppSettings["NetworkName"];
            _mcp = new AntwerpMcp(netName);
            _mcp.RemoteMcpName = ConfigurationManager.AppSettings["RemoteMcpName"];
            _mcp.ReceiveSensorMessage += OnReceiveSensorMessage;
            _mcp.ReceiveCmdResponse += OnReceiveCmdResponseMsg;
            _mcp.ReceivePwrFdbckMsg += OnRcvPowerFeedbackMsg;
            _mcp.ReceiveRmtPwrPinMsg += OnRemoteSetPowerPowerPin;
            _mcp.ReceiveLidarFrame += _lidarCtrl.PointMgr.ReceiveFramesFromHost;
            _mcp.LidarDisplayOn += OnReceiveLidarDispOnMsg;
            _mcp.LidarDisplayOff += OnReceiveLidarDispOffMsg;
            _mcp.LidarScanOn += SendStartScan;
            _mcp.RngSensorDisplayOn += OnReceiveRangeDispOnMsg;
            _mcp.RngSensorDisplayOff += OnReceiveRangeDispOffMsg;
            _mcp.UpperCamDisplayOn += OnReceiveUpperCamOnMsg;
            _mcp.UpperCamDisplayOff += OnReceiveUpperCamOffMsg;
            _mcp.UpperCamPanTiltUpdt += OnPanTiltUpdate;
            _mcp.RcvdUpperCamCommand += OnUpperCamCommand;
            string camErr = null;
            _mcp.UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamRelax, ref camErr);
            SetPanTiltBoxes();
            SetAllStatusLights();
            //StartMcp();
            _viewMdl = new ViewModel();
            this.DataContext = _viewMdl;
        }

        private void StartMcp()
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    var first = true;
                    while (!_mcpThreadCancelTkn.IsCancellationRequested)
                    {
                        if (first)
                        {
                            var netName = ConfigurationManager.AppSettings["NetworkName"];
                            _mcp = new AntwerpMcp(netName);
                            _mcp.RemoteMcpName = ConfigurationManager.AppSettings["RemoteMcpName"];
                            _mcp.ReceiveSensorMessage += OnReceiveSensorMessage;
                            _mcp.ReceiveCmdResponse += OnReceiveCmdResponseMsg;
                            _mcp.ReceivePwrFdbckMsg += OnRcvPowerFeedbackMsg;
                            _mcp.ReceiveRmtPwrPinMsg += OnRemoteSetPowerPowerPin;
                            _mcp.ReceiveLidarFrame += _lidarCtrl.PointMgr.ReceiveFramesFromHost;
                            _mcp.LidarDisplayOn += TurnLidarDisplayOn;
                            _mcp.LidarDisplayOff += TurnLidarDisplayOff;
                            _mcp.LidarScanOn += SendStartScan;
                            _mcp.RngSensorDisplayOn += TurnRngSensorDisplayOn;
                            _mcp.RngSensorDisplayOff += TurnRngSensorDisplayOff;
                            SetAllStatusLights();
                            first = false;
                        }
                        if (_mcpThreadCancelTkn.IsCancellationRequested)
                        {
                            _mcp.Dispose();
                            _mcp = null;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Dispatcher.Invoke(() => MessageBox.Show(ex.Message));
                }
            }, _mcpThreadCancelTkn.Token);
        }

        private void SetPanTiltBoxes()
        {
            Dispatcher.Invoke(() =>
            {
                if (_mcp == null || _mcp.UpperCamMgr == null)
                {
                    txtCurPan.Text = "0";
                    txtCurTilt.Text = "0";
                    return;
                }
                txtCurPan.Text = _mcp.UpperCamMgr.CurPanPos.ToString();
                txtCurTilt.Text = _mcp.UpperCamMgr.CurTiltPos.ToString();
            });
        }
            

        private void EnablePowerCheckboxes(bool enable)
        {
            Dispatcher.Invoke(() =>
            {
                powerButton1.IsHitTestVisible = enable;
                powerButton2.IsHitTestVisible = enable;
                powerButton3.IsHitTestVisible = enable;
                powerButton4.IsHitTestVisible = enable;
                powerButton5.IsHitTestVisible = enable;
                powerButton6.IsHitTestVisible = enable;
                if (enable)
                    lblInit.Visibility = Visibility.Hidden;
            });
        }

        private BitmapImage GetImageFromPath(string path)
        {
            BitmapImage bitmapImg = new BitmapImage();
            bitmapImg.BeginInit();
            bitmapImg.UriSource = new Uri(path, UriKind.RelativeOrAbsolute);
            bitmapImg.EndInit();

            return bitmapImg;
        }

        public void SendStartMotor(object sender, EventArgs e)
        {
            string errStr = null;
            if (!_mcp.SendLidarMotorStart(ref errStr))
                MessageBox.Show(errStr);
        }

        public void SendStartScan(object sender, EventArgs e)
        {
            string errStr = null;
            if (!_mcp.SendLidarScanStart(ref errStr))
                MessageBox.Show(errStr);
        }

        public void SendStopMotor(object sender, EventArgs e)
        {
            string errStr = null;
            if (!_mcp.SendLidarMotorStop(ref errStr))
                MessageBox.Show(errStr);
        }

        public void SendStopScan(object sender, EventArgs e)
        {
            string errStr = null;
            if (!_mcp.SendLidarScanStop(ref errStr))
                MessageBox.Show(errStr);
        }

        private void OnReceiveSensorMessage(BodyRngSensorMessage msg)
        {
            Dispatcher.Invoke(() =>
            {
                textFront.Text = msg.Front.ToString();
                textRight.Text = msg.Right.ToString();
                textBack.Text = msg.Rear.ToString();
                textLeft.Text = msg.Left.ToString();
                textFrontDown.Text = msg.FrontDown.ToString();
                textBackDown.Text = msg.RearDown.ToString();
            });
        }

        private void OnReceiveCmdResponseMsg(BodyCmdResponseSchema msg)
        {
            //Dispatcher.Invoke(() =>
            //{
            //    textMotorCmdResponse.Text = msg.ResponseMsg;
            //});
        }

        private void OnRcvPowerFeedbackMsg(PowerFeedbackMsg msg)
        {
            var pin = (PowerCircuitsEnum)msg.CircuitDigitalPin;
            var imgPath = OffLightPath;
            if (msg.PinState)
                imgPath = GreenLightPath;
            if (msg.CircuitWarnOn)
                imgPath = RedLightPath;
            SetPinLight(pin, imgPath);
            if (_statusLightSetupWait != null)
                _statusLightSetupWait.Set();
        }

        private void OnRemoteSetPowerPowerPin(int pin, bool state, string switchPropNm)
        {
            string errorStr = null;
            var fltPin = (float)pin;
            float fltTwo = 2;
            int isOdd = 0;
            if (fltPin % fltTwo > 0)
                isOdd = 1;
            int phraseNdx = pin * 2 + isOdd;
            _mcp.SendVoiceMsg(AntwerpMcp.PowerPinSetPhrases[phraseNdx], ref errorStr);
            SetPinState((PowerCircuitsEnum)pin, state);
            var imgPath = state ? "GreenLightPath" : "RedLightPath";
            SetPinLight((PowerCircuitsEnum)pin, imgPath);
            _viewMdl.SetSwitchState(pin, state, switchPropNm);
        }

        private void OnReceiveLidarDispOnMsg()
        {
            Dispatcher.Invoke(() => { chkDispLidarCloud.IsChecked = true; });
        }

        private void OnReceiveLidarDispOffMsg()
        {
            Dispatcher.Invoke(() => { chkDispLidarCloud.IsChecked = false; });
        }

        private void OnReceiveRangeDispOnMsg()
        {
            Dispatcher.Invoke(() => { chkDispRngSensors.IsChecked = true; });
        }

        private void OnReceiveRangeDispOffMsg()
        {
            Dispatcher.Invoke(() => { chkDispRngSensors.IsChecked = false; });
        }

        private void OnReceiveUpperCamOnMsg()
        {
            Dispatcher.Invoke(() => { chkDispRs400Viewer.IsChecked = true; });
        }

        private void OnReceiveUpperCamOffMsg()
        {
            Dispatcher.Invoke(() => { chkDispRs400Viewer.IsChecked = false; });
        }

        private void OnPanTiltUpdate()
        {
            SetPanTiltBoxes();
        }

        private void OnReceiveUpperCamPanSet()
        {
            int panVal;
            if (!int.TryParse(txtCurPan.Text, out panVal))
            {
                MessageBox.Show("Non-numeric pan value");
                return;
            }
            if (!int.TryParse(txtCurPan.Text, out panVal))
            {
                MessageBox.Show("Non-numeric pan value");
                return;
            }

        }

        private void OnReceiveUpperCamUpdtPanTilt()
        {
            if (_mcp == null || _mcp.UpperCamMgr == null)
                txtCurPan.Text = "0";
            txtCurPan.Text = _mcp.UpperCamMgr.CurPanPos.ToString();
        }

        private void OnUpperCamCommand(CameraCommands cmd)
        {
            switch (cmd)
            {
                case CameraCommands.GrabSnapshot:
                    TriggerSnapshot();
                    break;
                case CameraCommands.RgbStreamOn:
                    Dispatcher.Invoke(() =>
                    {
                        _azCustVisionMgr.ClearRectangles();
                        _rs400VwrCtrl.CurStream = 2;
                    });
                    break;
                case CameraCommands.DepthStreamOn:
                    Dispatcher.Invoke(() =>
                    {
                        _azCustVisionMgr.ClearRectangles();
                        _rs400VwrCtrl.CurStream = 1;
                    });
                    break;
            }
        }

        private void SetPinLight(PowerCircuitsEnum pin, string imgPath)
        {
            Dispatcher.Invoke(() =>
            {
                switch (pin)
                {
                    case PowerCircuitsEnum.Dock:
                        imgDockStat.Stretch = Stretch.Fill;
                        imgDockStat.Source = GetImageFromPath(imgPath);
                        _viewMdl.SetSwitchState(0, imgPath == GreenLightPath, "switch1On");
                        break;
                    case PowerCircuitsEnum.Router:
                        imgRouterStat.Stretch = Stretch.Fill;
                        imgRouterStat.Source = GetImageFromPath(imgPath);
                        _viewMdl.SetSwitchState(1, imgPath == GreenLightPath, "switch2On");
                        break;
                    case PowerCircuitsEnum.UsbHub:
                        imgUsbHubStat.Stretch = Stretch.Fill;
                        imgUsbHubStat.Source = GetImageFromPath(imgPath);
                        _viewMdl.SetSwitchState(2, imgPath == GreenLightPath, "switch3On");
                        break;
                    case PowerCircuitsEnum.Body:
                        imgBodyStat.Stretch = Stretch.Fill;
                        imgBodyStat.Source = GetImageFromPath(imgPath);
                        _viewMdl.SetSwitchState(3, imgPath == GreenLightPath, "switch4On");
                        break;
                    case PowerCircuitsEnum.UpperSensors:
                        imgUpperStat.Stretch = Stretch.Fill;
                        imgUpperStat.Source = GetImageFromPath(imgPath);
                        _viewMdl.SetSwitchState(4, imgPath == GreenLightPath, "switch5On");
                        break;
                    case PowerCircuitsEnum.Arm:
                        imgArmStat.Stretch = Stretch.Fill;
                        imgArmStat.Source = GetImageFromPath(imgPath);
                        _viewMdl.SetSwitchState(5, imgPath == GreenLightPath, "switch6On");
                        break;
                }
            });
        }

        private void SetAllStatusLights()
        {
            Dispatcher.Invoke(() =>
            {
                lblInit.Visibility = Visibility.Visible;
                EnablePowerButtons(false);
            });
            EnablePowerCheckboxes(false);
            _statusLightSetupWait = new AutoResetEvent(false);
            _statusLightSetupThread = new Thread(() =>
            {
                int curPin = 0;
                while (curPin < AntwerpMcp.Circuits.Length)
                {
                    SetStatusLight((PowerCircuitsEnum)curPin);
                    _statusLightSetupWait.WaitOne();
                    curPin++;
                }
                SetStatusLight(PowerCircuitsEnum.Dock);
                EnablePowerCheckboxes(true);
                _statusLightSetupWait = null;
                Dispatcher.Invoke(() =>
                {
                    lblInit.Visibility = Visibility.Hidden;
                    EnablePowerButtons(true);
                });
            });
            _statusLightSetupThread.Start();
        }

        private void EnablePowerButtons(bool enable)
        {
            powerButton1.IsEnabled = enable;
            powerButton2.IsEnabled = enable;
            powerButton3.IsEnabled = enable;
            powerButton4.IsEnabled = enable;
            powerButton5.IsEnabled = enable;
            powerButton6.IsEnabled = enable;
        }

        private void SetStatusLight(PowerCircuitsEnum pin)
        {
            var tstNdx = (short)pin;
            string errorStr = null;
            var success = _mcp.GetPinState(tstNdx, ref errorStr);
            //if (!success)
            //    MessageBox.Show("Failed to get pin state, error = " + errorStr);
        }

        private void SetPinState(PowerCircuitsEnum pin, bool onOff)
        {
            string errorStr = null;
            if (!_mcp.SetPinState((short)pin, onOff, ref errorStr))
                MessageBox.Show("Failed to set pin, error = " + errorStr);
        }

        private void TurnRngSensorDisplayOn()
        {
            if (_mcp != null)
                _mcp.DisplayRngSensorRdgs = true;
        }

        private void TurnRngSensorDisplayOff()
        {
            if (_mcp == null)
                return;

            _mcp.DisplayRngSensorRdgs = false;
            Dispatcher.Invoke(() =>
            {
                textFront.Text = "0";
                textRight.Text = "0";
                textBack.Text = "0";
                textLeft.Text = "0";
                textFrontDown.Text = "0";
                textBackDown.Text = "0";
            });
        }

        private void TurnLidarDisplayOn()
        {
            if (_mcp == null)
                return;

            _mcp.DisplayLidarPointCloud = true;
            _lidarCtrl.StartRendering();
            lidarCanvas.Visibility = Visibility.Visible;
        }

        private void TurnVideoDisplayOn()
        {
            if (_rs400VwrCtrl == null)
                LoadRs400Ctrl();

            rs400Canvas.Visibility = Visibility.Visible;
            _rs400VwrCtrl.CurStream = 2;
            string errStr = null;
            _mcp.UpperCamMgr.SendServoAction(SendBodyMsgs.UpperCamHome, ref errStr);
            SetPanTiltBoxes();
            _mcp.UpperCamOn = true;
        }

        private void TurnVideoDisplayOff()
        {
            rs400Canvas.Visibility = Visibility.Collapsed;
            _rs400VwrCtrl.StopStream();
            _mcp.UpperCamOn = false;
        }

        private void TurnLidarDisplayOff()
        {
            if (_mcp == null)
                return;

            _mcp.DisplayLidarPointCloud = false;
            _lidarCtrl.StopRendering();
            lidarCanvas.Visibility = Visibility.Collapsed;
        }

        private void LoadRs400Ctrl()
        {
            if (_rs400VwrCtrl != null)
                return;

            _rs400VwrCtrl = new Rs400VideoViewer();
            _rs400VwrCtrl.Margin = new Thickness(0, 0, 0, 0);
            Canvas.SetTop(_rs400VwrCtrl, 0);
            Canvas.SetLeft(_rs400VwrCtrl, 0);
            _rs400VwrCtrl.MaxHeight = 400;
            _rs400VwrCtrl.Height = 400;
            _rs400VwrCtrl.MaxWidth = 780;
            _azCustVisionMgr = new AzureCustomVisionMgr(_mcp, AntwerpMcp.AzureCustVisionChannelName, AntwerpMcp.AzureCustVisionConnectorName);
            _azCustVisionMgr.CamViewerCtrl = _rs400VwrCtrl;
            _azCustVisionMgr.Margin = new Thickness(0, 0, 0, 0);
            Canvas.SetTop(_azCustVisionMgr, 402);
            Canvas.SetLeft(_azCustVisionMgr, 0);
            _azCustVisionMgr.MaxHeight = 100;
            _azCustVisionMgr.Height = 100;
            _azCustVisionMgr.MaxWidth = 780;
            _azCustVisionMgr.XImageOffset = -0.075d;
            _azCustVisionMgr.YImageOffset = -0.01d;
            _azCustVisionMgr.ImageWidthOffset = -0.02d;
            rs400Canvas.Children.Add(_rs400VwrCtrl);
            rs400Canvas.Children.Add(_azCustVisionMgr);
            _mcp.UpperCamMgr.Rs400Vwr = _rs400VwrCtrl;
        }

        private void BtnStop_Click(object sender, RoutedEventArgs e)
        {
            string errorStr = null;
            _mcp.ClearSubsumption();
            if (!_mcp.SendBodyCmdMessage(SendBodyMsgs.Stop, ref errorStr))
            {
                MessageBox.Show(errorStr);
                return;
            }
            _mcp.InRoamMode = false;
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            if (_setAllLights)
            {
                e.Cancel = true;
                MessageBox.Show("Must finish power channel initialization before closing program");
                return;
            }

            //_mcpThreadCancelTkn.Cancel(false);
            _mcp.Dispose();
            Thread.Sleep(200);
            base.OnClosing(e);
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            Environment.Exit(0);
            Application.Current.Shutdown();
        }

        private void BtnForward_Click(object sender, RoutedEventArgs e)
        {
            string errorMsg = null;
            _mcp.LastBodyMsg = SendBodyMsgs.Forward;
            _mcp.ClearSubsumption();
            if (!_mcp.SendBodyCmdMessage(SendBodyMsgs.Forward, ref errorMsg))
            {
                MessageBox.Show(errorMsg);
                return;
            }
            _mcp.InRoamMode = true;
        }

        private void BtnBack_Click(object sender, RoutedEventArgs e)
        {
            string errorMsg = null;
            _mcp.ClearSubsumption();
            _mcp.LastBodyMsg = SendBodyMsgs.Backward;
            if (!_mcp.SendBodyCmdMessage(SendBodyMsgs.Backward, ref errorMsg))
            {
                MessageBox.Show(errorMsg);
                return;
            }
            _mcp.InRoamMode = true;
        }

        private void BtnPivotRight_Click(object sender, RoutedEventArgs e)
        {
            string errorMsg = null;
            _mcp.LastBodyMsg = SendBodyMsgs.PivotRight;
            _mcp.ClearSubsumption();
            if (!_mcp.SendBodyCmdMessage(SendBodyMsgs.PivotRight, ref errorMsg))
                MessageBox.Show(errorMsg);
        }

        private void BtnPivotLeft_Click(object sender, RoutedEventArgs e)
        {
            string errorMsg = null;
            _mcp.LastBodyMsg = SendBodyMsgs.PivotLeft;
            _mcp.ClearSubsumption();
            if (!_mcp.SendBodyCmdMessage(SendBodyMsgs.PivotLeft, ref errorMsg))
                MessageBox.Show(errorMsg);
        }

        private void PowerButton1_Click(object sender, RoutedEventArgs e)
        {
            if (_setAllLights)
                return;

            SetPinState(PowerCircuitsEnum.Dock, !_viewMdl.SwitchState[0]);
        }

        private void PowerButton2_Click(object sender, RoutedEventArgs e)
        {
            if (_setAllLights)
                return;

            SetPinState(PowerCircuitsEnum.Router, !_viewMdl.SwitchState[1]);
        }

        private void PowerButton3_Click(object sender, RoutedEventArgs e)
        {
            if (_setAllLights)
                return;

            SetPinState(PowerCircuitsEnum.UsbHub, !_viewMdl.SwitchState[2]);
        }

        private void PowerButton4_Click(object sender, RoutedEventArgs e)
        {
            if (_setAllLights)
                return;

            SetPinState(PowerCircuitsEnum.Body, !_viewMdl.SwitchState[3]);
        }

        private void PowerButton5_Click(object sender, RoutedEventArgs e)
        {
            if (_setAllLights)
                return;

            SetPinState(PowerCircuitsEnum.UpperSensors, !_viewMdl.SwitchState[4]);
        }

        private void PowerButton6_Click(object sender, RoutedEventArgs e)
        {
            if (_setAllLights)
                return;

            SetPinState(PowerCircuitsEnum.Arm, !_viewMdl.SwitchState[5]);
        }

        private void ChkLogMessages_Unchecked(object sender, RoutedEventArgs e)
        {
            _mcp.LogAllMessages = false;
        }

        private void ChkLogMessages_Checked(object sender, RoutedEventArgs e)
        {
            _mcp.LogAllMessages = true;
        }

        private void ChkDispRngSensors_Checked(object sender, RoutedEventArgs e)
        {
            TurnRngSensorDisplayOn();
        }

        private void ChkDispRngSensors_Unchecked(object sender, RoutedEventArgs e)
        {
            TurnRngSensorDisplayOff();
        }

        private void ChkDispLidarCloud_Checked(object sender, RoutedEventArgs e)
        {
            if (chkDispRs400Viewer.IsChecked ?? false)
                chkDispRs400Viewer.IsChecked = false;

            TurnLidarDisplayOn();
        }

        private void ChkDispLidarCloud_Unchecked(object sender, RoutedEventArgs e)
        {
            TurnLidarDisplayOff();
        }

        private void ChkDispRs400Viewer_Checked(object sender, RoutedEventArgs e)
        {
            if (chkDispLidarCloud.IsChecked ?? false)
                chkDispLidarCloud.IsChecked = false;

            TurnVideoDisplayOn();
        }

        private void ChkDispRs400Viewer_Unchecked(object sender, RoutedEventArgs e)
        {
            TurnVideoDisplayOff();
        }

        private void TriggerSnapshot()
        {
            Dispatcher.Invoke(() =>
            {
                _rs400VwrCtrl.GrabSnapshot();
                var scanFrame = new ScanFrame
                {
                    FrameTimestamp = DateTime.Now,
                    BotHeading = 0,
                    CamPanBearing = _mcp.UpperCamMgr.CurPanPos,
                    CamTiltAngle = _mcp.UpperCamMgr.CurTiltPos,
                    ImageBase64 = _rs400VwrCtrl.GetSnapFrameAsPngBase64(),
                    ImageFormat = ScanImageFormats.png
                };
                _azCustVisionMgr.CurrentFrame = scanFrame;
                _mcp.LastScanFrame = scanFrame;
                if (_mcp.WaitingForImage)
                    _mcp.ReturnLastScanToRemotePeer();
            });
        }

        private static string TempImgFile = "TestIMage.bmp";

        private void SaveImage()
        {
            byte[] imageBytes = null;
            string base64Str = _rs400VwrCtrl.GetSnapFrameAsBmpBase64();
            //Bitmap bmp;
            //using (MemoryStream outStream = new MemoryStream())
            //{
            //    BitmapEncoder enc = new BmpBitmapEncoder();
            //    enc.Frames.Add(BitmapFrame.Create((BitmapSource)_rs400VwrCtrl.SnapFrame));
            //    enc.Save(outStream);
            //    bmp = new Bitmap(outStream);
            //    imageBytes = outStream.ToArray();
            //}
            //base64Str = Convert.ToBase64String(imageBytes);

            File.WriteAllText(TempImgFile, base64Str);
        }

        private void BtnSetPan_Click(object sender, RoutedEventArgs e)
        {
            int newPan;
            if (!int.TryParse(txtCurPan.Text, out newPan))
            {
                MessageBox.Show("Current Pan Value must be an interger value");
                return;
            }
            var newPanTilt = new PanTiltCommandMsg
            {
                PanPos = newPan,
                TiltPos = _mcp.UpperCamMgr.CurTiltPos
            };
            string limitErr = null;
            if (!_mcp.UpperCamMgr.LimitHitCheck(newPanTilt, ref limitErr))
            {
                MessageBox.Show(limitErr);
                return;
            }
            _mcp.UpperCamMgr.NewPanPos = newPan;
            _mcp.ProcessStringCommand("UCSP");
        }

        private void BtnSetTilt_Click(object sender, RoutedEventArgs e)
        {
            int newTilt;
            if (!int.TryParse(txtCurTilt.Text, out newTilt))
            {
                MessageBox.Show("Current Tilt Value must be an interger value");
                return;
            }
            var newPanTilt = new PanTiltCommandMsg
            {
                PanPos = _mcp.UpperCamMgr.CurPanPos,
                TiltPos = newTilt
            };
            string limitErr = null;
            if (!_mcp.UpperCamMgr.LimitHitCheck(newPanTilt, ref limitErr))
            { 
                MessageBox.Show(limitErr);
                return;
            }
            _mcp.UpperCamMgr.NewTiltPos = newTilt;
            _mcp.ProcessStringCommand("UCST");
        }

        private void BtnPanPlus10_Click(object sender, RoutedEventArgs e)
        {
            var newPanTilt = new PanTiltCommandMsg
            {
                PanPos = _mcp.UpperCamMgr.CurPanPos + 10,
                TiltPos = _mcp.UpperCamMgr.CurTiltPos
            };
            string limitErr = null;
            if (!_mcp.UpperCamMgr.LimitHitCheck(newPanTilt, ref limitErr))
            {
                MessageBox.Show(limitErr);
                return;
            }
            _mcp.ProcessStringCommand("UCPU");
        }

        private void BtnTiltPlus10_Click(object sender, RoutedEventArgs e)
        {
            var newPanTilt = new PanTiltCommandMsg
            {
                PanPos = _mcp.UpperCamMgr.CurPanPos,
                TiltPos = _mcp.UpperCamMgr.CurTiltPos + 10
            };
            string limitErr = null;
            if (!_mcp.UpperCamMgr.LimitHitCheck(newPanTilt, ref limitErr))
            {
                MessageBox.Show(limitErr);
                return;
            }
            _mcp.ProcessStringCommand("UCTU");
        }

        private void BtnPanMinus10_Click(object sender, RoutedEventArgs e)
        {
            var newPanTilt = new PanTiltCommandMsg
            {
                PanPos = _mcp.UpperCamMgr.CurPanPos - 10,
                TiltPos = _mcp.UpperCamMgr.CurTiltPos
            };
            string limitErr = null;
            if (!_mcp.UpperCamMgr.LimitHitCheck(newPanTilt, ref limitErr))
            {
                MessageBox.Show(limitErr);
                return;
            }
            _mcp.ProcessStringCommand("UCPD");
        }

        private void BtnTiltnMinus10_Click(object sender, RoutedEventArgs e)
        {
            var newPanTilt = new PanTiltCommandMsg
            {
                PanPos = _mcp.UpperCamMgr.CurPanPos,
                TiltPos = _mcp.UpperCamMgr.CurTiltPos - 10
            };
            string limitErr = null;
            if (!_mcp.UpperCamMgr.LimitHitCheck(newPanTilt, ref limitErr))
            {
                MessageBox.Show(limitErr);
                return;
            }
            _mcp.ProcessStringCommand("UCTD");
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {

            var base64Str = File.ReadAllText(TempImgFile);

            Byte[] bitmapData = Convert.FromBase64String(base64Str);
            System.IO.MemoryStream streamBitmap = new System.IO.MemoryStream(bitmapData);
            Bitmap bitImage = new Bitmap((Bitmap)System.Drawing.Image.FromStream(streamBitmap));
            streamBitmap.Close();

            using (MemoryStream memory = new MemoryStream())
            {
                bitImage.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
                memory.Position = 0;
                BitmapImage bitmapimage = new BitmapImage();
                bitmapimage.BeginInit();
                bitmapimage.StreamSource = memory;
                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapimage.EndInit();
            }
        }

        private void BtnGrabSnap_Click(object sender, RoutedEventArgs e)
        {
            OnUpperCamCommand(CameraCommands.GrabSnapshot);
        }

        private void BtnColorStrm_Click(object sender, RoutedEventArgs e)
        {
            OnUpperCamCommand(CameraCommands.RgbStreamOn);
            //_azCustVisionMgr.ClearRectangles();
            //_rs400VwrCtrl.CurStream = 2;
        }

        private void BtnDepthStrm_Click(object sender, RoutedEventArgs e)
        {
            OnUpperCamCommand(CameraCommands.DepthStreamOn);
            //_azCustVisionMgr.ClearRectangles();
            //_rs400VwrCtrl.CurStream = 1;
        }
    }
}

Credits

Lee Phillips
1 project • 0 followers
Contact

Comments

Please log in or sign up to comment.