Пример #1
0
        /// <summary>
        /// Send an acknowledgement message.
        /// </summary>
        /// <param name="a_rowNr">The row number that should be acknowledged.</param>
        private void sendAck(uint a_rowNr)
        {
            CANMessage msg = new CANMessage(0x266, 0, 5);

            msg.elmExpectedResponses = (a_rowNr == 0)?0:1;
            uint  i    = 0;
            ulong data = 0;

            data = setCanData(data, (byte)0x40, i++);
            data = setCanData(data, (byte)0xA1, i++);
            data = setCanData(data, (byte)0x3F, i++);
            data = setCanData(data, (byte)(0x80 | (int)(a_rowNr)), i++);
            msg.setData(data);
            if (!m_canDevice.sendMessage(msg))
            {
                throw new Exception("Error sending ack");
            }
        }
Пример #2
0
        /// <summary>
        /// Start a KWP session.
        /// </summary>
        /// <remarks>
        /// A KWP session must be started before any requests can be sent.
        /// </remarks>
        /// <returns>True if the session was started, otherwise false.</returns>
        public bool startSession()
        {
            CANMessage msg = new CANMessage(0x220, 0, 8);

            msg.setData(0x000040021100813F);
            AddToCanTrace("Sending 0x000040021100813F message");

            m_kwpCanListener.setupWaitMessage(0x238);

            if (!m_canDevice.sendMessage(msg))
            {
                AddToCanTrace("Unable to send 0x000040021100813F message");
                return(false);
            }
            Console.WriteLine("Init msg sent");
            if (m_kwpCanListener.waitMessage(timeoutPeriod).getID() == 0x238)
            {
                AddToCanTrace("Successfully sent 0x000040021100813F message and received reply 0x238");
                return(true);
            }
            else
            {
                AddToCanTrace("Didn't receive 0x238 message as reply on 0x000040021100813F message");
                return(false);
            }

/*          if (!m_canDevice.sendMessage(msg))
 *          {
 *              AddToCanTrace("Unable to send 0x000040021100813F message");
 *              return false;
 *          }
 *          Console.WriteLine("Init msg sent");
 *          if (m_kwpCanListener.waitForMessage(0x238, timeoutPeriod).getID() == 0x238)
 *          {
 *              AddToCanTrace("Successfully sent 0x000040021100813F message and received reply 0x238");
 *              return true;
 *          }
 *          else
 *          {
 *              AddToCanTrace("Didn't receive 0x238 message as reply on 0x000040021100813F message");
 *              return false;
 *          }
 */
        }
Пример #3
0
        public void BackgroundRun(bool IsUserAbort)
        {
            Executor(IsUserAbort);

            CANMessage[] canMessages = new CANMessage[32];
            int          num         = CANDevice.Receive(CANPortIndex, canMessages);

            for (int i = 0; i < num; i++)
            {
                if (IsShowSendRecive)
                {
                    WriteLine("Receive:" + canMessages[i].ToString());
                }
                Executor(canMessages[i]);
                Executor();
            }

            Executor();
        }
Пример #4
0
 private void StartFirmwareUpdateProcedure(string deviceVersion)
 {
     if (ForceUpdate || deviceVersion != FirmwareFileVersion)
     {
         Console.WriteLine("Starting update to {0}", FirmwareFileVersion);
         var canMessage = new CANMessage(BuildCANId(CANMaerklinCommand.Reset, CS2AddressHash), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x80, 0xff });
         Array.Copy(CurrentDeviceId.HexStringToByteArray(), 0, canMessage.Data, 0, 4);
         serialCAN.SendCAN(canMessage);
         /* delay for boot ? */
         Thread.Sleep(500);
         serialCAN.SendCAN(new CANMessage(BuildCANId(CANMaerklinCommand.Bootloader, CS2AddressHash), new byte[0]));
     }
     else
     {
         Console.WriteLine("Firmware already installed.");
         UpdateCompletedAutoResetEvent.Set();
         UpdateRunning = false;
     }
 }
Пример #5
0
        /// <summary>
        /// Send a message that starts a session. This is used to test if there is
        /// a connection.
        /// </summary>
        /// <returns></returns>
        private bool sendSessionRequest()
        {
            CANMessage msg1 = new CANMessage(0x220, 0, 8);

            LAWICEL.CANMsg msg = new LAWICEL.CANMsg();
            msg1.setData(0x000040021100813f);

            if (!sendMessage(msg1))
            {
                return(false);
            }
            if (waitForMessage(0x238, 1000, out msg) == 0x238)
            {
                //Ok, there seems to be a ECU somewhere out there.
                //Now, sleep for 10 seconds to get a session timeout. This is needed for
                //applications on higher level. Otherwise there will be no reply when the
                //higher level application tries to start a session.
                Thread.Sleep(10000);
                return(true);
            }
            return(false);
        }
Пример #6
0
        //-------------------------------------------------------------------------

        /**
         *  Waits for arrival of a specific CAN message or any message if ID = 0.
         *
         *  @param      a_canID     message ID
         *  @param      timeout     timeout, ms
         *  @param      canMsg      message
         *
         *  @return                 message ID
         */
        public uint waitForMessage(uint a_canID, uint timeout,
                                   out CANMessage canMsg)
        {
            canMsg = new CANMessage();
            Debug.Assert(canMsg != null);
            canMsg.setID(0);

            caCombiAdapter.caCANFrame frame = new caCombiAdapter.caCANFrame();
            if (this.combi.CAN_GetMessage(ref frame, timeout) &&
                (frame.id == a_canID || a_canID == 0))
            {
                // message received
                canMsg.setID(frame.id);
                canMsg.setLength(frame.length);
                canMsg.setData(frame.data);

                return(frame.id);
            }

            // timed out
            return(0);
        }
