//  This is where we get responses from the TCP Client helper class.  This is where we handle the events that are raised
        //  This is a CUSTOM class.  how we interact with it was defined by the programmer.  This is not a Crestron Class!
        //  DO NOT assume this is how you use a Crestron class.   Look at the Sourcecode for that class to see how to interact with a
        //  TCP Client directly.
        private void MyClient_tcpHelperEvent(object sender, TCPClientHelperEventArgs e)
        {
            // Send a string out our Virtual Console for display
            VirtualConsole.Send(String.Format("TCPIPClient Event Message={0}", e.Message), true);

            // e.Message will contain keywords telling us what event was raised
            // STATUS = Status change   RX = data was received  FAIL= connection failed
            if (e.Message == "STATUS")
            {
                // Connected is a boolean  True = connected, False = disconnected
                // We leverage the NOT ! operator here to make our feedback correct.
                myXpanel.BooleanInput[20].BoolValue = e.Connected;  //Connected Feedback join
                myXpanel.BooleanInput[21].BoolValue = !e.Connected; //Disconnected Feedback join
            }

            if (e.Message == "RX")                                         // We have data waiting to be processed that is in e.RX
            {
                VirtualConsole.Send(String.Format("RX Text = {0}", e.RX)); // Print out what came in for debugging

                ProjProcessing(e.RX);                                      // Call our method we created below to process
                                                                           // the received data from the projector.
                                                                           // This method is at the bottom of this file
            }

            myXpanel.StringInput[2].StringValue = e.RX;   // display on our touch panel any received data
        }
        /***********************************************************************************************
        * Session 3  File loading and display and sorting  methods
        ***********************************************************************************************/

        private void LoadFile()
        {
            // Do not forget to add the Masters2021() class instantiation at the top to get the file created for students.
            // Challenge:  What if the file was not there? How can we check if it exists before we try to open it?
            //             Add the code to check if the file exists at that location only only  open and read it  if it does.
            var path = Directory.GetApplicationRootDirectory() + "/user/names.txt";

            VirtualConsole.Send(String.Format("Opening file {0}", path));

            // We should check to see if the file was actually able to be opened and read sucessfully and report back if it was unable to.
            // Add code here to make this fail gracefully if it was unable to open the file or read from it.

            var file   = new FileStream(path, FileMode.Open);
            var stream = new StreamReader(file);

            VirtualConsole.Send("File Opened");  // If the above crashed by throwing an exception,  this will not execute.

            // C# arrays are zero based, we have index 0 to 9 available
            var i = 0;

            while (!stream.EndOfStream)       // Loop until we get to the end of the file
            {
                Names[i] = stream.ReadLine(); // Load the line from the file into the array
                i++;                          // Increment our index variable
            }
            stream.Close();                   // Extremely important. Don not forget to close these
            file.Close();

            VirtualConsole.Send(String.Format("Read {0} entries", i));

            // We hardcoded the number of entries below,  this is NOT a best practice in coding.  we should know how many we read and
            // all processing from that point on should be based on the amount of data that was read.  This also means using a hard array is
            // not the right answer.    As an exercise, investigate what other storage method that can change based on data read in could be
            // used instead of an array.
            // Hint: it's not a dictionary.

            for (i = 0; i < 10; i++)                                         // Loop to load the array into the touch panel strings
            {
                myXpanel.StringInput[(uint)(30 + i)].StringValue = Names[i]; // (uint) casts the integers to unsigned integers
            }
        }
        // This is how you create a Virtual Console Command Method: This is a freebie that is not a part of the Masters Session
        private string TestFunc(string s)
        {
            VirtualConsole.Send(string.Format("Test Command line Called and  after it was sent {0}", s));

            return("");  //return an empty string.
        }
        /*****************************************************************************************************
        *  Session 1 Touchpanel Events setup, adding  page flips and basic logic
        *****************************************************************************************************/

        //  This is where our Touchpanel Signal changes are processed.   Digitals, Analogs, and Serials All end up here.
        private void MyXpanel_SigChange(BasicTriList currentDevice, SigEventArgs args)
        {
            VirtualConsole.Send(String.Format("TP Sig Change  Type={0} Number={1} State={2}",
                                              args.Sig.Type, args.Sig.Number, args.Sig.BoolValue), true); // Question: What is all this? What does {0} mean?
                                                                                                          // and yes a line can be on 2 lines
                                                                                                          // Answer: Look up String.Format and read up on the specifier string.  This is like the Trace and MakeString in Simpl Plus.

            // Important to split it like this for more flexibility.   Check if it's a digital, then anything below is for digitals
            if (args.Sig.Type == eSigType.Bool) // Is it a digital?
            {
                //SESSION 1: Create a Momentary Button  (This has to be outside the check if true below)
                if (args.Sig.Number == 11)
                {
                    // This passes the button events back to the feedback
                    myXpanel.BooleanInput[11].BoolValue = args.Sig.BoolValue;  // we want both the press and release here
                }

                //SESSION 1:  Check if it is pressed
                if (args.Sig.BoolValue == true) // Look for only pressed button events
                {
                    // Page Navigation                  Session 1: Instructor put in the first page nav code The rest are student lab
                    switch (args.Sig.Number)
                    {
                    case 1:
                        myXpanel.BooleanInput[1].BoolValue  = true;     // Traditional way of setting it high then low
                        myXpanel.BooleanInput[1].BoolValue  = false;
                        myXpanel.StringInput[1].StringValue = "Front Page";
                        break;

                    case 2:
                        myXpanel.BooleanInput[2].Pulse();      // using the built in method to pulse it
                        myXpanel.StringInput[1].StringValue = "Projector";
                        break;

                    case 3:
                        myXpanel.BooleanInput[3].Pulse();
                        myXpanel.StringInput[1].StringValue = "Phone Book";
                        break;
                    }

                    /******************************************************************************************
                    *   Session 1 Lab work below (Momentary is above outside the "true" check for boolvalue)
                    ******************************************************************************************/

                    // Create a Toggle Button
                    if (args.Sig.Number == 10)
                    {
                        // We read the current feedback and use the compliment or NOT by using ! to toggle between states
                        myXpanel.BooleanInput[10].BoolValue = !myXpanel.BooleanInput[10].BoolValue; // Invert The Feedback
                    }

                    // Interlock with simple Logic. Pay attention to the join numbers used

                    /*
                     * switch(args.Sig.Number)
                     * {
                     *  case 12:
                     *      myXpanel.BooleanInput[13].BoolValue = false;
                     *      myXpanel.BooleanInput[14].BoolValue = false;
                     *      myXpanel.BooleanInput[12].BoolValue = true;
                     *      break;
                     *
                     *  case 13:
                     *      myXpanel.BooleanInput[12].BoolValue = false;
                     *      myXpanel.BooleanInput[14].BoolValue = false;
                     *      myXpanel.BooleanInput[13].BoolValue = true;
                     *      break;
                     *
                     *  case 14:
                     *      myXpanel.BooleanInput[12].BoolValue = false;
                     *      myXpanel.BooleanInput[13].BoolValue = false;
                     *      myXpanel.BooleanInput[14].BoolValue = true;
                     *      break;
                     * }
                     */

                    //Interlock leveraging the power of a method
                    if (args.Sig.Number >= 12 && args.Sig.Number <= 14)            // We only want joins 12,13,14
                    {
                        Interlock(myXpanel.BooleanInput, 12, 14, args.Sig.Number); // Look for this custom method below
                    }

                    /**********************************************************************************************
                    *  Session 2
                    **********************************************************************************************/


                    // ####     Projector Button Press Logic
                    // ####     All of this SHOULD exist in a method or even another class to make the code more manageable and organized
                    //
                    //          We are still inside the pair of IF statements for "was this a boolean(digital) and was it true(pressed)
                    //          Nested If statements can get complex and hard to navigate quickly.

                    ProjectorButtonPress(args.Sig.Number);  // Call our method below

                    //  Hidden Challenge:   Make the projector Connect when it is on the projector Page,
                    //                      and Disconnect when the user leaves the projector page.

                    // Second hidden Challenge: Do not use join numbers but instead use an enum with names
                    //                          so you can easily remap to different buttons

                    /********************************************************************************************
                    *  Session 3
                    ********************************************************************************************/

                    // File Read and Sort button management  Methods start down near line 370
                    switch (args.Sig.Number)
                    {
                    case 30:
                        LoadFile();      // Call our method to load the file and display the contents
                        break;

                    case 31:
                        SortFile();      // Call our method to sort the strings and present them
                        break;
                    }
                }
            }
        }