static void Main(string[] args)
        {
            Dictionary <string, dynamic> dict;
            int i;

            // Use program arguments if specified, otherwise use constants.
            // Arguments are in the sequence [[[<master_ip> <slave_ip>] <output_file>] <samples_to_receive]
            string master_ip   = MASTER_IP;
            string slave_ip    = SLAVE_IP;
            string output_file = OUTPUT_FILE;

            if (args.Length >= 2)
            {
                master_ip = args[0];
                slave_ip  = args[1];
            }
            if (args.Length >= 3)
            {
                output_file = args[2];
            }
            if (args.Length >= 4)
            {
                samples_to_receive = Convert.ToInt32(args[3]);
            }

            // Instantiate Socket array.
            sock = new Socket[NumberOfModules];

            // Initialize boundary objects
            LanXIRESTBoundary master = new LanXIRESTBoundary(master_ip);
            LanXIRESTBoundary slave  = new LanXIRESTBoundary(slave_ip);

            // Start measurement
            // During this process commands are generally performed on SLAVEs first, finished with MASTER

            // Set synchronization mode on the LAN-XI modules. The body tells which module is master and which is slave. Body for each module is prepared in SyncParametersMaster.json and SyncParametersSlave.json
            // For this command, the MASTER should be treated first, then the slave(s)
            string syncParametersMaster = File.ReadAllText(@"PTPInputStreamingOutputGenerator_SyncParametersMaster.json");

            master.PutRequestWithPath("/rest/rec/syncmode", syncParametersMaster);
            string syncParametersSlave = File.ReadAllText(@"PTPInputStreamingOutputGenerator_SyncParametersSlave.json");

            slave.PutRequestWithPath("/rest/rec/syncmode", syncParametersSlave);

            // Wait until PTP is locked on all modules
            slave.WaitForPtpState("Locked");
            master.WaitForPtpState("Locked");

            // Open the Recorder application on the modules. The same body is sent to both modules, and is prepared in OpenParameters.json
            string openParameters = File.ReadAllText(@"PTPInputStreamingOutputGenerator_OpenParameters.json");

            slave.PutRequestWithPath("/rest/rec/open", openParameters);
            master.PutRequestWithPath("/rest/rec/open", openParameters);

            // Prepare generator
            string outputChannelStart = File.ReadAllText(@"PTPInputStreamingOutputGenerator_OutputChannelStart.json");

            slave.PutRequestWithPath("/rest/rec/generator/prepare", outputChannelStart);
            master.PutRequestWithPath("/rest/rec/generator/prepare", outputChannelStart);

            // Configure generator channels
            string outputChannelConfiguration = File.ReadAllText(@"PTPInputStreamingOutputGenerator_OutputChannelSetup.json");

            slave.PutRequestWithPath("/rest/rec/generator/output", outputChannelConfiguration);
            master.PutRequestWithPath("/rest/rec/generator/output", outputChannelConfiguration);

            // Start output
            slave.PutRequestWithPath("/rest/rec/generator/start", outputChannelStart);
            master.PutRequestWithPath("/rest/rec/generator/start", outputChannelStart);
            master.PutRequestWithPath("/rest/rec/generator/apply", null);

            // Wait for all modules to be ready; Input in Sampling state, and module in the RecorderOpened state.
            slave.WaitForInputState("Sampling");
            master.WaitForInputState("Sampling");
            slave.WaitForRecorderState("RecorderOpened");
            master.WaitForRecorderState("RecorderOpened");

            // Create Recorder configuration on all modules
            slave.PutRequestWithPath("/rest/rec/create", null);
            master.PutRequestWithPath("/rest/rec/create", null);

            // Wait for all modules to be in the RecorderConfiguring state.
            slave.WaitForRecorderState("RecorderConfiguring");
            master.WaitForRecorderState("RecorderConfiguring");

            // Set a configuration for the input channels. Default configuration can be obtained by sending a GET request to /rest/rec/channels/input/default.
            // The body has been prepared and stored in PTPInputStreamingOutputGenerator_InputChannelSetup.json. In this example the setup is identical for the two modules, but it may differ as needed.
            string inputChannelConfiguration = File.ReadAllText(@"PTPInputStreamingOutputGenerator_InputChannelSetup.json");

            slave.PutRequestWithPath("/rest/rec/channels/input", inputChannelConfiguration);
            master.PutRequestWithPath("/rest/rec/channels/input", inputChannelConfiguration);

            // Wait until all modules enter the Settled input state
            slave.WaitForInputState("Settled");
            master.WaitForInputState("Settled");

            // Synchronize modules.
            slave.PutRequestWithPath("/rest/rec/synchronize", null);
            master.PutRequestWithPath("/rest/rec/synchronize", null);

            // Wait for all modules to enter the Synchronized input state
            slave.WaitForInputState("Synchronized");
            master.WaitForInputState("Synchronized");

            // Start streaming between internally in the LAN-XI modules.
            slave.PutRequestWithPath("/rest/rec/startstreaming", null);
            master.PutRequestWithPath("/rest/rec/startstreaming", null);

            // Wait for all modules to enter the RecorderStreaming state
            slave.WaitForRecorderState("RecorderStreaming");
            master.WaitForRecorderState("RecorderStreaming");

            // Get the TCP ports provided by each LAN-XI module for streaming samples
            dict = master.GetRequestWithPath("/rest/rec/destination/socket");
            UInt16 slavePort = (UInt16)dict["tcpPort"];

            dict = slave.GetRequestWithPath("/rest/rec/destination/socket");
            UInt16 masterPort = (UInt16)dict["tcpPort"];

            // Connect to sockets. Let ConnectStreams() handle this.
            ConnectStreams(new string[] { master_ip, slave_ip }, new UInt16[] { masterPort, slavePort });

            // Start measuring.
            slave.PostRequestWithPath("/rest/rec/measurements", null);
            master.PostRequestWithPath("/rest/rec/measurements", null);

            // Wait for modules to enter RecorderRecording state
            slave.WaitForRecorderState("RecorderRecording");
            master.WaitForRecorderState("RecorderRecording");

            // Streaming should now be running.

            // Let Stream() method handle streaming
            Stream();

            // Stop measuring and close sockets for all modules.
            // During this process commands are generaly performed on MASTER module first, then on SLAVEs

            // Stop measurement on modules
            master.PutRequestWithPath("/rest/rec/measurements/stop", null);
            slave.PutRequestWithPath("/rest/rec/measurements/stop", null);

            // Wait for all modules to enter RecorderStreaming state
            master.WaitForRecorderState("RecorderStreaming");
            slave.WaitForRecorderState("RecorderStreaming");

            // Close sockets
            for (i = 0; i < NumberOfModules; i++)
            {
                sock[i].Shutdown(SocketShutdown.Both);
                sock[i].Close();
            }

            // Finish recording
            master.PutRequestWithPath("/rest/rec/finish", null);
            slave.PutRequestWithPath("/rest/rec/finish", null);

            // Stop output
//            master.PutRequestWithPath("/rest/rec/generator/stop", outputChannelStart);
//            slave.PutRequestWithPath("/rest/rec/generator/stop", outputChannelStart);

            // Wait for modules to enter the RecorderOpened state
            master.WaitForRecorderState("RecorderOpened");
            slave.WaitForRecorderState("RecorderOpened");

            // Close Recorder application on all modules
            master.PutRequestWithPath("/rest/rec/close", null);
            slave.PutRequestWithPath("/rest/rec/close", null);

            // Wait for modules to enter the Idle state
            master.WaitForRecorderState("Idle");
            slave.WaitForRecorderState("Idle");

            // Write collected samples to output file
            StreamWriter file = new StreamWriter(output_file);

            for (int j = 0; j < outputSamples[0, 0].Count; j++)
            {
                file.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}", outputSamples[0, 0][j], outputSamples[0, 1][j], outputSamples[0, 2][j], outputSamples[0, 3][j],
                               outputSamples[1, 0][j], outputSamples[1, 1][j], outputSamples[1, 2][j], outputSamples[1, 3][j]);
            }
            file.Close();
        }