using ProjectBase.Data.Logs; using System; using System.Text; /// /// Inclusion of PEAK PCAN-Basic namespace /// using TPCANHandle = System.UInt16; using TPCANBitrateFD = System.String; using TPCANTimestampFD = System.UInt64; namespace SIASUN.Autopilot.Device { public class CanHelper : ICanHelper { #region Delegates /// /// Read-Delegate Handler /// //private delegate void ReadDelegateHandler(); #endregion #region Members /// /// Saves the desired connection mode /// private bool m_IsFD = false; /// /// Saves the handle of a PCAN hardware /// private TPCANHandle m_PcanHandle; /// /// Saves the baudrate register for a conenction /// private TPCANBaudrate m_Baudrate; /// /// Saves the type of a non-plug-and-play hardware /// private TPCANType m_HwType; /// /// Stores the status of received messages for its display /// private System.Collections.ArrayList m_LastMsgsList; /// /// Read Delegate for calling the function "ReadMessages" /// //private ReadDelegateHandler m_ReadDelegate; /// /// Receive-Event /// private System.Threading.AutoResetEvent m_ReceiveEvent; /// /// Thread for message reading (using events) /// private System.Threading.Thread m_ReadThread; /// /// Handles of non plug and play PCAN-Hardware /// private TPCANHandle[] m_NonPnPHandles; /// /// MessageType is extend or standard /// private bool bExtend = false; private bool bFD = false; private bool bBRS = false; private bool bRemote = false; public CanHelper() { // Creates the list for received messages // //m_LastMsgsList = new System.Collections.ArrayList(); // Creates the delegate used for message reading // //m_ReadDelegate = new ReadDelegateHandler(ReadMessages); // Creates the event used for signalize incomming messages // m_ReceiveEvent = new System.Threading.AutoResetEvent(false); } #endregion /// /// 初始化can总线 /// public void Initialize() { try { string IOCan = System.Configuration.ConfigurationManager.AppSettings["IO_CAN"]; string Interrupt = System.Configuration.ConfigurationManager.AppSettings["Interrupt_CAN"]; setBaudrate(); setCANHandle(); setHardWareType(); TPCANStatus stsResult; // Connects a selected PCAN-Basic channel stsResult = PCANBasic.Initialize( m_PcanHandle, m_Baudrate, m_HwType, Convert.ToUInt32(IOCan, 16), Convert.ToUInt16(Interrupt)); if (stsResult != TPCANStatus.PCAN_ERROR_OK) LogHelper.log.Error(GetFormatedError(stsResult)); else // Prepares the PCAN-Basic's PCAN-Trace file // ConfigureTraceFile(); // Sets the connection status of the main-form // SetConnectionStatus(stsResult == TPCANStatus.PCAN_ERROR_OK); } catch (Exception ex) { LogHelper.log.Error("can总线初始化失败。", ex); } } /// /// Activates/deaactivates the different controls of the main-form according /// with the current connection status /// /// Current status. True if connected, false otherwise private void SetConnectionStatus(bool bConnected) { if (!bConnected) { LogHelper.log.Debug("can总线未连接成功"); } else { // Create and start the tread to read CAN Message using SetRcvEvent() // System.Threading.ThreadStart threadDelegate = new System.Threading.ThreadStart(this.CANReadThreadFunc); m_ReadThread = new System.Threading.Thread(threadDelegate); m_ReadThread.IsBackground = true; m_ReadThread.Start(); } } /// /// Thread-Function used for reading PCAN-Basic messages /// private void CANReadThreadFunc() { UInt32 iBuffer; TPCANStatus stsResult; iBuffer = Convert.ToUInt32(m_ReceiveEvent.SafeWaitHandle.DangerousGetHandle().ToInt32()); // Sets the handle of the Receive-Event. // stsResult = PCANBasic.SetValue(m_PcanHandle, TPCANParameter.PCAN_RECEIVE_EVENT, ref iBuffer, sizeof(UInt32)); if (stsResult != TPCANStatus.PCAN_ERROR_OK) { LogHelper.log.Error(GetFormatedError(stsResult)); return; } // While this mode is selected while (true) { // Waiting for Receive-Event // if (m_ReceiveEvent.WaitOne(50)) ReadMessages(); } } /// /// Function for reading PCAN-Basic messages /// private void ReadMessages() { TPCANStatus stsResult; // We read at least one time the queue looking for messages. // If a message is found, we look again trying to find more. // If the queue is empty or an error occurr, we get out from // the dowhile statement. do { stsResult = m_IsFD ? ReadMessageFD() : ReadMessage(); if (stsResult == TPCANStatus.PCAN_ERROR_ILLOPERATION) break; } while (!Convert.ToBoolean(stsResult & TPCANStatus.PCAN_ERROR_QRCVEMPTY)); } /// /// Function for reading messages on FD devices /// /// A TPCANStatus error code private TPCANStatus ReadMessageFD() { TPCANMsgFD CANMsg; TPCANTimestampFD CANTimeStamp; TPCANStatus stsResult; // We execute the "Read" function of the PCANBasic // stsResult = PCANBasic.ReadFD(m_PcanHandle, out CANMsg, out CANTimeStamp); if (stsResult != TPCANStatus.PCAN_ERROR_QRCVEMPTY) // We process the received message // ProcessMessage(CANMsg, CANTimeStamp); return stsResult; } /// /// Function for reading CAN messages on normal CAN devices /// /// A TPCANStatus error code private TPCANStatus ReadMessage() { TPCANMsg CANMsg; TPCANTimestamp CANTimeStamp; TPCANStatus stsResult; // We execute the "Read" function of the PCANBasic // stsResult = PCANBasic.Read(m_PcanHandle, out CANMsg, out CANTimeStamp); if (stsResult != TPCANStatus.PCAN_ERROR_QRCVEMPTY) // We process the received message // ProcessMessage(CANMsg, CANTimeStamp); return stsResult; } /// /// Processes a received message, in order to convert to bll /// /// The received PCAN-Basic message /// True if the message must be created, false if it must be modified private void ProcessMessage(TPCANMsg theMsg, TPCANTimestamp itsTimeStamp) { TPCANMsgFD newMsg; TPCANTimestampFD newTimestamp; newMsg = new TPCANMsgFD(); newMsg.DATA = new byte[64]; newMsg.ID = theMsg.ID; newMsg.DLC = theMsg.LEN; for (int i = 0; i < ((theMsg.LEN > 8) ? 8 : theMsg.LEN); i++) newMsg.DATA[i] = theMsg.DATA[i]; newMsg.MSGTYPE = theMsg.MSGTYPE; newTimestamp = Convert.ToUInt64(itsTimeStamp.micros + 1000 * itsTimeStamp.millis + 0x100000000 * 1000 * itsTimeStamp.millis_overflow); ProcessMessage(newMsg, newTimestamp); } /// /// Processes a received message, in order to show it in the Message-ListView /// /// The received PCAN-Basic message /// True if the message must be created, false if it must be modified private void ProcessMessage(TPCANMsgFD theMsg, TPCANTimestampFD itsTimeStamp) { MessageStatus msgStsCurrentMsg; msgStsCurrentMsg = new MessageStatus(theMsg, itsTimeStamp); // 拆包 Package_Data_Msg rp = new Package_Data_Msg(); object up = rp.UnPackage(msgStsCurrentMsg.IdString, msgStsCurrentMsg.CANMsg.DATA); // 将事件加入委托链 EventManage.Instance.RaiseEvent(msgStsCurrentMsg.IdString, rp.Message); } /// /// 断开连接 /// public void disConnect() { // Abort Read Thread if it exists // if (m_ReadThread != null) { m_ReadThread.Abort(); m_ReadThread.Join(); m_ReadThread = null; } // Releases the used PCAN-Basic channel // PCANBasic.Uninitialize(m_PcanHandle); } /// /// 设置波特率 /// private void setBaudrate() { int baudRate = int.Parse(System.Configuration.ConfigurationManager.AppSettings["Baudrate_CAN"]); switch (baudRate) { case 0: m_Baudrate = TPCANBaudrate.PCAN_BAUD_1M; break; case 1: m_Baudrate = TPCANBaudrate.PCAN_BAUD_800K; break; case 2: m_Baudrate = TPCANBaudrate.PCAN_BAUD_500K; break; case 3: m_Baudrate = TPCANBaudrate.PCAN_BAUD_250K; break; case 4: m_Baudrate = TPCANBaudrate.PCAN_BAUD_125K; break; case 5: m_Baudrate = TPCANBaudrate.PCAN_BAUD_100K; break; case 6: m_Baudrate = TPCANBaudrate.PCAN_BAUD_95K; break; case 7: m_Baudrate = TPCANBaudrate.PCAN_BAUD_83K; break; case 8: m_Baudrate = TPCANBaudrate.PCAN_BAUD_50K; break; case 9: m_Baudrate = TPCANBaudrate.PCAN_BAUD_47K; break; case 10: m_Baudrate = TPCANBaudrate.PCAN_BAUD_33K; break; case 11: m_Baudrate = TPCANBaudrate.PCAN_BAUD_20K; break; case 12: m_Baudrate = TPCANBaudrate.PCAN_BAUD_10K; break; case 13: m_Baudrate = TPCANBaudrate.PCAN_BAUD_5K; break; default: m_Baudrate = TPCANBaudrate.PCAN_BAUD_500K; break; } } /// /// 设置CAN总线句柄 /// private void setCANHandle() { int handle = int.Parse(System.Configuration.ConfigurationManager.AppSettings["TPHandle_CAN"]); switch (handle) { case 0: m_PcanHandle = PCANBasic.PCAN_NONEBUS; break; case 1: m_PcanHandle = PCANBasic.PCAN_ISABUS1; break; case 2: m_PcanHandle = PCANBasic.PCAN_ISABUS2; break; case 3: m_PcanHandle = PCANBasic.PCAN_ISABUS3; break; case 4: m_PcanHandle = PCANBasic.PCAN_ISABUS4; break; case 5: m_PcanHandle = PCANBasic.PCAN_ISABUS5; break; case 6: m_PcanHandle = PCANBasic.PCAN_ISABUS6; break; case 7: m_PcanHandle = PCANBasic.PCAN_ISABUS7; break; case 8: m_PcanHandle = PCANBasic.PCAN_ISABUS8; break; case 9: m_PcanHandle = PCANBasic.PCAN_DNGBUS1; break; case 10: m_PcanHandle = PCANBasic.PCAN_PCIBUS1; break; case 11: m_PcanHandle = PCANBasic.PCAN_PCIBUS2; break; case 12: m_PcanHandle = PCANBasic.PCAN_PCIBUS3; break; case 13: m_PcanHandle = PCANBasic.PCAN_PCIBUS4; break; case 14: m_PcanHandle = PCANBasic.PCAN_PCIBUS5; break; case 15: m_PcanHandle = PCANBasic.PCAN_PCIBUS6; break; case 16: m_PcanHandle = PCANBasic.PCAN_PCIBUS7; break; case 17: m_PcanHandle = PCANBasic.PCAN_PCIBUS8; break; case 18: m_PcanHandle = PCANBasic.PCAN_USBBUS1; break; case 19: m_PcanHandle = PCANBasic.PCAN_USBBUS2; break; case 20: m_PcanHandle = PCANBasic.PCAN_USBBUS3; break; case 21: m_PcanHandle = PCANBasic.PCAN_USBBUS4; break; case 22: m_PcanHandle = PCANBasic.PCAN_USBBUS5; break; case 23: m_PcanHandle = PCANBasic.PCAN_USBBUS6; break; case 24: m_PcanHandle = PCANBasic.PCAN_USBBUS7; break; case 25: m_PcanHandle = PCANBasic.PCAN_USBBUS8; break; case 26: m_PcanHandle = PCANBasic.PCAN_PCCBUS1; break; case 27: m_PcanHandle = PCANBasic.PCAN_PCCBUS2; break; default: m_PcanHandle = PCANBasic.PCAN_PCCBUS2; break; } } private void setHardWareType() { int hwType = int.Parse(System.Configuration.ConfigurationManager.AppSettings["HwType_CAN"]); switch (hwType) { case 0: m_HwType = TPCANType.PCAN_TYPE_ISA; break; case 1: m_HwType = TPCANType.PCAN_TYPE_ISA_SJA; break; case 2: m_HwType = TPCANType.PCAN_TYPE_ISA_PHYTEC; break; case 3: m_HwType = TPCANType.PCAN_TYPE_DNG; break; case 4: m_HwType = TPCANType.PCAN_TYPE_DNG_EPP; break; case 5: m_HwType = TPCANType.PCAN_TYPE_DNG_SJA; break; case 6: m_HwType = TPCANType.PCAN_TYPE_DNG_SJA_EPP; break; default: m_HwType = TPCANType.PCAN_TYPE_DNG_SJA_EPP; break; } } /// /// 写入数据包 /// /// public void Write(byte[] data, string id) { TPCANStatus stsResult; // Send the message // stsResult = m_IsFD ? WriteFrameFD(data, id) : WriteFrame(data, id); // The message was successfully sent // if (stsResult == TPCANStatus.PCAN_ERROR_OK) LogHelper.log.Debug("Message was successfully SENT"); // An error occurred. We show the error. // else LogHelper.log.Error(GetFormatedError(stsResult)); } /// /// 写入数据帧 /// /// 标准数据 /// id /// private TPCANStatus WriteFrame(byte[] data, string id) { TPCANMsg CANMsg; // We create a TPCANMsg message structure // CANMsg = new TPCANMsg(); CANMsg.DATA = new byte[8]; // We configurate the Message. The ID, // Length of the Data, Message Type // and the data // CANMsg.ID = Convert.ToUInt32(id, 16); CANMsg.LEN = Convert.ToByte(data.Length); CANMsg.MSGTYPE = bExtend ? TPCANMessageType.PCAN_MESSAGE_EXTENDED : TPCANMessageType.PCAN_MESSAGE_STANDARD; // If a remote frame will be sent, the data bytes are not important. // if (bRemote) CANMsg.MSGTYPE |= TPCANMessageType.PCAN_MESSAGE_RTR; else { // We get so much data as the Len of the message // for (int i = 0; i < GetLengthFromDLC(CANMsg.LEN, true); i++) { CANMsg.DATA[i] = data[i]; } } // The message is sent to the configured hardware // return PCANBasic.Write(m_PcanHandle, ref CANMsg); } /// /// FD类型写入数据帧 /// /// 数据 /// /// private TPCANStatus WriteFrameFD(byte[] data, string id) { TPCANMsgFD CANMsg; int iLength; // We create a TPCANMsgFD message structure // CANMsg = new TPCANMsgFD(); CANMsg.DATA = new byte[64]; // We configurate the Message. The ID, // Length of the Data, Message Type // and the data // CANMsg.ID = Convert.ToUInt32(id, 16); CANMsg.DLC = Convert.ToByte(data.Length); CANMsg.MSGTYPE = bExtend ? TPCANMessageType.PCAN_MESSAGE_EXTENDED : TPCANMessageType.PCAN_MESSAGE_STANDARD; CANMsg.MSGTYPE |= bFD ? TPCANMessageType.PCAN_MESSAGE_FD : TPCANMessageType.PCAN_MESSAGE_STANDARD; CANMsg.MSGTYPE |= bBRS ? TPCANMessageType.PCAN_MESSAGE_BRS : TPCANMessageType.PCAN_MESSAGE_STANDARD; // If a remote frame will be sent, the data bytes are not important. // if (bRemote) CANMsg.MSGTYPE |= TPCANMessageType.PCAN_MESSAGE_RTR; else { // We get so much data as the Len of the message // iLength = GetLengthFromDLC(CANMsg.DLC, (CANMsg.MSGTYPE & TPCANMessageType.PCAN_MESSAGE_FD) == 0); for (int i = 0; i < iLength; i++) { CANMsg.DATA[i] = data[i]; } } // The message is sent to the configured hardware // return PCANBasic.WriteFD(m_PcanHandle, ref CANMsg); } /// /// Configures the PCAN-Trace file for a PCAN-Basic Channel /// private void ConfigureTraceFile() { UInt32 iBuffer; TPCANStatus stsResult; // Configure the maximum size of a trace file to 5 megabytes // iBuffer = 5; stsResult = PCANBasic.SetValue(m_PcanHandle, TPCANParameter.PCAN_TRACE_SIZE, ref iBuffer, sizeof(UInt32)); if (stsResult != TPCANStatus.PCAN_ERROR_OK) LogHelper.log.Error(GetFormatedError(stsResult)); // Configure the way how trace files are created: // * Standard name is used // * Existing file is ovewritten, // * Only one file is created. // * Recording stopts when the file size reaches 5 megabytes. // iBuffer = PCANBasic.TRACE_FILE_SINGLE | PCANBasic.TRACE_FILE_OVERWRITE; stsResult = PCANBasic.SetValue(m_PcanHandle, TPCANParameter.PCAN_TRACE_CONFIGURE, ref iBuffer, sizeof(UInt32)); if (stsResult != TPCANStatus.PCAN_ERROR_OK) LogHelper.log.Error(GetFormatedError(stsResult)); } /// /// Help Function used to get an error as text /// /// Error code to be translated /// A text with the translated error private string GetFormatedError(TPCANStatus error) { StringBuilder strTemp; // Creates a buffer big enough for a error-text // strTemp = new StringBuilder(256); // Gets the text using the GetErrorText API function // If the function success, the translated error is returned. If it fails, // a text describing the current error is returned. // if (PCANBasic.GetErrorText(error, 0, strTemp) != TPCANStatus.PCAN_ERROR_OK) return string.Format("An error occurred. Error-code's text ({0:X}) couldn't be retrieved", error); else return strTemp.ToString(); } #region Help functions /// /// Convert a CAN DLC value into the actual data length of the CAN/CAN-FD frame. /// /// A value between 0 and 15 (CAN and FD DLC range) /// A value indicating if the msg is a standard CAN (FD Flag not checked) /// The length represented by the DLC private int GetLengthFromDLC(int dlc, bool isSTD) { if (dlc <= 8) return dlc; if (isSTD) return 8; switch (dlc) { case 9: return 12; case 10: return 16; case 11: return 20; case 12: return 24; case 13: return 32; case 14: return 48; case 15: return 64; default: return dlc; } } #endregion help functions } }