/// <summary>
        /// This function sends out a FDXDatagram
        /// </summary>
        /// <param name="datagram">the datagram that is to be send</param>
        /// <returns></returns>
        public int Send(ref FDXDatagram datagram)
        {
            //Set the current valid sequence number
            datagram.SetSequenceNumber(this.mNextTransmitSequenceNumber);

            // convert the datagram object to a byte array
            var outData = datagram.GenerateBuffer();
            var res     = this.mSocket.Send(outData, outData.Length);

            // Increment the sequence number for the next datagram
            this.mNextTransmitSequenceNumber = FDXDatagram.IncrementSequenceNumber(this.mNextTransmitSequenceNumber);
            return(res);
        }
        /// <summary>
        /// This function receives a datagram on the socket
        /// </summary>
        /// <param name="datagram">Reference to a datagram to store the information</param>
        /// <returns>size of the received datagram, -1 if no data is received</returns>
        public int Receive(ref FDXDatagram datagram)
        {
            // Get the IPEndpoint
            var ep = new System.Net.IPEndPoint(this.mCanoeAddr, this.mPort);

            try
            {
                if (this.mSocket.Available > 0)
                {
                    datagram.Buffer = this.mSocket.Receive(ref ep);
                }
            }
            catch (System.Net.Sockets.SocketException)
            {
                Console.Out.WriteLine("No answer from CANoe Client");
                return(-1);
            }

            return(datagram.Buffer.Length);
        }
        public void DispatchDatagram(ref FDXDatagram datagram)
        {
            var lCopy = datagram.Buffer;

            //if the received bytes are too short, skip
            if (lCopy.Length < 16)
            {
                this.OnFormatError("Datagram too short");
                return;
            }

            // extract datagram header information
            var header = DatagramHeader.DeSerialize(lCopy);

            //if the signature doesn't match, skip
            if (header.FDXSignature != FDXHelper.kFDXSignature)
            {
                this.OnFormatError("Signature mismatch");
                return;
            }

            //if the major version doesn't match, skip
            if (header.FDXMajorVersion != FDXHelper.kFDXMajorVersion)
            {
                this.OnFormatError("Incorrect Major version");
                return;
            }

            //if the minor version doesn't match, skip
            if (header.FDXMinorVersion != FDXHelper.kFDXMinorVersion)
            {
                this.OnFormatError("Incorrect Minor Version");
                return;
            }

            //checking the sequence number
            if (header.SequenceNumber == FDXHelperSequenceNumber.kSequenceNumberUnused)
            {
                //we don't use sequence numbering
            }
            else
            {
                // check if the received sequence number is the expected
                if ((header.SequenceNumber & 0x7FFF) != this.mNextExpectedSequenceNumber)
                {
                    this.OnSequenceError(this.mNextExpectedSequenceNumber, header.SequenceNumber);
                }
                // if the session end sequence number arrives, reset the expected sequence number
                else if ((header.SequenceNumber & FDXHelperSequenceNumber.kSequenceNumberSessionEndFlag) != 0)
                {
                    this.mNextExpectedSequenceNumber = FDXHelperSequenceNumber.kSequenceNumberStart;
                }
                else
                {
                    // if expected sequence number matches the received sequence number
                    this.mNextExpectedSequenceNumber = FDXDatagram.IncrementSequenceNumber(header.SequenceNumber);
                }
            }

            //extract the number of commands and set the position
            var numOfCommands       = header.NumberOfCommands;
            var commandPosAndLength = DatagramHeader.Size();

            //create a buffer for the single commands
            var data = new byte[lCopy.Length - commandPosAndLength];

            for (var i = 0; i < numOfCommands; ++i)
            {
                //copy command number i into the buffer
                Array.Copy(lCopy, commandPosAndLength, data, 0, lCopy.Length - commandPosAndLength);

                //extract the header information and copy the data from the buffer into the concrete command
                // buffer
                var cmdHead         = CommandHeader.DeSerialize(data);
                var concreteCommand = new byte[cmdHead.CommandSize];
                Array.Copy(data, 0, concreteCommand, 0, concreteCommand.Length);
                commandPosAndLength += concreteCommand.Length;

                // with the command code we can decide what to do with the received command
                switch (cmdHead.CommandCode)
                {
                case FDXHelperCommandCode.kCommandCodeDataExchange:
                {
                    var cmd = DataExchangeCommand.DeSerialize(concreteCommand);
                    this.OnDataExchange(cmd);
                    break;
                }

                case FDXHelperCommandCode.kCommandCodeStatus:
                {
                    var cmd = StatusCommand.DeSerialize(concreteCommand);
                    this.OnStatus(cmd);
                    break;
                }

                case FDXHelperCommandCode.kCommandCodeSequenceNumberError:
                {
                    var com = SequenceNumberErrorCommand.DeSerialize(concreteCommand);
                    this.OnSequenceError(com.ExpectedSeqNr, com.ReceivedSeqNr);
                    break;
                }
                }
            }
        }
        static void Main(string[] args)
        {
            //Register delegate to catch CTRL-C on the console
            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
            {
                e.Cancel     = true;
                sKeepRunning = false;
            };
            Console.Out.WriteLine("The FDX-Client can be terminated with CTRL-C");

            var    addr                  = String.Empty;
            ushort port                  = 0;
            sbyte  cProbCapModifier      = 1;
            var    isFuelConsumingActive = false;

            //setup connection parameters when specified
            if (args.Length == 2)
            {
                addr = args[0];
                port = ushort.Parse(args[1]);
            }

            //Create socket, diagram and Dispatcher
            var fdxSocket   = new FDXSocket();
            var fdxDatagram = new FDXDatagram();
            var disp        = new A429DataDispatcher();

            //Create data packet and initialize with data
            var data = new A429InputData
            {
                FuelTempAndAdvisoryWarning = -2048,
                ProbeCapacitance           = 0
            };

            var ovhdData = new A429OvhdData();

            //Set the new connection settings only when specified
            if (addr != string.Empty)
            {
                fdxSocket.SetCANoeAddr(addr, port);
            }
            fdxSocket.Open();

            //Initialize datagram and set start command
            fdxDatagram.InitWithHeader();
            fdxDatagram.AddStartCommand();

            //Send start command to CANoe
            fdxSocket.Send(ref fdxDatagram);

            // wait one second for initializing CANoe
            Thread.Sleep(1000);

            while (sKeepRunning)
            {
                fdxDatagram.InitWithHeader();

                //Update values only when measurement is running
                if (FDXHelperMeasurementState.CurrentMeasurementState == FDXHelperMeasurementState.kMeasurementStateRunning)
                {
                    if (data.FuelTempAndAdvisoryWarning < 2048)
                    {
                        data.FuelTempAndAdvisoryWarning += 4;
                    }
                    else
                    {
                        data.FuelTempAndAdvisoryWarning = -2048;
                    }
                    if (cProbCapModifier == 1 && data.ProbeCapacitance > 399)
                    {
                        cProbCapModifier = -1;
                    }
                    else if (cProbCapModifier == -1 && data.ProbeCapacitance < 1)
                    {
                        cProbCapModifier = 1;
                    }
                    else
                    {
                        // activate fuel consumption when probe capacitance is over 100
                        if (isFuelConsumingActive == false && data.ProbeCapacitance > 10)
                        {
                            ovhdData.SysMainEng1 = 1;
                            ovhdData.SysMainEng2 = 1;
                            ovhdData.SysMainEng3 = 1;
                            ovhdData.SysMainEng4 = 1;
                            ovhdData.SysTTankL   = 1;
                            ovhdData.SysTTankR   = 1;
                            fdxDatagram.AddDataExchange(A429OvhdData.sGroupId, ovhdData.Serialize());
                            isFuelConsumingActive = true;
                        }
                        // deactivate fuel consumption when probe capacitance is under 100
                        else if (isFuelConsumingActive && data.ProbeCapacitance <= 10)
                        {
                            ovhdData.SysMainEng1 = 0;
                            ovhdData.SysMainEng2 = 0;
                            ovhdData.SysMainEng3 = 0;
                            ovhdData.SysMainEng4 = 0;
                            ovhdData.SysTTankL   = 0;
                            ovhdData.SysTTankR   = 0;
                            fdxDatagram.AddDataExchange(A429OvhdData.sGroupId, ovhdData.Serialize());
                            isFuelConsumingActive = false;
                        }

                        data.ProbeCapacitance += (0.4f * cProbCapModifier);
                    }

                    fdxDatagram.AddDataExchange(A429InputData.sGroupId, data.Serialize());
                    fdxDatagram.AddDataRequest(A429OutputData.sGroupId);
                }

                //poll status whether measurement is not running
                fdxDatagram.AddStatusRequest();
                fdxSocket.Send(ref fdxDatagram);

                //receive and dispatch datagram
                if (fdxSocket.Receive(ref fdxDatagram) > -1)
                {
                    disp.DispatchDatagram(ref fdxDatagram);
                }

                // sleep 250ms for next interval
                Thread.Sleep(250);
            }

            //Wait one second before shutting down CANoe
            Thread.Sleep(1000);

            //Init and send stop command
            fdxDatagram.InitWithHeader();
            fdxDatagram.AddStopCommand();
            fdxSocket.Send(ref fdxDatagram);
        }