Пример #7
0
        /// <summary>
        /// readMessages is the "run" method of this class. It reads all incomming messages
        /// and publishes them to registered ICANListeners.
        /// </summary>
        public void readMessages()
        {
            int readResult = 0;

            LAWICEL.CANMsg r_canMsg   = new LAWICEL.CANMsg();
            CANMessage     canMessage = new CANMessage();

            while (true)
            {
                lock (m_synchObject)
                {
                    if (m_endThread)
                    {
                        return;
                    }
                }
                readResult = LAWICEL.canusb_Read(m_deviceHandle, out r_canMsg);
                if (readResult == LAWICEL.ERROR_CANUSB_OK)
                {
                    canMessage.setID(r_canMsg.id);
                    canMessage.setLength(r_canMsg.len);
                    canMessage.setTimeStamp(r_canMsg.timestamp);
                    canMessage.setFlags(r_canMsg.flags);
                    canMessage.setData(r_canMsg.data);
                    lock (m_listeners)
                    {
                        foreach (ICANListener listener in m_listeners)
                        {
                            listener.handleMessage(canMessage);
                        }
                    }
                }
                else if (readResult == LAWICEL.ERROR_CANUSB_NO_MESSAGE)
                {
                    Thread.Sleep(1);
                }
            }
        }
Пример #8
0
 public CanFrameEventArgs(CANMessage message)
 {
     _message = message;
 }
Пример #9
0
        // ##############################################################################
        static public int GetCANMess(byte InterfaceNum, byte DeviceNum, ref CANMessage Msg)
        {
            byte[] USBOutBuf = new byte[30];
            byte[] USBInBuf  = new byte[100];
            int    Result;


            if (InterfaceNum > 9)
            {
                return(Resp_ErrImproperCallingParameter);
            }
            if (!ClassInit[InterfaceNum])
            {
                Result = Init(InterfaceNum);
                if (Result == Resp_ErrNoInterfacePresent)
                {
                    return(Result);
                }
            }

            USBOutBuf[0] = DeviceNum;
            USBOutBuf[1] = (byte)LLCMD.GETCANMESREQ;

            Result = USBRawCommand(InterfaceNum, USBOutBuf, 2, ref USBInBuf);

            if (Result < 0)
            {
                return(Result);                  // return error
            }
            if ((int)USBInBuf[0] != (int)LLCMD.GETCANMESRESP)
            {
                return(Resp_ErrImproperResponse);                                                    //improper response from node
            }
            if ((int)USBInBuf[1] == 0)
            {
                return(Resp_NAck);
            }

            Msg.DLC        = (byte)(USBInBuf[4] & 0x0F);
            Msg.MO         = ((USBInBuf[1] & 0x10) > 0) ? (byte)1 : (byte)0; // two message objects defined by 0x10 bit
            Msg.ID         = ((long)(USBInBuf[6] & 0x3F)) << 24;             // lose the top two bits
            Msg.ID        |= ((long)USBInBuf[7] << 16);
            Msg.ID        |= ((long)USBInBuf[8] << 8);
            Msg.ID        |= (long)USBInBuf[9];
            Msg.EID        = ((USBInBuf[5] & 0x80) > 0) ? true : false; // extended ID
            Msg.RTR        = ((USBInBuf[5] & 0x40) > 0) ? true : false; // RTR true
            Msg.BOF        = ((USBInBuf[5] & 0x20) > 0) ? true : false; // Buffer Overflow
            Msg.SendOption = 0;

            for (int c = 0; c < Msg.DLC; c++)
            {
                Msg.Data[c] = USBInBuf[c + 10];
            }

            if (USBInBuf[3] == (Msg.DLC + 10))                          // timestamp
            {
                Msg.TimeStamp  = (long)USBInBuf[Msg.DLC + 10] * .524288;
                Msg.TimeStamp += ((long)USBInBuf[Msg.DLC + 11] * .002048);
                Msg.TimeStamp += ((long)USBInBuf[Msg.DLC + 12] * .000008);
            }
            else
            {
                Msg.TimeStamp = 0;
            }

            return(Resp_Ack);
        }
Пример #10
0
        // ##############################################################################
        // Error Int is returned with the two byte values from the MCP2515, the highest byte
        // is from the TXBnCRTL register and the lower byte is the TEC
        // Option variable, lower 4 bits, defines message priority (lowest 2 bits) and Txwait bit (0x04 bit)
        static public int SendCANMess(byte InterfaceNum, byte DeviceNum, CANMessage Msg, out int ErrorInt)
        {
            byte[] USBOutBuf = new byte[30];
            byte[] USBInBuf  = new byte[100];
            int    Result;

            ErrorInt = 0;


            if (Msg.DLC > 8)
            {
                return(Resp_ErrImproperCallingParameter);
            }
            if (Msg.MO > 2)
            {
                return(Resp_ErrImproperCallingParameter);
            }

            if (InterfaceNum > 9)
            {
                return(Resp_ErrImproperCallingParameter);
            }
            if (!ClassInit[InterfaceNum])
            {
                Result = Init(InterfaceNum);
                if (Result == Resp_ErrNoInterfacePresent)
                {
                    return(Result);
                }
            }

            USBOutBuf[0] = DeviceNum;
            USBOutBuf[1] = (byte)LLCMD.SENDCANREQ;
            USBOutBuf[2] = (byte)(Msg.ID >> 24);
            USBOutBuf[3] = (byte)(Msg.ID >> 16);
            USBOutBuf[4] = (byte)(Msg.ID >> 8);
            USBOutBuf[5] = (byte)Msg.ID;
            if (Msg.EID)
            {
                USBOutBuf[2] |= 0x80;              // set highest bit high for extended ID messages
            }
            if (Msg.RTR)
            {
                USBOutBuf[2] |= 0x40;                             // set next highest bit high for RTR messages
            }
            USBOutBuf[6] = (byte)(Msg.SendOption & 0x07);         // transmit wait and priority bits
            USBOutBuf[7] = (byte)((byte)(Msg.MO << 4) | Msg.DLC); // Tx buffer to send and DLC

            for (int c = 0; c < Msg.DLC; c++)
            {
                USBOutBuf[c + 8] = Msg.Data[c];
            }

            Result = USBRawCommand(InterfaceNum, USBOutBuf, Msg.DLC + 8, ref USBInBuf);

            if (Result < 0)
            {
                return(Result);                             // return error
            }
            if ((int)USBInBuf[0] != (int)LLCMD.SENDCANRESP)
            {
                return(Resp_ErrImproperResponse);                                              //improper response from node
            }
            if (((int)USBInBuf[0] == (int)LLCMD.SENDCANRESP) && ((int)USBInBuf[1] == Resp_Ack))
            {
                return(Resp_Ack);  // success
            }
            else
            {
                ErrorInt  = (USBInBuf[2] << 8); // first error code
                ErrorInt |= USBInBuf[3];        // second error code
            }
            return(Resp_NAck);
        }
