using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;

/* Overview:
 * 1. The best way to automate multiple VNAs connected to the same PC is to install multiple copies of VNA software into different folders.
 * 2. This example is assuming two copies of S2VNA software are installed under:
 *    C:\VNA\S2VNA_copy1\
 *    C:\VNA\S2VNA_copy2\
 * 
 * Before running the code:
 * 1. Make sure two copies of VNA software in installed under C:\VNA\S2VNA_copy1\ and C:\VNA\S2VNA_copy2\
 *    or modify the code with proper directory paths and recompile the code
 * 2. Change the serial numbers to real VNA serial numbers in line 37 and 38
 * 
 *  -- 7/27/2021
 */

namespace Automate_multiple_VNAs_example
{
    internal class Program
    {
        // first copy
        private static TcpClient tcpClient1;
        private static NetworkStream netStream1;
        // second copy
        private static TcpClient tcpClient2;
        private static NetworkStream netStream2;

        private static void Main(string[] args)
        {
            Console.WriteLine("Launching 2 VNA software instances...");
            // launch two VNA software instances, change visible to off if don't want to display User Interface
            Process.Start("C:\\VNA\\S2VNA_copy1\\S2VNA.exe", "/SocketServer:on /SocketPort:5025 /SerialNumber:12345678 /visible:on");
            Process.Start("C:\\VNA\\S2VNA_copy2\\S2VNA.exe", "/SocketServer:on /SocketPort:5026 /SerialNumber:87654321 /visible:on");

            // create client and open the socket
            tcpClient1 = new TcpClient("127.0.0.1", 5025); 
            netStream1 = tcpClient1.GetStream();

            tcpClient2 = new TcpClient("127.0.0.1", 5026); 
            netStream2 = tcpClient2.GetStream();

            // wait for VNAs to connect to the software instances. Time out in 10 seconds
            for (int i = 0; i < 20; i++)
            {
                string is_ready1 = sendReceive(netStream1, tcpClient1, "SYST:READy?");
                string is_ready2 = sendReceive(netStream2, tcpClient2, "SYST:READy?");
                if (is_ready1 == "1\n" && is_ready2 == "1\n") {
                    break;
                }
                if (i == 19) {
                    Console.WriteLine("At least one of the VNAs failed to connect to VNA software. Please check USB connection");
                    Console.WriteLine("Press any key to close...");
                    Console.ReadKey();
                    send(netStream1, tcpClient1, "SYST:TERM");
                    send(netStream2, tcpClient2, "SYST:TERM");
                    return;
                }
                Thread.Sleep(500); // check ready status every half a second
            }

            // query the serial number of the VNAs
            string id1 = sendReceive(netStream1, tcpClient1, "*IDN?");
            string id2 = sendReceive(netStream2, tcpClient2, "*IDN?");
            Console.WriteLine("Socket 5025 is connected to:" + id1);
            Console.WriteLine("Socket 5026 is connected to:" + id2);

            Console.WriteLine("Press any key to close the VNA software instances...");
            Console.ReadKey();
            // --------------------------------------------------------------------------------------------------------
            // terminate both VNA software instances
            send(netStream1, tcpClient1, "SYST:TERM");
            send(netStream2, tcpClient2, "SYST:TERM");

            // close the stream
            netStream1.Close();
            netStream2.Close();
            // close the client
            tcpClient1.Close();
            tcpClient2.Close();
            
            // --------------------------------------------------------------------------------------------------------
            Console.WriteLine("Press any key to close...");
            Console.ReadKey();
            // --------------------------------------------------------------------------------------------------------
        }

        // ============================================================================================================
        // All TCP communicatiuon support functions
        // ============================================================================================================

        private static void send(NetworkStream stream, TcpClient client, string command)

        {
            writeStream(stream, client, command);
        }

        private static string sendReceive(NetworkStream stream, TcpClient client, string command)

        {
            string response = "";
            writeStream(stream, client, command);
            response = readStream(stream, client);
            return response;
        }

        // ============================================================================================================
        private static void writeStream(NetworkStream stream, TcpClient client, string command)

        {
            try
            {
                if (stream.CanWrite)
                {
                    // convert command to bytes
                    byte[] sendBytes = Encoding.UTF8.GetBytes(command + "\n");

                    // write command to stream
                    stream.Write(sendBytes, 0, sendBytes.Length);
                }
                else
                {
                    // close the stream
                    stream.Close();
                    // close the client
                    client.Close();
                }
            }
            catch
            {
            }
        }

        // ============================================================================================================
        private static string readStream(NetworkStream stream, TcpClient client)
        {
            string returnData = "";
            string last_byte_in_string = "";
            if (stream.CanRead)
            {
                // read stream into a byte buffer
                byte[] bytes = new byte[client.ReceiveBufferSize];

                // loop until all data is received from the server
                StringBuilder completeDataStringBuilder = new StringBuilder();

                do
                {
                    // note: this method blocks until at least one byte is read
                    int byteCount = stream.Read(bytes, 0, bytes.Length);

                    // convert data received to string and append it
                    //completeDataStringBuilder.AppendFormat("{0}", Encoding.ASCII.GetString(bytes, 0, byteCount));

                    if (byteCount > 0)  // because of -1, we don't want this to be negative
                    {
                        completeDataStringBuilder.AppendFormat("{0}", Encoding.ASCII.GetString(bytes, 0, byteCount - 1));
                        last_byte_in_string = Encoding.ASCII.GetString(bytes, byteCount - 1, 1);
                        completeDataStringBuilder.AppendFormat("{0}", last_byte_in_string);
                    }
                    else
                    {
                        Thread.Sleep(100);
                    }
                } while (stream.DataAvailable || last_byte_in_string != "\n");
                
                returnData = completeDataStringBuilder.ToString();
                last_byte_in_string = ""; // reset last_byte_in_string
            }
            else
            {
                // close the stream
                stream.Close();
                // close the client
                client.Close();
            }
            return returnData;
        }
    }
}