Пример #11
0
        private CANMessageData BuildCANMessageData(string MsgIdentifier, long TimeValue)
        {
            CANMessage oMsgDef = oCanConfig.GetCANMessage(MsgIdentifier, MessageResearchOption.Identifier);

            //Get engeneering value for each parameter of the message
            foreach (CANParameter oParamDef in oMsgDef.Parameters)
            {
                DataAssociation oParamAssociation = oAssociations.GetAssociation(MsgIdentifier, oParamDef.Name);

                double DataValue = 0;

                //Retrieve formated value
                if (!(oParamAssociation == null))
                {
                    switch (oParamAssociation.AssociationType)
                    {
                    case CS_DataAssociationType.AcqData:

                        if (!(oDataFile.GetValueAtTime(oParamAssociation.DataColumnTitle, TimeValue, out DataValue)))
                        {
                            return(null);
                        }
                        break;

                    case CS_DataAssociationType.BuiltSignal:

                        DataValue = oSignalLibCollection.GetSignalValueAtTime(oParamAssociation.SignalLibrary, oParamAssociation.SignalName, TimeValue);
                        break;

                    case CS_DataAssociationType.FixedValue:

                        DataValue = oParamAssociation.DefaultValue;
                        break;

                    default:

                        DataValue = 0;
                        break;
                    }
                }
                else
                {
                    if (TimeValue == 0)
                    {
                        DialogResult Rep = MessageBox.Show("No association found for CAN parameter " + oParamDef.Name
                                                           + " of message ID " + MsgIdentifier
                                                           + "\nDo you want to continue cycle creation anyway ?",
                                                           Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

                        if (Rep.Equals(DialogResult.No))
                        {
                            MessageBox.Show("Cycle creation abort !", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
                            return(null);
                        }
                    }
                }

                oParamDef.DecodedValue = DataValue;
            }

            //Encode the message
            CANMessageEncoded oMsgEncoded = new CANMessageEncoded(oMsgDef);

            oMsgEncoded.EncodeMessage();

            return(oMsgEncoded.GetCANMessageData());
        }
Пример #12
0
        //Executor Process Message
        public void Executor(CANMessage CANMessage)
        {
            lock (this)
            {
                if (!IsRunning)
                {
                    return;
                }

                if (CANMessage.ExternFlag)
                {
                    return;
                }

                Job j = JobRunning;

                //ACK
                if (j.Type == Job.JobType.ACK)
                {
                    if (_StepRunning == 1)
                    {
                        if (CheckCANMessage(CANMessage, ID_ACK, DA_ACK))
                        {
                            RunningNext();
                        }
                        else if (CheckCANMessage(CANMessage, ID_ACK, DA_NACK))
                        {
                            RunningNext();
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                }
                //READ
                else if (j.Type == Job.JobType.Read)
                {
                    if (CheckCANMessage(CANMessage, ID_READ, DA_NACK))
                    {
                        RunningNext(JobEventType.NACK, "NACK");
                    }

                    int m = (int)Math.Ceiling(j.DataNum / 8f);
                    if (_StepRunning == 1)//ACK
                    {
                        if (CheckCANMessage(CANMessage, ID_READ, DA_ACK))
                        {
                            _StepRunning++;
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                    else if (_StepRunning >= 2 && _StepRunning <= m + 1)//DATA
                    {
                        if (CheckCANMessage(CANMessage, ID_READ) && CANMessage.DataLen > 0)
                        {
                            if (_StepRunning == 2)
                            {
                                j.DataReceive = new byte[j.DataNum];
                            }
                            Array.Copy(CANMessage.Data, 0, j.DataReceive, (_StepRunning - 2) * CANMessage.DataLenMax, CANMessage.DataLen);
                            _StepRunning++;
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                    else if (_StepRunning == m + 2)//ACK
                    {
                        if (CheckCANMessage(CANMessage, ID_READ, DA_ACK))
                        {
                            RunningNext();
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                    else
                    {
                        TimeoutCheck();
                    }
                }
                //WRITE
                else if (j.Type == Job.JobType.Write)
                {
                    if (CheckCANMessage(CANMessage, ID_WRITE, DA_NACK))
                    {
                        RunningNext(JobEventType.NACK, "NACK");
                    }

                    int m = (int)Math.Ceiling(j.DataSend.Length / 8f);
                    if (_StepRunning == 2 * m + 2)
                    {
                        if (CheckCANMessage(CANMessage, ID_WRITE, DA_ACK))
                        {
                            RunningNext();
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                    else if (_StepRunning % 2 == 1 && _StepRunning <= 2 * m + 1)
                    {
                        if (CheckCANMessage(CANMessage, ID_WRITE, DA_ACK))
                        {
                            _StepRunning++;
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                }
                //ERASE
                else if (j.Type == Job.JobType.Erase)
                {
                    if (CheckCANMessage(CANMessage, ID_ERASE, DA_NACK))
                    {
                        RunningNext(JobEventType.NACK, "NACK");
                    }

                    if (_StepRunning == 1 || _StepRunning == 3)
                    {
                        if (CheckCANMessage(CANMessage, ID_ERASE, DA_ACK))
                        {
                            _StepRunning++;
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                    else if (_StepRunning == 4)
                    {
                        if (CheckCANMessage(CANMessage, ID_ERASE, DA_ACK))
                        {
                            RunningNext();
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                }
                //GV
                else if (j.Type == Job.JobType.GetState)
                {
                    if (CheckCANMessage(CANMessage, ID_GV, DA_NACK))
                    {
                        RunningNext(JobEventType.NACK, "NACK");
                    }

                    if (_StepRunning == 1)
                    {
                        if (CheckCANMessage(CANMessage, ID_GV, DA_ACK))
                        {
                            _StepRunning++;
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                    else if (_StepRunning == 2)
                    {
                        if (CheckCANMessage(CANMessage, ID_GV, 1))
                        {
                            j.DataReceive    = new byte[3];
                            j.DataReceive[0] = CANMessage.Data[0];
                            _StepRunning++;
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                    else if (_StepRunning == 3)
                    {
                        if (CheckCANMessage(CANMessage, ID_GV, 2))
                        {
                            j.DataReceive[1] = CANMessage.Data[0];
                            j.DataReceive[2] = CANMessage.Data[1];
                            _StepRunning++;
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                    else if (_StepRunning == 4)
                    {
                        if (CheckCANMessage(CANMessage, ID_GV, DA_ACK))
                        {
                            RunningNext();
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                }
                //GO
                else if (j.Type == Job.JobType.Go)
                {
                    if (CheckCANMessage(CANMessage, ID_GO, DA_NACK))
                    {
                        RunningNext(JobEventType.NACK, "NACK");
                    }

                    if (_StepRunning == 1)
                    {
                        if (CheckCANMessage(CANMessage, ID_GO, DA_ACK))
                        {
                            RunningNext();
                        }
                        else
                        {
                            ErrorWriteLine("Unknow Message:" + CANMessage.ToString());
                        }
                    }
                }
                else
                {
                    ErrorWriteLine("Unsupport Job:" + j.Type.ToString());
                }
            }
        }
Пример #13
0
        /// <summary>
        /// Decodes the PCAN trace file CAN messages in raw format into the engineering format according to the specified CAN Configuration
        /// </summary>
        /// <returns>Decoding result (True: OK / False: Error)</returns>
        private bool DecodeTrcFile()
        {
            bool bComputeVirtuals = false;
            long iRecord          = 0;

            if (!(VCLibraries == null))
            {
                VCLibraries.InitLibrariesComputation();
                bComputeVirtuals = true;
            }

            foreach (TraceRecord Record in Records)
            {
                iRecord++;

                if (oCanConfig.IsUsedIdentifier(Record.MessageIdentifier))
                {
                    CANMessage oMessage = oCanConfig.GetCANMessage(Record.MessageIdentifier, MessageResearchOption.Identifier);

                    if (!(oMessage == null))
                    {
                        CANMessageDecoded oMsgDecoded = new CANMessageDecoded(oMessage, Record);

                        if (oMsgDecoded.bMessageDecoded)
                        {
                            foreach (CANParameter oParam in oMsgDecoded.Parameters)
                            {
                                AddDataSample(oParam.Name, Record.TimeOffset, oParam.DecodedValue);

                                if (bComputeVirtuals)
                                {
                                    VCLibraries.UpDateVariableElement(oParam.Name, oParam.DecodedValue);
                                }
                            }

                            if (bComputeVirtuals)
                            {
                                VCLibraries.ComputeLibraries();

                                foreach (CS_VirtualChannelsLibrary oLib in VCLibraries.Libraries)
                                {
                                    foreach (CS_VirtualChannel oVirtual in oLib.Channels)
                                    {
                                        if (oVirtual.bComputed && (oVirtual.bNewValue | iRecord == Records.Count))                                         //Virtual channel computation for the last record in order to have value until the end
                                        {
                                            oVirtual.bNewValue = false;

                                            if (!oVirtual.InError)
                                            {
                                                if (!Double.IsNaN(oVirtual.Value))
                                                {
                                                    AddDataSample(oVirtual.Name, Record.TimeOffset, oVirtual.Value);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(true);
        }
Пример #14
0
        /// <summary>
        /// waitForMessage waits for a specific CAN message give by a CAN id.
        /// </summary>
        /// <param name="a_canID">The CAN id to listen for</param>
        /// <param name="timeout">Listen timeout</param>
        /// <param name="r_canMsg">The CAN message with a_canID that we where listening for.</param>
        /// <returns>The CAN id for the message we where listening for, otherwise 0.</returns>
        public override uint waitForMessage(uint a_canID, uint timeout, out CANMessage canMsg)
        {
            /*
             * int readResult = 0;
             * int nrOfWait = 0;
             * while (nrOfWait < timeout)
             * {
             *  LAWICEL.CANMsg r_canMsg = new LAWICEL.CANMsg();
             *  canMsg = new CANMessage();
             *  readResult = LAWICEL.canusb_Read(m_deviceHandle, out r_canMsg);
             *  if (readResult == LAWICEL.ERROR_CANUSB_OK)
             *  {
             *      //Console.WriteLine("rx id: 0x" + r_canMsg.id.ToString("X4"));
             *      if (r_canMsg.id != a_canID)
             *      {
             *          nrOfWait++;
             *          continue;
             *      }
             *      else
             *      {
             *          canMsg.setID(r_canMsg.id);
             *          canMsg.setData(r_canMsg.data);
             *          canMsg.setFlags(r_canMsg.flags);
             *          return (uint)r_canMsg.id;
             *      }
             *  }
             *  else if (readResult == LAWICEL.ERROR_CANUSB_NO_MESSAGE)
             *  {
             *      Thread.Sleep(1);
             *      nrOfWait++;
             *  }
             * }
             * canMsg = new CANMessage();
             * return 0;*/
            LAWICEL.CANMsg r_canMsg;
            canMsg = new CANMessage();
            int readResult = 0;
            int nrOfWait   = 0;

            while (nrOfWait < timeout)
            {
                r_canMsg   = new LAWICEL.CANMsg();
                readResult = LAWICEL.canusb_Read(m_deviceHandle, out r_canMsg);
                if (readResult == LAWICEL.ERROR_CANUSB_OK)
                {
                    Thread.Sleep(1);
                    AddToCanTrace("rx: 0x" + r_canMsg.id.ToString("X4") + r_canMsg.data.ToString("X16"));
                    if (r_canMsg.id == 0x00)
                    {
                        nrOfWait++;
                    }
                    else if (r_canMsg.id != a_canID)
                    {
                        continue;
                    }
                    canMsg.setData(r_canMsg.data);
                    canMsg.setID(r_canMsg.id);
                    canMsg.setLength(r_canMsg.len);
                    return((uint)r_canMsg.id);
                }
                else if (readResult == LAWICEL.ERROR_CANUSB_NO_MESSAGE)
                {
                    Thread.Sleep(1);
                    nrOfWait++;
                }
            }
            r_canMsg = new LAWICEL.CANMsg();
            return(0);
        }
Пример #15
0
        /// <summary>
        /// readMessages is the "run" method of this class. It reads all incomming messages
        /// and publishes them to registered ICANListeners.
        /// </summary>
        public void readMessages()
        {
            int readResult = 0;

            LAWICEL.CANMsg r_canMsg   = new LAWICEL.CANMsg();
            CANMessage     canMessage = new CANMessage();

            Console.WriteLine("readMessages started");
            while (true)
            {
                lock (m_synchObject)
                {
                    if (m_endThread)
                    {
                        Console.WriteLine("readMessages ended");
                        return;
                    }
                }
                readResult = LAWICEL.canusb_Read(m_deviceHandle, out r_canMsg);
                if (readResult == LAWICEL.ERROR_CANUSB_OK)
                {
                    //Console.WriteLine(r_canMsg.id.ToString("X6") + " " + r_canMsg.data.ToString("X16"));
                    //if (MessageContainsInformationForRealtime(r_canMsg.id))
                    {
                        canMessage.setID(r_canMsg.id);
                        canMessage.setLength(r_canMsg.len);
                        canMessage.setTimeStamp(r_canMsg.timestamp);
                        canMessage.setFlags(r_canMsg.flags);
                        canMessage.setData(r_canMsg.data);
                        lock (m_listeners)
                        {
                            bitsPerSecond += 109;
                            AddToCanTrace("RX: " + r_canMsg.id.ToString("X6") + " " + r_canMsg.data.ToString("X16"));
                            rxCount++;
                            foreach (ICANListener listener in m_listeners)
                            {
                                //while (listener.messagePending()) ; // dirty, make this better
                                listener.handleMessage(canMessage);
                            }
                            CastInformationEvent("", rxCount, txCount, errCount); // <GS-05042011> re-activated this function
                        }
                        //Thread.Sleep(1);
                    }

                    // cast event to application to process message
                    //if (MessageContainsInformationForRealtime(r_canMsg.id))
                    //{
                    //TODO: process all other known msg id's into the realtime view
                    //  CastInformationEvent(canMessage); // <GS-05042011> re-activated this function
                    //}
                }
                else if (readResult == LAWICEL.ERROR_CANUSB_NO_MESSAGE)
                {
                    // Console.WriteLine("No message");
                    Thread.Sleep(1);
                }
                else
                {
                    Console.WriteLine("Result: " + readResult.ToString("X8"));
                }

                /*int stat = LAWICEL.canusb_Status(m_deviceHandle);
                 * if (stat != 0)
                 * {
                 *  Console.WriteLine("status: " + stat.ToString("X4"));
                 * }*/
            }
        }
Пример #16
0
        private void SerialCAN_CANMessageReceived(object sender, CANMessageReceivedEventArgs e)
        {
            int requestedBlockIndex;

            if (UpdateRunning)
            {
                switch ((CANMaerklinCommand)(e.CANMessage.Id & 0xFFFF0000UL))
                {
                case (CANMaerklinCommand.Ping):
                    Console.WriteLine("received CAN Ping {0}\n", e.CANMessage);
                    if (CurrentDeviceHash == (e.CANMessage.Id & 0x00FFFF))
                    {
                        if (!FileUpdateRunning)
                        {
                            //Ping received from update device so we expect the File update has been completed
                            Console.WriteLine("UPDATE COMPLETE");

                            UpdateCompletedAutoResetEvent.Set();
                            UpdateRunning = false;
                        }
                    }
                    break;

                case (CANMaerklinCommand.PingRsp):
                    Console.WriteLine("received CAN Ping answer from Device {0}\n", (DeviceType)(e.CANMessage.Data[0]));
                    if ((e.CANMessage.Data.Length == 8) && (e.CANMessage.Data[0] == (byte)DeviceType))
                    {
                        string deviceVersion = string.Format("{0}.{1}", e.CANMessage.Data[4], e.CANMessage.Data[5]);

                        switch ((DeviceType)e.CANMessage.Data[0])
                        {
                        case DeviceType.GB2:
                            Console.WriteLine("found Gleisbox with ID 0x{0} Version {1}\n", CurrentDeviceId, deviceVersion);
                            CurrentDeviceId   = GetDeviceIdFromCanMessage(e.CANMessage);
                            CurrentDeviceHash = e.CANMessage.Id & 0x00FFFF;
                            PingReceived      = true;
                            break;

                        case DeviceType.MS2:
                            Console.WriteLine("found MS2 with ID 0x{0} Version {1}\n", CurrentDeviceId, deviceVersion);
                            CurrentDeviceId   = GetDeviceIdFromCanMessage(e.CANMessage);
                            CurrentDeviceHash = e.CANMessage.Id & 0x00FFFF;
                            PingReceived      = true;
                            break;
                        }

                        if (IsFirmwareUpdate)
                        {
                            StartFirmwareUpdateProcedure(deviceVersion);
                        }
                        else
                        {
                            StartConfigFileSend();
                        }
                    }
                    break;

                case CANMaerklinCommand.BootloaderRsp:
                    //Firmware update process
                    if (PingReceived)
                    {
                        if (e.CANMessage.Data.Length == 8 && GetDeviceIdFromCanMessage(e.CANMessage) == CurrentDeviceId && ((e.CANMessage.Data[7] == 0x10) || (e.CANMessage.Data[7] == 0x32)))
                        {
                            Console.WriteLine("Send initial block id");
                            SendInitialBlockId(e.CANMessage);
                        }
                        else
                        {
                            /* first data block */
                            if (LastSentFrame == null || e.CANMessage.Data.ByteArrayEquals(LastSentFrame.Data) && LastBinBlock == TotalBlocksToSend)
                            {
                                Console.WriteLine("Send first data block");
                                SendFirmwareBlock(DataBytesToSendWithPadding.Skip(LastBinBlock * (int)CurrentUpdateFiles[CurrentUpdateFileIndex].blockSize).ToArray(), DataBytesToSendWithPadding.Length - TotalBlocksToSend * (int)CurrentUpdateFiles[CurrentUpdateFileIndex].blockSize);
                                LastBinBlock--;
                            }
                            else
                            {
                                //there seems to be different NACK types : 0xF1 and 0xF2
                                if (CheckFrameNack != null && e.CANMessage.Data.Length >= 5 && CheckFrameNack.Data.Length >= 5 &&
                                    e.CANMessage.Id == CheckFrameNack.Id && e.CANMessage.Data.Take(4).ToArray().ByteArrayEquals(CheckFrameNack.Data.Take(4).ToArray()) &&
                                    (e.CANMessage.Data[4] == 0xF1 || e.CANMessage.Data[4] == 0xF2)
                                    )
                                {
                                    Console.WriteLine("Aiiee got NACK. Aborting");
                                    UpdateCompletedAutoResetEvent.Set();
                                    UpdateRunning = false;
                                }

                                //MS2 may use 0x0000 as hash -> compare CheckFrameBlockId full 8 bytes
                                if (CheckFrameBlockId != null && e.CANMessage.Data.ByteArrayEquals(CheckFrameBlockId.Data))
                                {
                                    SendFirmwareBlock(DataBytesToSendWithPadding.Skip(LastBinBlock * (int)CurrentUpdateFiles[CurrentUpdateFileIndex].blockSize).ToArray(), (int)CurrentUpdateFiles[CurrentUpdateFileIndex].blockSize);
                                    //firmware flashing has to be sent in reversed order
                                    LastBinBlock--;
                                }

                                //MS2 may use 0x0000 as hash -> compare CheckFrame full 8 bytes
                                if (CheckFrame != null && e.CANMessage.Data.Length == CheckFrame.Data.Length && e.CANMessage.Data.Take(5).ToArray().ByteArrayEquals(CheckFrame.Data.Take(5).ToArray()))
                                {
                                    if ((LastBinBlock >= 0))
                                    {
                                        SendNextBlockId((byte)(LastBinBlock + CurrentUpdateFiles[CurrentUpdateFileIndex].blockOffset));
                                    }
                                    else
                                    {
                                        if (DeviceType == DeviceType.MS2)
                                        {
                                            Console.WriteLine("Reboot MS2 - be patient...");
                                            //end of update
                                            LastSentFrame = new CANMessage(BuildCANId(CANMaerklinCommand.Bootloader, CS2AddressHash), new byte[] { 0x00, 0x00, 0x00, 0x00, 0xf5 });
                                            Array.Copy(CurrentDeviceId.HexStringToByteArray(), 0, LastSentFrame.Data, 0, 4);
                                            serialCAN.SendCAN(LastSentFrame);
                                            Thread.Sleep(1000);
                                            //soft reset
                                            LastSentFrame = new CANMessage(BuildCANId(CANMaerklinCommand.Bootloader, CS2AddressHash), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x11 });
                                            Array.Copy(CurrentDeviceId.HexStringToByteArray(), 0, LastSentFrame.Data, 0, 4);
                                            serialCAN.SendCAN(LastSentFrame);
                                            Thread.Sleep(13000);

                                            //Start updating MS2 files
                                            StartConfigFileSend();
                                        }
                                        else
                                        {
                                            Console.WriteLine("UPDATE COMPLETE");

                                            UpdateCompletedAutoResetEvent.Set();
                                            UpdateRunning = false;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    break;

                case CANMaerklinCommand.ConfigDataQuery:
                    //Send/Update CONFIG DATA

                    if (e.CANMessage.Data.ByteArrayToHexString() == "6666666666666666")
                    {
                        if (RequestedConfigNameData.EndsWith("ver"))
                        {
                            if (AckconfigFileRequestFrame != null)
                            {
                                serialCAN.SendCAN(AckconfigFileRequestFrame);
                            }

                            //Send config data

                            LastBinBlock = 0;
                            PrepareCS2FileVersionInfoToSend(CurrentUpdateFiles[CurrentUpdateFileIndex]);
                            byte[] fileSizeBytes = BitConverter.GetBytes(FileLength).ToBigEndian();
                            Array.Copy(fileSizeBytes, LastSentFrame.Data, 4);
                            Crc16Ccitt crc16Ccitt = new Crc16Ccitt(InitialCrcValue.NonZero1);
                            LastSentFrame.Id = BuildCANId(CANMaerklinCommand.ConfigDataStream, e.CANMessage.Id & 0x00FFFF);
                            var crc16 = crc16Ccitt.ComputeChecksumBytes(DataBytesToSendWithPadding).ToBigEndian();
                            LastSentFrame.Data[4] = crc16[0];
                            LastSentFrame.Data[5] = crc16[1];
                            Console.WriteLine("block checksum 0x{0}", crc16.ByteArrayToHexString());
                            serialCAN.SendCAN(LastSentFrame);

                            //send all data
                            for (int i = 0; i < DataBytesToSendWithPadding.Length; i += 8)
                            {
                                LastSentFrame.Data = new byte[8];
                                Array.Copy(DataBytesToSendWithPadding, i, LastSentFrame.Data, 0, 8);
                                serialCAN.SendCAN(LastSentFrame);
                            }
                            Console.WriteLine("Data sent.");
                        }
                    }
                    else if (e.CANMessage.Data.ByteArrayToHexString().Substring(6) == "0000000000" && int.TryParse(Encoding.ASCII.GetString(e.CANMessage.Data).Trim('\0'), out requestedBlockIndex))
                    {
                        FileUpdateRunning = true;
                        if (!string.IsNullOrEmpty(RequestedConfigNameData))
                        {
                            Console.WriteLine("Request file {0} block {1}", RequestedConfigNameData, requestedBlockIndex);

                            byte[] bytesToSend = DataBytesToSendWithPadding.Skip(requestedBlockIndex * CurrentUpdateFiles[CurrentUpdateFileIndex].blockSize).Take(CurrentUpdateFiles[CurrentUpdateFileIndex].blockSize).ToArray();
                            //check if requested block is last block
                            if (DataBytesToSendWithPadding.Length / CurrentUpdateFiles[CurrentUpdateFileIndex].blockSize == requestedBlockIndex)
                            {
                                FileUpdateRunning = false;
                            }

                            byte[] fileSizeBytes = BitConverter.GetBytes(bytesToSend.Length).ToBigEndian();

                            //Prepare messages for later sending (FFFFFF... message)
                            AckconfigFileRequestFrame    = e.CANMessage.Clone();
                            AckconfigFileRequestFrame.Id = BuildCANId(CANMaerklinCommand.ConfigDataQueryRsp, CS2AddressHash);
                            serialCAN.SendCAN(AckconfigFileRequestFrame);

                            Array.Copy(fileSizeBytes, LastSentFrame.Data, 4);
                            Crc16Ccitt crc16Ccitt = new Crc16Ccitt(InitialCrcValue.NonZero1);
                            LastSentFrame.Id = BuildCANId(CANMaerklinCommand.ConfigDataStream, e.CANMessage.Id & 0x00FFFF);
                            var crc16 = crc16Ccitt.ComputeChecksumBytes(bytesToSend).ToBigEndian();
                            LastSentFrame.Data[4] = crc16[0];
                            LastSentFrame.Data[5] = crc16[1];
                            Console.WriteLine("block checksum 0x{0}", crc16.ByteArrayToHexString());
                            Thread.Sleep(100);
                            serialCAN.SendCAN(LastSentFrame);

                            //send block
                            LastSentFrame.Id = BuildCANId(CANMaerklinCommand.ConfigDataStream, e.CANMessage.Id & 0x00FFFF);
                            for (int i = 0; i < bytesToSend.Length; i += 8)
                            {
                                LastSentFrame.Data = new byte[8];
                                Array.Copy(bytesToSend, i, LastSentFrame.Data, 0, 8);
                                serialCAN.SendCAN(LastSentFrame);
                            }
                        }
                    }
                    else
                    {
                        RequestedConfigNameData = Encoding.ASCII.GetString(e.CANMessage.Data).Trim('\0');
                        CurrentUpdateFileIndex  = Array.FindIndex(ms2_update_data, x => x.versionName == RequestedConfigNameData);
                        if (Array.Exists(ms2_update_data, x => x.versionName == RequestedConfigNameData))
                        {
                            //CS2 version string request
                            Console.WriteLine("Request .CS2 fileinfo {0}", RequestedConfigNameData);
                            CurrentUpdateFileIndex = Array.FindIndex(ms2_update_data, x => x.versionName == RequestedConfigNameData);
                            LastSentFrame          = new CANMessage(BuildCANId(CANMaerklinCommand.ConfigDataStream, CS2AddressHash), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });

                            //Prepare messages for later sending (FFFFFF... message)
                            AckconfigFileRequestFrame    = e.CANMessage.Clone();
                            AckconfigFileRequestFrame.Id = BuildCANId(CANMaerklinCommand.ConfigDataQueryRsp, CS2AddressHash);
                        }
                        else if (Array.Exists(ms2_update_data, x => x.name == RequestedConfigNameData))
                        {
                            //File data request
                            Console.WriteLine("Request file {0}", RequestedConfigNameData);
                            CurrentUpdateFileIndex = Array.FindIndex(ms2_update_data, x => x.name == RequestedConfigNameData);
                            ReadFirmwareFile(CurrentUpdateFiles[CurrentUpdateFileIndex], false);
                            LastSentFrame = new CANMessage(BuildCANId(CANMaerklinCommand.ConfigDataStream, CS2AddressHash), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });

                            AckconfigFileRequestFrame    = e.CANMessage.Clone();
                            AckconfigFileRequestFrame.Id = BuildCANId(CANMaerklinCommand.ConfigDataStreamRsp, CS2AddressHash);
                        }
                        else
                        {
                            Console.WriteLine("Couldn't find requested file {0}", RequestedConfigNameData);
                        }
                    }
                    break;

                default:
                    break;
                }
            }
        }
Пример #17
0
        /// <summary>
        /// Send a KWP request.
        /// </summary>
        /// <param name="a_request">A KWP request.</param>
        /// <param name="r_reply">A KWP reply.</param>
        /// <returns>The status of the request.</returns>
        public override RequestResult sendRequest(KWPRequest a_request, out KWPReply r_reply)
        {
            uint row;
            uint all_rows = row = nrOfRowsToSend(a_request.getData());

            m_kwpCanListener.setupWaitMessage(0x258);

            // Send one or several request messages.
            for (; row > 0; row--)
            {
                CANMessage msg = new CANMessage(0x240, 0, 8);
                msg.elmExpectedResponses = a_request.ElmExpectedResponses;
                msg.setData(createCanMessage(a_request.getData(), row - 1));
                if ((msg.getData() & 0xFFFFUL) == 0xA141UL)
                {
                    msg.elmExpectedResponses = 0;
                }
                if (all_rows == 22)
                {
                    msg.elmExpectedResponses = row == 1 ? 1 : 0; // on last message (expect 1 reply)
                }
                if (!m_canDevice.sendMessage(msg))
                {
                    r_reply = new KWPReply();
                    return(RequestResult.ErrorSending);
                }
            }

            var response = m_kwpCanListener.waitMessage(getTimeout());

            // Receive one or several replys and send an ack for each reply.
            if (response.getID() == 0x258)
            {
                uint nrOfRows = (uint)(response.getCanData(0) & 0x3F) + 1;
                row = 0;
                if (nrOfRows == 0)
                {
                    throw new Exception("Wrong nr of rows");
                }
                //Assume that no KWP reply contains more than 0x200 bytes
                byte[] reply = new byte[0x200];
                reply = collectReply(reply, response.getData(), row);
                sendAck(nrOfRows - 1);
                nrOfRows--;

                m_kwpCanListener.setupWaitMessage(0x258);

                while (nrOfRows > 0)
                {
                    response = m_kwpCanListener.waitMessage(getTimeout());
                    if (response.getID() == 0x258)
                    {
                        row++;
                        reply = collectReply(reply, response.getData(), row);
                        sendAck(nrOfRows - 1);
                        nrOfRows--;
                    }
                    else
                    {
                        logger.Debug("1response.getID == " + response.getID());
                        r_reply = new KWPReply();
                        return(RequestResult.Timeout);
                    }
                }
                r_reply = new KWPReply(reply, a_request.getNrOfPID());
                return(RequestResult.NoError);
            }
            else
            {
                logger.Debug("2response.getID == " + response.getID());
                r_reply = new KWPReply();
                return(RequestResult.Timeout);
            }
        }
Пример #18
0
        /// <summary>
        /// Send a KWP request.
        /// </summary>
        /// <param name="a_request">A KWP request.</param>
        /// <param name="r_reply">A KWP reply.</param>
        /// <returns>The status of the request.</returns>
        public RequestResult sendRequest(KWPRequest a_request, out KWPReply r_reply)
        {
            CANMessage msg = new CANMessage(0x240, 0, 8);
            uint       row = nrOfRowsToSend(a_request.getData());

            m_kwpCanListener.setupWaitMessage(0x258);

            // Send one or several request messages.
            for (; row > 0; row--)
            {
                msg.setData(createCanMessage(a_request.getData(), row - 1));
                if (!m_canDevice.sendMessage(msg))
                {
                    r_reply = new KWPReply();
                    return(RequestResult.ErrorSending);
                }
            }

            msg = m_kwpCanListener.waitMessage(timeoutPeriod);
            //         msg = m_kwpCanListener.waitForMessage(0x258, timeoutPeriod);

            // Receive one or several replys and send an ack for each reply.
            if (msg.getID() == 0x258)
            {
                uint nrOfRows = (uint)(msg.getCanData(0) & 0x3F) + 1;
                row = 0;
                if (nrOfRows == 0)
                {
                    throw new Exception("Wrong nr of rows");
                }
                //Assume that no KWP reply contains more than 0x200 bytes
                byte[] reply = new byte[0x200];
                reply = collectReply(reply, msg.getData(), row);
                sendAck(nrOfRows - 1);
                nrOfRows--;

                m_kwpCanListener.setupWaitMessage(0x258);

                while (nrOfRows > 0)
                {
//                    msg = m_kwpCanListener.waitForMessage(0x258, timeoutPeriod);
                    msg = m_kwpCanListener.waitMessage(timeoutPeriod);
                    if (msg.getID() == 0x258)
                    {
                        row++;
                        reply = collectReply(reply, msg.getData(), row);
                        sendAck(nrOfRows - 1);
                        nrOfRows--;
                    }
                    else
                    {
                        r_reply = new KWPReply();
                        return(RequestResult.Timeout);
                    }
                }
                r_reply = new KWPReply(reply, a_request.getNrOfPID());
                return(RequestResult.NoError);
            }
            else
            {
                r_reply = new KWPReply();
                return(RequestResult.Timeout);
            }
        }
Пример #19
0
        /// <summary>
        /// Convert the DBC object into a CANStream CAN configuration object
        /// </summary>
        /// <param name="Rate">CAN bus Baud rate</param>
        /// <param name="CanNodeName">CAN device name</param>
        /// <returns>CANStream CAN configuration object of the DBC</returns>
        public CANMessagesConfiguration Convert_DBC_to_XCC(int Rate, string CanNodeName)
        {
            CANMessagesConfiguration oXCC = new CANMessagesConfiguration();

            //Bus Baud rate
            oXCC.CanRate = Rate;

            //Config name
            oXCC.Name = "DBC:";
            if (!(BusComponents == null))
            {
                for (int i = 0; i < BusComponents.Length; i++)
                {
                    oXCC.Name = oXCC.Name + " " + BusComponents[i];
                }
            }

            foreach (DBCFrame oFrame in Frames)
            {
                CANMessage oCSMsg = new CANMessage();

                oCSMsg.Identifier = NumberBaseConverter.Dec2Hex(oFrame.Identifier);
                oCSMsg.DLC        = oFrame.ByteLength;
                oCSMsg.Name       = oFrame.Name;
                oCSMsg.Comment    = oFrame.GetNodeComments();
                oCSMsg.Period     = 1000;           //Default value

                //oCSMsg.RxTx=CanMsgRxTx.Tx; //Default value
                if (oFrame.Transmitter.Equals(CanNodeName))
                {
                    oCSMsg.RxTx = CanMsgRxTx.Tx;
                }
                else
                {
                    oCSMsg.RxTx = CanMsgRxTx.Rx;
                }

                oCSMsg.MultiplexerName = oFrame.GetMultiplexerSignalName();

                foreach (DBCSignal oSignal in oFrame.Signals)
                {
                    if ((oCSMsg.RxTx.Equals(CanMsgRxTx.Tx)) || (oCSMsg.RxTx.Equals(CanMsgRxTx.Rx) && oSignal.Destination.Equals(CanNodeName)))
                    {
                        CANParameter oCSParam = new CANParameter();

                        oCSParam.Name    = oSignal.Name;
                        oCSParam.Comment = oSignal.GetSignalComments();
                        oCSParam.Unit    = oSignal.Unit;

                        oCSParam.StartBit  = oSignal.StartBit;
                        oCSParam.Length    = oSignal.Length;
                        oCSParam.Endianess = (CanParameterEndianess)Enum.Parse(typeof(CanParameterEndianess), oSignal.Endianess.ToString());
                        oCSParam.Signed    = oSignal.Signed;

                        oCSParam.Gain = oSignal.Gain;
                        oCSParam.Zero = oSignal.Zero;

                        if (!(oSignal.Multiplexer == null))
                        {
                            if (!(oSignal.Multiplexer.IsMultiplexerSignal))
                            {
                                oCSParam.IsMultiplexed    = true;
                                oCSParam.MultiplexerValue = oSignal.Multiplexer.MultiplexerValue;
                            }
                        }

                        oCSMsg.Parameters.Add(oCSParam);
                    }
                }

                if (oCSMsg.Parameters.Count > 0)
                {
                    oXCC.Messages.Add(oCSMsg);
                }
            }

            return(oXCC);
        }