因为用不到CAN的高速功能(CAN_FD),因此相关的都可以删除或者注释掉。
修改后,最重要的三个函数和一个数据接收委托事件就是
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MCUCommunication.USBCAN.ZLGCAN
{
public class UsbCANUtil
{
protected Logger UsbCANLogger { get; set; }
//定义线程委托,用于接收数据处理
public delegate void USBCANReceiveData(byte[] revdata);
public delegate void RxMessage(ZCAN_Receive_Data msg, bool bridge = false);
public delegate void DisconnectDelegate(CommunicationHelper.CommunicatinType type);
#region CAN参数定义
const int NULL = 0;
//const int CANFD_BRS = 0x01; /* bit rate switch (second bitrate for payload data) */
//const int CANFD_ESI = 0x02; /* error state indicator of the transmitting node */
/* CAN payload length and DLC definitions according to ISO 11898-1 */
//const int CAN_MAX_DLC = 8;
const int CAN_MAX_DLEN = 8;
/* CAN FD payload length and DLC definitions according to ISO 11898-7 */
//const int CANFD_MAX_DLC = 15;
//const int CANFD_MAX_DLEN = 64;
const uint CAN_EFF_FLAG = 0x80000000U; /* EFF/SFF is set in the MSB */
const uint CAN_RTR_FLAG = 0x40000000U; /* remote transmission request */
const uint CAN_ERR_FLAG = 0x20000000U; /* error message frame */
const uint CAN_ID_FLAG = 0x1FFFFFFFU; /* id */
public DeviceInfo[] kDeviceType =
{
new DeviceInfo(Define.ZCAN_USBCAN1, 1),
new DeviceInfo(Define.ZCAN_USBCAN2, 2),
new DeviceInfo(Define.ZCAN_USBCAN_E_U, 1),
new DeviceInfo(Define.ZCAN_USBCAN_2E_U, 2),
//目前本上位机只支持周立功USBCAN的四种设备,后期根据需求再添加
//new DeviceInfo(Define.ZCAN_PCIECANFD_100U, 1),
//new DeviceInfo(Define.ZCAN_PCIECANFD_200U, 2),
//new DeviceInfo(Define.ZCAN_PCIECANFD_400U, 4),
//new DeviceInfo(Define.ZCAN_USBCANFD_200U, 2),
//new DeviceInfo(Define.ZCAN_USBCANFD_100U, 1),
//new DeviceInfo(Define.ZCAN_USBCANFD_MINI, 1),
//new DeviceInfo(Define.ZCAN_CANETTCP, 1),
//new DeviceInfo(Define.ZCAN_CANETUDP, 1),
//new DeviceInfo(Define.ZCAN_CLOUD, 1)
};
uint[] kAbitTiming =
{
0x00018B2E,//1Mbps
0x00018E3A,//800kbps
0x0001975E,//500kbps
0x0001AFBE,//250kbps
0x0041AFBE,//125kbps
0x0041BBEE,//100kbps
0x00C1BBEE //50kbps
};
uint[] kDbitTiming =
{
0x00010207,//5Mbps
0x0001020A,//4Mbps
0x0041020A,//2Mbps
0x0081830E //1Mbps
};
byte[] kTiming0 =
{
0x00, //1000kbps
0x00, //800kbps
0x00, //500kbps
0x01, //250kbps
0x03, //125kbps
0x04, //100kbps
0x09, //50kbps
0x18, //20kbps
0x31, //10kbps
0xBF //5kbps
};
byte[] kTiming1 =
{
0x14,//1000kbps
0x16,//800kbps
0x1C,//500kbps
0x1C,//250kbps
0x1C,//125kbps
0x1C,//100kbps
0x1C,//50kbps
0x1C,//20kbps
0x1C,//10kbps
0xFF //5kbps
};
uint[] kBaudrate =
{
1000000,//1000kbps
800000,//800kbps
500000,//500kbps
250000,//250kbps
125000,//125kbps
100000,//100kbps
50000,//50kbps
20000,//20kbps
10000,//10kbps
5000 //5kbps
};
#endregion
int channel_index_;
IntPtr device_handle_;
IntPtr channel_handle_;
IProperty property_;
RecvDataThread recv_data_thread_;
private int DeviceTypeIndex;//设备类型索引,对应ComboBox下拉框
private int DeviceIndex=0;//设备索引,对应ComboBox下拉框,本上位机只针对一个USBCAN设备调试,不存在接入多个相同设备的情况,因此设备索引一直为0
private int BuadRateIndex;//波特率索引,对应ComboBox下拉框
public event USBCANReceiveData USBCANReceiveEvent;
public event RxMessage rxmessage;
public event DisconnectDelegate disconnectEvent;
private static readonly Lazy<UsbCANUtil> lazy = new Lazy<UsbCANUtil>(() => new UsbCANUtil());
public static UsbCANUtil Instance => lazy.Value;
public ManualResetEvent manualResetEvent = new ManualResetEvent(false);
#region 构造函数
public UsbCANUtil()
{
}
#endregion
#region 生成CAN ID
public uint MakeCanId(uint id, int eff, int rtr, int err)//1:extend frame 0:standard frame
{
uint ueff = (uint)(!!(Convert.ToBoolean(eff)) ? 1 : 0);
uint urtr = (uint)(!!(Convert.ToBoolean(rtr)) ? 1 : 0);
uint uerr = (uint)(!!(Convert.ToBoolean(err)) ? 1 : 0);
return id | ueff << 31 | urtr << 30 | uerr << 29;
}
#endregion
#region 判断拓展帧还是标准帧
public bool IsEFF(uint id)//1:extend frame 0:standard frame
{
return !!Convert.ToBoolean((id & CAN_EFF_FLAG));
}
#endregion
#region 判断是远程帧还是数据帧
public bool IsRTR(uint id)//1:remote frame 0:data frame
{
return !!Convert.ToBoolean((id & CAN_RTR_FLAG));
}
#endregion
#region 判断是错误帧还是正常帧
public bool IsERR(uint id)//1:error frame 0:normal frame
{
return !!Convert.ToBoolean((id & CAN_ERR_FLAG));
}
#endregion
#region 获取ID
public uint GetId(uint id)
{
return id & CAN_ID_FLAG;
}
#endregion
#region CAN设备关闭
public void CANDeviceClose()
{
//if (CANDeviceOpen())
//{
// Method.ZCAN_CloseDevice(device_handle_);
//}
Method.ZCAN_CloseDevice(device_handle_);
}
#endregion
#region CAN设备开启
///
/// CAN设备开启
///
/// 设备是否开启
public bool CANDeviceOpen()
{
//开启USBCAN端口
uint device_type_index_ = (uint)DeviceTypeIndex;
uint device_index_;
device_index_ = (uint)DeviceIndex;
try
{
device_handle_ = Method.ZCAN_OpenDevice(kDeviceType[device_type_index_].device_type, device_index_, 0);
}
catch (Exception)
{
MessageBox.Show("DLL运行不匹配,请选择其他版本APP!");
return false;
}
if (NULL == (int)device_handle_)
{
//MessageBox.Show("打开设备失败,请检查设备类型和设备索引号是否正确", "提示",
// MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("打开设备失败,请检查设备类型和设备索引号是否正确");
return false;
}
return true;
}
#endregion
#region 初始化CAN设备
private bool InitCanDevice()
{
if (!CANDeviceOpen())
{
//MessageBox.Show("设备还没打开", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("设备还没打开");
return false;
}
try
{
//uint type = kDeviceType[DeviceIndex].device_type;
IntPtr ptr = Method.GetIProperty(device_handle_);
if (NULL == (int)ptr)
{
MessageBox.Show("设置指定路径属性失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("设置指定路径属性失败");
return false;
}
property_ = (IProperty)Marshal.PtrToStructure((IntPtr)((UInt32)ptr), typeof(IProperty));
if (!setBaudrate(kBaudrate[BuadRateIndex]))
{
MessageBox.Show("设置波特率失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("设置波特率失败");
return false;
}
ZCAN_CHANNEL_INIT_CONFIG config_ = new ZCAN_CHANNEL_INIT_CONFIG();
config_.canfd.mode = (byte)0;//0 正常模式 ,1 只听模式
config_.can_type = Define.TYPE_CAN;
config_.can.timing0 = kTiming0[BuadRateIndex];
config_.can.timing1 = kTiming1[BuadRateIndex];
config_.can.filter = 0;
config_.can.acc_code = 0;
config_.can.acc_mask = 0xFFFFFFFF;
IntPtr pConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config_));
Marshal.StructureToPtr(config_, pConfig, true);
//int size = sizeof(ZCAN_CHANNEL_INIT_CONFIG);
//IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(size);
//System.Runtime.InteropServices.Marshal.StructureToPtr(config_, ptr, true);
channel_handle_ = Method.ZCAN_InitCAN(device_handle_, (uint)channel_index_, pConfig);
Marshal.FreeHGlobal(pConfig);
if (NULL == (int)channel_handle_)
{
//MessageBox.Show("初始化CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("初始化CAN失败");
return false;
}
}
catch (Exception ex)
{
UsbCANLogger.Info(ex.Message);
return false;
}
return true;
}
#endregion
#region 启动CAN
public bool CANDeviceStart()
{
if (!InitCanDevice())
{
//MessageBox.Show("初始化CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("初始化CAN失败");
return false;
}
if (Method.ZCAN_StartCAN(channel_handle_) != Define.STATUS_OK)
{
//MessageBox.Show("启动CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("启动CAN失败");
return false;
}
if (null == recv_data_thread_)
{
recv_data_thread_ = new RecvDataThread();
recv_data_thread_.setChannelHandle(channel_handle_);
recv_data_thread_.setStart(true);
recv_data_thread_.RecvCANData += this.AddData;
//recv_data_thread_.RecvFDData += this.AddData;
}
else
{
recv_data_thread_.setChannelHandle(channel_handle_);
}
return true;
}
#endregion
#region 复位CAN
public bool CANDeviceReset()
{
if (Method.ZCAN_ResetCAN(channel_handle_) != Define.STATUS_OK)
{
//MessageBox.Show("复位失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("复位失败");
return false;
}
return true;
}
#endregion
#region 数据发送
public bool CANDataSend(string IDstr,string DataText)
{
if (DataText.Length == 0)
{
//MessageBox.Show("数据为空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("数据为空");
return false;
}
uint id = (uint)System.Convert.ToInt32(IDstr, 16);
string data = DataText;
uint result; //发送的帧数
ZCAN_Transmit_Data can_data = new ZCAN_Transmit_Data();
can_data.frame.can_id = MakeCanId(id, 0, 0, 0);
can_data.frame.data = new byte[8];
can_data.frame.can_dlc = (byte)SplitData(data, ref can_data.frame.data, CAN_MAX_DLEN);
can_data.transmit_type = (uint)0; //0正常发送 1单次发送 2自发自收 3单次自发自收
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
Marshal.StructureToPtr(can_data, ptr, true);
result = Method.ZCAN_Transmit(channel_handle_, ptr, 1);
Marshal.FreeHGlobal(ptr);
if (result != 1)
{
//MessageBox.Show("发送数据失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("发送数据失败");
AddErr();
return false;
}
return true;
}
public bool CANDataSend(uint id, byte[] Data)
{
if (Data.Length == 0 && Data.Length > 8)
{
//MessageBox.Show("数据为空,或者超出范围", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
//UsbCANLogger.Info("数据为空,或者超出范围");
return false;
}
uint result=0; //发送的帧数
ZCAN_Transmit_Data can_data = new ZCAN_Transmit_Data();
can_data.frame.can_id = MakeCanId(id, 0, 0, 0);
can_data.frame.data = Data;
can_data.frame.can_dlc = (byte)Data.Length;
can_data.transmit_type = (uint)0; //0正常发送 1单次发送 2自发自收 3单次自发自收
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
Marshal.StructureToPtr(can_data, ptr, true);
lock (recv_data_thread_)
{
try
{
int times = 0;
var flag = manualResetEvent.WaitOne(TimeSpan.FromMilliseconds(3));
if (flag)
{
times = 0;
//IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
//Marshal.StructureToPtr(can_data, ptr, true);
result = Method.ZCAN_Transmit(channel_handle_, ptr, 1);
manualResetEvent.Reset();
Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fffff ::: 1 :::") + string.Format("{0:X2} {1:X2}", Data[1], Data[2]) + "///" + result);
Marshal.FreeHGlobal(ptr);
if (result != 1)
{
AddErr();
return false;
}
else
{
SaveDataToLog(can_data);
}
}
else
{
while (!flag)
{
//times++;
//IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
//Marshal.StructureToPtr(can_data, ptr, true);
result = Method.ZCAN_Transmit(channel_handle_, ptr, 1);
manualResetEvent.Reset();
Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fffff ::: 2 :::") + string.Format("{0:X2} {1:X2}", Data[1], Data[2]) + "///" + result);
//Marshal.FreeHGlobal(ptr);
if (result != 1)
{
times++;
AddErr();
if (times>0)
{
Marshal.FreeHGlobal(ptr);
times = 0;
CommunicationHelper.Instance.DeviceDisConnect(CommunicationHelper.CommunicatinType.USBCAN);
if (disconnectEvent != null)
{
disconnectEvent(CommunicationHelper.CommunicatinType.USBCAN);
}
return false;
}
}
else
{
SaveDataToLog(can_data);
Marshal.FreeHGlobal(ptr);
break;
}
}
}
}
catch (Exception)
{
}
}
return true;
}
#endregion
#region 更改CAN设备的通道数
public void SetCANDeviceChannel(int kDeviceTypeindex,int Channel)
{
try
{
int Num = (int)kDeviceType[kDeviceTypeindex].channel_count;
if (Channel >= Num)
{
return;
}
channel_index_ = Channel;
}
catch (Exception ex)
{
MessageBox.Show("出现错误!");
}
}
#endregion
#region SetDeviceTypeIndex
public void SetDeviceTypeIndex(int deviceTypeIndex)
{
DeviceTypeIndex = deviceTypeIndex;
}
#endregion
#region SetBuadRateIndex
public void SetBuadRateIndex(int buadRateIndex)
{
BuadRateIndex = buadRateIndex;
}
#endregion
#region 获取设备的通道数并保存到列表中,ComboBox源
public List<int> GetCANDeviceChannelNum(int kDeviceTypeIndex)
{
int Num = (int)kDeviceType[kDeviceTypeIndex].channel_count;
var list = new List<int>();
for (int i = 0; i < Num; i++)
{
list.Add(i);
}
return list;
}
#endregion
//设置波特率
private bool setBaudrate(UInt32 baud)
{
string path = channel_index_ + "/baud_rate";
string value = baud.ToString();
byte[] byteArray = System.Text.Encoding.Default.GetBytes(value);
//char* pathCh = (char*)System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(path).ToPointer();
//char* valueCh = (char*)System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(value).ToPointer();
return 1 == property_.SetValue(path, byteArray);
}
private void AddData(ZCAN_Receive_Data[] data, uint len)
{
//string list_box_data_ = "";
for (uint i = 0; i < len; ++i)
{
byte[] revData=new byte[11];//ID + DLC +Data,最长11个字节
ZCAN_Receive_Data can = data[i];
uint id = data[i].frame.can_id;
revData[0]= (byte)(id / 256);
revData[1]= (byte)(id % 256);
revData[2]= (byte)can.frame.can_dlc;
byte[] tarData = new byte[3+ can.frame.can_dlc];
string str = string.Format("{0:X2} {1:X2} {2:X2} ", revData[0], revData[1], revData[2]);
for (uint j = 0; j < can.frame.can_dlc; ++j)
{
revData[3 + j] = (byte)can.frame.data[j];
str += string.Format("{0:X2} ", revData[3 + j]);
}
Array.Copy(revData,tarData,tarData.Length);//拷贝数组,不是每个Data都是8个字节
Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fffff :::")+str);
CommunicationHelper.Instance.DebugLogAdd(str);
manualResetEvent.Set(); //Set() 方法的调用使得ManualResetEvent对象的bool变量值为True,所有线程被释放并继续执行
manualResetEvent.Reset(); //如果我们想多次发送信号,那么我们必须在调用Set()方法后立即调用Reset()方法。
if (USBCANReceiveEvent != null)
{
USBCANReceiveEvent(tarData);
}
if (rxmessage != null)
{
rxmessage(can);
}
}
}
private string AddErr()
{
ZCAN_CHANNEL_ERROR_INFO pErrInfo = new ZCAN_CHANNEL_ERROR_INFO();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(pErrInfo));
Marshal.StructureToPtr(pErrInfo, ptr, true);
if (Method.ZCAN_ReadChannelErrInfo(channel_handle_, ptr) != Define.STATUS_OK)
{
//UsbCANLogger.Info("获取错误信息失败");
MessageBox.Show("获取错误信息失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
Marshal.FreeHGlobal(ptr);
string errorInfo = String.Format("错误码:{0:D1}", pErrInfo.error_code);
//UsbCANLogger.Info(errorInfo);
//MessageBox.Show(errorInfo);
return errorInfo;
}
//拆分text到发送data数组
private int SplitData(string data, ref byte[] transData, int maxLen)
{
string[] dataArray = data.Split(' ');
for (int i = 0; (i < maxLen) && (i < dataArray.Length); i++)
{
transData[i] = Convert.ToByte(dataArray[i].Substring(0, 2), 16);
}
return dataArray.Length;
}
private void SaveDataToLog(ZCAN_Transmit_Data can_data)
{
byte[] SendData = new byte[11];
SendData[0] = (byte)(can_data.frame.can_id / 256);
SendData[1] = (byte)(can_data.frame.can_id % 256);
SendData[2] = (byte)can_data.frame.can_dlc;
Array.Copy(can_data.frame.data, 0, SendData, 3, can_data.frame.can_dlc);
var str = CommunicationHelper.Instance.ByteToStr(SendData);
CommunicationHelper.Instance.DebugLogAdd(str);
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
namespace MCUCommunication.USBCAN.ZLGCAN
{
//接收数据线程类
public class RecvDataThread
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void RecvCANDataEventHandler(ZCAN_Receive_Data[] data, uint len);//CAN数据接收事件委托
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void RecvFDDataEventHandler(ZCAN_ReceiveFD_Data[] data, uint len);//CANFD数据接收事件委托
const int TYPE_CAN = 0;
const int TYPE_CANFD = 1;
bool m_bStart;
IntPtr channel_handle_;
Thread recv_thread_;
static object locker = new object();
public static RecvCANDataEventHandler OnRecvCANDataEvent;
//public static RecvFDDataEventHandler OnRecvFDDataEvent;
public RecvDataThread()
{
}
public event RecvCANDataEventHandler RecvCANData
{
add { OnRecvCANDataEvent += new RecvCANDataEventHandler(value); }
remove { OnRecvCANDataEvent -= new RecvCANDataEventHandler(value); }
}
//public event RecvFDDataEventHandler RecvFDData
//{
// add { OnRecvFDDataEvent += new RecvFDDataEventHandler(value); }
// remove { OnRecvFDDataEvent -= new RecvFDDataEventHandler(value); }
//}
public void setStart(bool start)
{
m_bStart = start;
if (start)
{
recv_thread_ = new Thread(RecvDataFunc);
recv_thread_.IsBackground = true;
recv_thread_.Start();
}
else
{
recv_thread_.Join();
recv_thread_ = null;
}
}
public void setChannelHandle(IntPtr channel_handle)
{
lock(locker)
{
channel_handle_ = channel_handle;
}
}
//数据接收函数
protected void RecvDataFunc()
{
ZCAN_Receive_Data[] can_data = new ZCAN_Receive_Data[10000];
//ZCAN_ReceiveFD_Data[] canfd_data = new ZCAN_ReceiveFD_Data[10000];
uint len;
try
{
while (m_bStart)
{
lock (locker)
{
len = Method.ZCAN_GetReceiveNum(channel_handle_, TYPE_CAN);
if (len > 0)
{
int size = Marshal.SizeOf(typeof(ZCAN_Receive_Data));
IntPtr ptr = Marshal.AllocHGlobal((int)len * size);
len = Method.ZCAN_Receive(channel_handle_, ptr, len, 50);
for (int i = 0; i < len; ++i)
{
can_data[i] = (ZCAN_Receive_Data)Marshal.PtrToStructure(
(IntPtr)((Int64)ptr + i * size), typeof(ZCAN_Receive_Data));
}
//SaveDataToLog(can_data[0]);
OnRecvCANDataEvent(can_data, len);
Marshal.FreeHGlobal(ptr);
}
//len = Method.ZCAN_GetReceiveNum(channel_handle_, TYPE_CANFD);
//if (len > 0)
//{
// int size = Marshal.SizeOf(typeof(ZCAN_ReceiveFD_Data));
// IntPtr ptr = Marshal.AllocHGlobal((int)len * size);
// len = Method.ZCAN_ReceiveFD(channel_handle_, ptr, len, 50);
// for (int i = 0; i < len; ++i)
// {
// canfd_data[i] = (ZCAN_ReceiveFD_Data)Marshal.PtrToStructure(
// (IntPtr)((UInt32)ptr+i*size), typeof(ZCAN_ReceiveFD_Data));
// }
// OnRecvFDDataEvent(canfd_data, len);
// Marshal.FreeHGlobal(ptr);
//}
}
Thread.Sleep(1);
}
}
catch (Exception ex)
{
}
}
private void SaveDataToLog(ZCAN_Receive_Data can_data)
{
byte[] SendData = new byte[11];
SendData[0] = (byte)(can_data.frame.can_id / 256);
SendData[1] = (byte)(can_data.frame.can_id % 256);
SendData[2] = (byte)can_data.frame.can_dlc;
Array.Copy(can_data.frame.data, 0, SendData, 3, can_data.frame.can_dlc);
var str = CommunicationHelper.Instance.ByteToStr(SendData);
CommunicationHelper.Instance.DebugLogAdd(str);
}
}
}
using System;
using System.Runtime.InteropServices;
namespace MCUCommunication.USBCAN.ZLGCAN
{
[StructLayout(LayoutKind.Sequential)]
public struct ZCAN
{
public uint acc_code;
public uint acc_mask;
public uint reserved;
public byte filter;
public byte timing0;
public byte timing1;
public byte mode;
};
[StructLayout(LayoutKind.Sequential)]
public struct CANFD
{
public uint acc_code;
public uint acc_mask;
public uint abit_timing;
public uint dbit_timing;
public uint brp;
public byte filter;
public byte mode;
public UInt16 pad;
public uint reserved;
};
[StructLayout(LayoutKind.Sequential)]
public struct can_frame
{
public uint can_id; /* 32 bit MAKE_CAN_ID + EFF/RTR/ERR flags */
public byte can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
public byte __pad; /* padding */
public byte __res0; /* reserved / padding */
public byte __res1; /* reserved / padding */
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] data/* __attribute__((aligned(8)))*/;
};
[StructLayout(LayoutKind.Sequential)]
public struct canfd_frame
{
public uint can_id; /* 32 bit MAKE_CAN_ID + EFF/RTR/ERR flags */
public byte len; /* frame payload length in byte */
public byte flags; /* additional flags for CAN FD,i.e error code */
public byte __res0; /* reserved / padding */
public byte __res1; /* reserved / padding */
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] data/* __attribute__((aligned(8)))*/;
};
[StructLayout(LayoutKind.Explicit)]
public struct ZCAN_CHANNEL_INIT_CONFIG
{
[FieldOffset(0)]
public uint can_type; //type:TYPE_CAN TYPE_CANFD
[FieldOffset(4)]
public ZCAN can;
[FieldOffset(4)]
public CANFD canfd;
};
[StructLayout(LayoutKind.Sequential)]
public struct ZCAN_Transmit_Data
{
public can_frame frame;
public uint transmit_type;
};
[StructLayout(LayoutKind.Sequential)]
public struct ZCAN_Receive_Data
{
public can_frame frame;
public UInt64 timestamp;//us
};
[StructLayout(LayoutKind.Sequential)]
public struct ZCAN_TransmitFD_Data
{
public canfd_frame frame;
public uint transmit_type;
};
public struct DeviceInfo
{
public uint device_type; //设备类型
public uint channel_count;//设备的通道个数
public DeviceInfo(uint type, uint count)
{
device_type = type;
channel_count = count;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct ZCAN_ReceiveFD_Data
{
public canfd_frame frame;
public UInt64 timestamp;//us
};
[StructLayout(LayoutKind.Sequential)]
public struct ZCAN_CHANNEL_ERROR_INFO
{
public uint error_code;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] passive_ErrData;
public byte arLost_ErrData;
} ;
//for zlg cloud
[StructLayout(LayoutKind.Sequential)]
public struct ZCLOUD_DEVINFO
{
public int devIndex;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] owner;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] model;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] fwVer;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] hwVer;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] serial;
public byte canNum;
public int status; // 0:online, 1:offline
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] bCanUploads; // each channel enable can upload
public byte bGpsUpload;
};
//[StructLayout(LayoutKind.Sequential)]
//public struct ZCLOUD_DEV_GROUP_INFO
//{
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
// public char[] groupName;
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
// public char[] desc;
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
// public char[] groupId;
// //public ZCLOUD_DEVINFO *pDevices;
// public IntPtr pDevices;
// public uint devSize;
//};
[StructLayout(LayoutKind.Sequential)]
public struct ZCLOUD_USER_DATA
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] username;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] mobile;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] dllVer;
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
// public char[] email;
// public IntPtr pDevGroups;
// public uint devGroupSize;
public uint devCnt;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public ZCLOUD_DEVINFO[] devices;
};
public class Define
{
public const int TYPE_CAN = 0;
public const int TYPE_CANFD = 1;
public const int ZCAN_USBCAN1 = 3;
public const int ZCAN_USBCAN2 = 4;
public const int ZCAN_CANETUDP = 12;
public const int ZCAN_CANETTCP = 17;
public const int ZCAN_USBCAN_E_U = 20;
public const int ZCAN_USBCAN_2E_U = 21;
public const int ZCAN_PCIECANFD_100U = 38;
public const int ZCAN_PCIECANFD_200U = 39;
public const int ZCAN_PCIECANFD_400U = 40;
public const int ZCAN_USBCANFD_200U = 41;
public const int ZCAN_USBCANFD_100U = 42;
public const int ZCAN_USBCANFD_MINI = 43;
public const int ZCAN_CLOUD = 46;
public const int ZCAN_CANFDNET_400U_TCP = 52;
public const int ZCAN_CANFDNET_400U_UDP = 53;
public const int STATUS_ERR = 0;
public const int STATUS_OK = 1;
};
public class Method
{
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr ZCAN_OpenDevice(uint device_type, uint device_index, uint reserved);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ZCAN_CloseDevice(IntPtr device_handle);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
// pInitConfig -> ZCAN_CHANNEL_INIT_CONFIG
public static extern IntPtr ZCAN_InitCAN(IntPtr device_handle, uint can_index, IntPtr pInitConfig);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ZCAN_StartCAN(IntPtr channel_handle);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ZCAN_ResetCAN(IntPtr channel_handle);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
// pTransmit -> ZCAN_Transmit_Data
public static extern uint ZCAN_Transmit(IntPtr channel_handle, IntPtr pTransmit, uint len);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
// pTransmit -> ZCAN_TransmitFD_Data
public static extern uint ZCAN_TransmitFD(IntPtr channel_handle, IntPtr pTransmit, uint len);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ZCAN_GetReceiveNum(IntPtr channel_handle, byte type);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ZCAN_Receive(IntPtr channel_handle, IntPtr data, uint len, int wait_time = -1);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ZCAN_ReceiveFD(IntPtr channel_handle, IntPtr data, uint len, int wait_time = -1);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
// pErrInfo -> ZCAN_CHANNEL_ERROR_INFO
public static extern uint ZCAN_ReadChannelErrInfo(IntPtr channel_handle, IntPtr pErrInfo);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetIProperty(IntPtr device_handle);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern bool ZCLOUD_IsConnected();
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void ZCLOUD_SetServerInfo(string httpAddr, ushort httpPort,
string mqttAddr, ushort mqttPort);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ZCLOUD_ConnectServer(string username, string password);
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ZCLOUD_DisconnectServer();
[DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr ZCLOUD_GetUserData();
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int SetValueFunc(string path, byte[] value);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr GetValueFunc(string path);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr GetPropertysFunc(string path, string value);
public struct IProperty
{
public SetValueFunc SetValue;
public GetValueFunc GetValue;
public GetPropertysFunc GetPropertys;
};
}
需要注意的是,生成的exe同等级文件夹路径下需要添加dll库文件
还需要注意dll库兼容的是64位或32位系统(若出现无法读取dll文件的情况,大概率就是dll与系统不匹配的问题)
using DCMotorControlSystem.Commoms.Protocols;
using MCUCommunication.USBCAN.ECAN;
using MCUCommunication.USBCAN.ZLGCAN;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static MCUCommunication.CommunicationHelper;
namespace MCUCommunication.Protocols
{
public class CanOpenSDOLib
{
private static readonly Lazy<CanOpenSDOLib> lazy = new Lazy<CanOpenSDOLib>(() => new CanOpenSDOLib());
public static CanOpenSDOLib Instance => lazy.Value;
private Queue<SDO> sdo_queue = new Queue<SDO>();
public Dictionary<UInt16, SDO> SDOcallbacks = new Dictionary<ushort, SDO>();
ConcurrentQueue<CanPacket> packetqueue = new ConcurrentQueue<CanPacket>();
public delegate void PacketEvent(CanPacket p, DateTime dt);
public event PacketEvent packetevent;
public delegate void SDOEvent(CanPacket p, DateTime dt);
public event SDOEvent sdoevent;
public delegate void SDODelegate(CanPacket p);
public event SDODelegate SDODelegateevent;
public byte[] DownLoadData = null;
bool threadrun = true;
private readonly object lockThread = new object();
public CanOpenSDOLib()
{
}
///
/// Send a Can packet on the bus
///
///
public bool SendPacket(CommunicatinType type,CanPacket p, bool bridge = false)
{
var flag = false;
switch (type)
{
case CommunicatinType.USBCAN:
ZCAN_Receive_Data msg = p.ToMsg();
flag = UsbCANUtil.Instance.CANDataSend(msg.frame.can_id, msg.frame.data);
if (flag)
{
Driver_rxmessage(msg, bridge);
}
break;
case CommunicatinType.ECAN:
CAN_OBJ obj = p.ToMsg2();
flag = ECANUtil.Instance.DataSend(obj.ID,obj.data);
if (flag)
{
Driver_rxmessage(obj, bridge);
}
break;
default:
break;
}
return flag;
}
///
/// Recieved message callback handler
///
/// CanOpen message recieved from the bus
private void Driver_rxmessage(ZCAN_Receive_Data msg, bool bridge = false)
{
//Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fffff ::: 2 :::") + string.Format("Driver_rxmessage {0:X2} {1:X2}", msg.frame.data[1], msg.frame.data[2]));
//packetqueue.Enqueue(new CanPacket(msg, bridge));
var p = new CanPacket(msg, bridge);
packetqueue.Enqueue(new CanPacket(msg, bridge));
if (SDODelegateevent != null)
{
SDODelegateevent(p);
}
}
///
/// Recieved message callback handler
///
/// CanOpen message recieved from the bus
private void Driver_rxmessage(CAN_OBJ msg, bool bridge = false)
{
var p = new CanPacket(msg, bridge);
packetqueue.Enqueue(new CanPacket(msg, bridge));
if (SDODelegateevent != null)
{
SDODelegateevent(p);
}
}
public void open(CommunicatinType type)
{
switch (type)
{
case CommunicatinType.USBCAN:
UsbCANUtil.Instance.rxmessage += Driver_rxmessage;
break;
case CommunicatinType.SerialPort:
break;
case CommunicatinType.ECAN:
ECANUtil.Instance.rxmessage += Driver_rxmessage;
break;
default:
break;
}
threadrun = true;
Thread thread = new Thread(new ThreadStart(asyncprocess));
//Thread thread = new Thread(new ThreadStart(syncprocess));
thread.Start();
}
///
/// Close the CanOpen CanFestival driver
///
public void close(CommunicatinType type)
{
threadrun = false;
switch (type)
{
case CommunicatinType.USBCAN:
UsbCANUtil.Instance.rxmessage -= Driver_rxmessage;
break;
case CommunicatinType.SerialPort:
break;
case CommunicatinType.ECAN:
ECANUtil.Instance.rxmessage -= Driver_rxmessage;
break;
default:
break;
}
}
void asyncprocess()
{
while (threadrun)
{
CanPacket cp;
//while (threadrun && packetqueue.IsEmpty && sdo_queue.Count == 0 && SDO.isEmpty())
while (packetqueue.IsEmpty && sdo_queue.Count == 0 && SDO.isEmpty())
{
System.Threading.Thread.Sleep(1);
}
while (packetqueue.TryDequeue(out cp))
{
//SDO replies 0x601-0x67F
if (cp.cob >= 0x580 && cp.cob < 0x600)
{
if (cp.len != 8)
return;
lock (sdo_queue)
{
if (SDOcallbacks.ContainsKey(cp.cob))
{
if (SDOcallbacks[cp.cob].SDOProcess(cp))
{
SDOcallbacks.Remove(cp.cob);
}
}
if (sdoevent != null)
sdoevent(cp, DateTime.Now);
}
}
//SDO 发送
if (cp.cob >= 0x600 && cp.cob < 0x680)
{
if (sdoevent != null)
sdoevent(cp, DateTime.Now);
}
SDO.kick_SDO();
lock (sdo_queue)
{
if (sdo_queue.Count > 0)
{
SDO sdoobj = sdo_queue.Peek();
if (!SDOcallbacks.ContainsKey((UInt16)(sdoobj.node + 0x580)))
{
sdoobj = sdo_queue.Dequeue();
SDOcallbacks.Add((UInt16)(sdoobj.node + 0x580), sdoobj);
sdoobj.sendSDO();
}
}
}
//System.Threading.Thread.Sleep(1);
}
}
}
#region SDOHelpers
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// UInt32 data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, UInt32 udata, Action<SDO> completedcallback)
{
byte[] bytes = BitConverter.GetBytes(udata);
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// Int64 data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, Int64 udata, Action<SDO> completedcallback)
{
byte[] bytes = BitConverter.GetBytes(udata);
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// UInt64 data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, UInt64 udata, Action<SDO> completedcallback)
{
byte[] bytes = BitConverter.GetBytes(udata);
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// Int32 data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, Int32 udata, Action<SDO> completedcallback)
{
byte[] bytes = BitConverter.GetBytes(udata);
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// UInt16 data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, Int16 udata, Action<SDO> completedcallback)
{
byte[] bytes = BitConverter.GetBytes(udata);
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// UInt16 data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, UInt16 udata, Action<SDO> completedcallback)
{
byte[] bytes = BitConverter.GetBytes(udata);
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// float data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, float ddata, Action<SDO> completedcallback)
{
byte[] bytes = BitConverter.GetBytes(ddata);
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// a byte of data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, byte udata, Action<SDO> completedcallback)
{
byte[] bytes = new byte[1];
bytes[0] = udata;
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// a byte of unsigned data to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, sbyte udata, Action<SDO> completedcallback)
{
byte[] bytes = new byte[1];
bytes[0] = (byte)udata;
return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// byte[] of data (1-8 bytes) to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, byte[] data, Action<SDO> completedcallback)
{
SDO sdo = new SDO(this, node, Keycode, subindex, SDO.Direction.SDO_WRITE, completedcallback, data);
lock (sdo_queue)
sdo_queue.Enqueue(sdo);
return sdo;
}
///
/// Write to a node via SDO
///
/// Node ID
/// Object Dictionary Index
/// Object Dictionary sub index
/// byte[] of data (1-8 bytes) to send
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains error/status codes
public SDO SDOwrite(CommunicatinType type,byte node, byte[] data, Action<SDO> completedcallback)
{
SDO sdo=null;
if (data.Length >= 5)
{
var Keycode = data[2] * 256 + data[1];
var subindex = data[3];
byte[] Databuffer = new byte[data.Length - 4];
Array.Copy(data,4, Databuffer,0,data.Length-4);
sdo = new SDO(this, node, (ushort)Keycode, subindex, SDO.Direction.SDO_WRITE, completedcallback, Databuffer);
sdo.communicatinType = type;
bool wpsent = false;
byte cmd = 0;
sdo.expitided = true;
switch (Databuffer.Length)
{
case 1:
cmd = 0x2f;
break;
case 2:
cmd = 0x2b;
break;
case 3:
cmd = 0x27;
break;
case 4:
cmd = 0x23;
break;
default:
//Bigger than 4 bytes we use segmented transfer
cmd = 0x21;
sdo.expitided = false;
byte[] payload = new byte[4];
payload[0] = (byte)Databuffer.Length;
payload[1] = (byte)(Databuffer.Length >> 8);
payload[2] = (byte)(Databuffer.Length >> 16);
payload[3] = (byte)(Databuffer.Length >> 24);
sdo.expitideddata = (UInt32)Databuffer.Length;
sdo.totaldata = 0;
wpsent = true;
if (cmd == data[0])
{
sdo.WriteFlag = sdo.sendpacket(cmd, payload);
if (sdo.WriteFlag)
{
lock (sdo_queue)
sdo_queue.Enqueue(sdo);
}
}
break;
}
if (wpsent == false)
{
sdo.WriteFlag = sdo.sendpacket(cmd, Databuffer);
if (sdo.WriteFlag)
{
lock (sdo_queue)
sdo_queue.Enqueue(sdo);
}
}
}
return sdo;
}
///
/// Read from a remote node via SDO
///
/// Node ID to read from
/// Object Dictionary Index
/// Object Dictionary sub index
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains returned data and error/status codes
public SDO SDOread(byte node, UInt16 Keycode, byte subindex, Action<SDO> completedcallback)
{
SDO sdo = new SDO(this, node, Keycode, subindex, SDO.Direction.SDO_READ, completedcallback, null);
lock (sdo_queue)
sdo_queue.Enqueue(sdo);
return sdo;
}
///
/// Read from a remote node via SDO
///
/// Node ID to read from
/// Object Dictionary Index
/// Object Dictionary sub index
/// Call back on finished/error event
/// SDO class that is used to perform the packet handshake, contains returned data and error/status codes
public SDO SDOread(CommunicatinType type, byte node, byte[] data, Action<SDO> completedcallback)
{
SDO sdo = null;
if (data.Length == 4)//读取时Data的长度为4
{
var Keycode = data[2] * 256 + data[1];
var subindex = data[3];
byte[] Databuffer = new byte[4] { 0,0,0,0 };
//Array.Copy(data, 0, Databuffer, 0, data.Length);
sdo = new SDO(this, node, (ushort)Keycode, subindex, SDO.Direction.SDO_READ, completedcallback, null);
sdo.communicatinType = type;
sdo.ReadFlag = sdo.sendpacket(data[0], Databuffer);
if (sdo.ReadFlag)
{
lock (sdo_queue)
sdo_queue.Enqueue(sdo);
}
}
return sdo;
}
///
/// Get the current length of Enqueued items
///
///
public int getSDOQueueSize()
{
return sdo_queue.Count;
}
///
/// Flush the SDO queue
///
public void flushSDOqueue()
{
lock (sdo_queue)
sdo_queue.Clear();
}
#endregion
}
}
using MCUCommunication.USBCAN.ECAN;
using MCUCommunication.USBCAN.ZLGCAN;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MCUCommunication.Protocols
{
public class CanPacket
{
public UInt16 cob;
public byte len;
public byte[] data;
public bool bridge = false;
public CanPacket()
{
}
///
/// Construct C# Canpacket from a CanFestival message
///
/// A CanFestival message struct
public CanPacket(ZCAN_Receive_Data msg, bool bridge = false)
{
//cob = msg.cob_id;
cob = (ushort)msg.frame.can_id;
//len = msg.len;
len = msg.frame.can_dlc;
data = new byte[len];
this.bridge = bridge;
//byte[] temp = BitConverter.GetBytes(msg.frame.data);
Array.Copy(msg.frame.data, data, len);
}
///
/// Construct C# Canpacket from a CanFestival message
///
/// A CanFestival message struct
public CanPacket(CAN_OBJ msg, bool bridge = false)
{
//cob = msg.cob_id;
cob = (ushort)msg.ID;
//len = msg.len;
len = msg.DataLen;
data = new byte[len];
this.bridge = bridge;
//byte[] temp = BitConverter.GetBytes(msg.frame.data);
Array.Copy(msg.data, data, len);
}
///
/// Convert to a CanFestival message
///
/// CanFestival message
public ZCAN_Receive_Data ToMsg()
{
ZCAN_Receive_Data msg = new ZCAN_Receive_Data();
msg.frame.can_id = cob;
msg.frame.can_dlc = len;
byte[] temp = new byte[8];
Array.Copy(data, temp, len);
msg.frame.data = temp;
return msg;
}
///
/// Convert to a CanFestival message
///
/// CanFestival message
public CAN_OBJ ToMsg2()
{
CAN_OBJ msg = new CAN_OBJ();
msg.ID = cob;
msg.DataLen = len;
byte[] temp = new byte[8];
Array.Copy(data, temp, len);
msg.data = temp;
return msg;
}
///
/// Dump current packet to string
///
/// Formatted string of current packet
public override string ToString()
{
string output = string.Format("{0:x3} {1:x1}", cob, len);
for (int x = 0; x < len; x++)
{
output += string.Format(" {0:x2}", data[x]);
}
return output;
}
}
}
using MVVM;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using static MCUCommunication.CommunicationHelper;
namespace MCUCommunication.Protocols
{
public class KeyCodeLib
{
public static Dictionary<string, KeyCodeModel> keyCodeTable = new Dictionary<string, KeyCodeModel>();
private static readonly Lazy<KeyCodeLib> lazy = new Lazy<KeyCodeLib>(() => new KeyCodeLib());
public static KeyCodeLib Instance => lazy.Value;
public List<KeyCodeModel> keyCodeModels;
public KeyCodeLib()
{
ReadKeyCodeXml();
keyCodeModels = GetKeyCodeList();
//keyCodeModels = GetKeyCodeList();
}
#region 读取Xml文件
private void ReadKeyCodeXml()
{
try
{
//var Filesname = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "KeyCode\\KeyCodeTable_SerialPort.xml"); //KeyCodeTable_SerialPort.xml SerialPort xml file
var Filesname = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "KeyCode\\KeyCodeTable_CANopen.xml"); //KeyCodeTable_CANopen.xml CANopen xml file
FileInfo fileInfo = new FileInfo(Filesname);
if (!fileInfo.Exists) return;
else
{
using (var stream = new FileStream(Filesname, FileMode.OpenOrCreate))
{
var formater = new XmlSerializer(typeof(KeyCodeTable));
var reader = new XmlTextReader(stream);
var model = formater.Deserialize(reader) as KeyCodeTable;
KeyCodeTableModel.KeyCodeTables = model;
}
}
var KeyCodeTables = KeyCodeTableModel.KeyCodeTables;
for (int i = 0; i < KeyCodeTables.KeyCode.Count; i++)
{
if (KeyCodeTables.KeyCode[i].SubIndexModel.SubIndex.Count > 0)
{
//var list = new List();
//for (int j = 0; j < KeyCodeTables.KeyCode[i].SubIndexModel.SubIndex.Count; j++)
//{
// list.Add(KeyCodeTables.KeyCode[i].SubIndexModel.SubIndex[j]);
//}
var str1 = JudgeDictionaryDuplicateKey(KeyCodeTables, KeyCodeTables.KeyCode[i].KeyCodeName, i);
var str2 = JudgeDictionaryDuplicateValue(KeyCodeTables, KeyCodeTables.KeyCode[i].KeyCodeValue, i);
if (str1 != "" || str2 != "") return;
keyCodeTable.Add(KeyCodeTables.KeyCode[i].KeyCodeName, new KeyCodeModel(KeyCodeTables.KeyCode[i].KeyCodeName, KeyCodeTables.KeyCode[i].KeyCodeValue, KeyCodeTables.KeyCode[i].SubIndexModel.SubIndex));
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
#endregion
#region 判断Dictionary重复KeyCodeName
public string JudgeDictionaryDuplicateKey(KeyCodeTable model,string Key,int index)
{
string str = "";
for (int i = 0; i < model.KeyCode.Count; i++)
{
if (Key==model.KeyCode[i].KeyCodeName && index != i)
{
str += $"第{i}个{model.KeyCode[i].KeyCodeName}与第{index}个{Key}KeyCode名称重复!\n请检查KeyCodeTable.xml是否正确!";
MessageBox.Show(str);
return str;
}
}
return str;
}
#endregion
#region 判断Dictionary重复KeyCodeValue
public string JudgeDictionaryDuplicateValue(KeyCodeTable model, string Value, int index)
{
string str = "";
for (int i = 0; i < model.KeyCode.Count; i++)
{
if (Value == model.KeyCode[i].KeyCodeValue && index != i)
{
str += $"第{i}个{model.KeyCode[i].KeyCodeValue}与第{index}个{Value}KeyCode键值重复!\n请检查KeyCodeTable.xml是否正确!";
MessageBox.Show(str);
return str;
}
}
return str;
}
#endregion
#region 获取KeyCode列表
//public List GetKeyCodeList()
//{
// var list = new List();
// foreach (var item in KeyCodeTable)
// {
// if (JudyKey(item.Value.CMDHexValue) != "")
// {
// list.Add(new KeyCodeModel(item.Key, JudyKey(item.Value.CMDHexValue), 0));
// }
// else
// {
// list.Add(new KeyCodeModel(item.Key, item.Value.CMDHexValue, 0));
// }
// }
// return list;
//}
public List<KeyCodeModel> GetKeyCodeList()
{
var list = new List<KeyCodeModel>();
foreach (var item in keyCodeTable)
{
list.Add(new KeyCodeModel(item.Key, item.Value.CMDHexValue, item.Value.SubIndex));
}
return list;
}
#endregion
#region 终端--根据部分获取KeyCode
public List<string> GetKeyCode(string someCmd)
{
//var list = GetKeyCodeList();
var tarlist = new List<string>();
foreach (var item in keyCodeModels)
{
if (item.CMDstring.ToLower().Trim().Contains(someCmd.Trim().ToLower()))
{
tarlist.Add(item.CMDstring);
}
}
return tarlist;
}
#endregion
#region 判断重复键值
public string JudyKey(string value)
{
var tarStr = "";
foreach (var item in keyCodeTable)
{
if (value == item.Key)
{
tarStr = item.Value.CMDHexValue;
}
}
return tarStr;
}
#endregion
#region 判断空值Data
public byte[] JudyData(byte[] value)
{
return value;
}
#endregion
#region 获取KeyCode的值
public string GetKeyCodeNameFromHexStr(string HexkeyCode)
{
string str = "";
foreach (var item in keyCodeModels)
{
if (HexkeyCode != null && item.CMDHexValue.ToLower().Trim() == HexkeyCode.ToLower().Trim())
{
str = item.CMDstring;
}
}
return str;
}
public string GetKeyCodeValue(string keyCode)
{
var tarStr = "";
//var list = GetKeyCodeList();
foreach (var item in keyCodeModels)
{
if (keyCode!=null && item.CMDstring.ToLower().Trim() == keyCode.ToLower().Trim())//转换为小写在判断
{
tarStr = item.CMDHexValue;
}
}
return tarStr;
}
public byte[] GetKeyCodeHexValue(string keyCode)
{
byte[] byteArray = new byte[2];
//var list = GetKeyCodeList();
foreach (var item in keyCodeModels)
{
if (keyCode != null && item.CMDstring.ToLower().Trim() == keyCode.ToLower().Trim())
{
//小端
byteArray[1] = (byte)(Convert.ToInt32(item.CMDHexValue, 16) / 256);
byteArray[0] = (byte)(Convert.ToInt32(item.CMDHexValue, 16) % 256);
}
}
return byteArray;//不满足则返回0
}
//public string GetKeyCodeHexValueStr(string keyCode)
//{
// string byteArray = "";
// //var list = GetKeyCodeList();
// foreach (var item in keyCodeModels)
// {
// if (keyCode != null && item.CMDHexValue.ToLower().Trim() == keyCode.ToLower().Trim())
// {
// byteArray =
// }
// }
// return byteArray;//不满足则返回0
//}
#endregion
#region 获取数值大小
public int GetKeyCodeListValueSizeFromKeyCodeStr(string keyCode,int index)
{
int str = 0;
foreach (var item in keyCodeModels)
{
if (keyCode != null && item.CMDstring.ToLower().Trim() == keyCode.ToLower().Trim())
{
if (index + 1 > item.SubIndex.Count) return 0;
else
{
str = item.SubIndex[index].Size;
}
}
}
return str;
}
#endregion
#region 填充Data
public void SetData(string keycode,int index,int Package)
{
foreach (var item in keyCodeModels)
{
if (item.CMDHexValue.ToLower().Trim() == keycode.ToLower().Trim())
{
foreach (var sub in item.SubIndex)
{
if (index==sub.Index)
{
sub.Value = BitConverter.GetBytes(Package);
}
}
}
}
}
#endregion
#region 获取Data
public int GetData(string keycode,int index)
{
int array=0;
foreach (var item in keyCodeModels)
{
if (item.CMDHexValue.ToLower().Trim() == keycode.ToLower().Trim())
{
foreach (var sub in item.SubIndex)
{
if (index == sub.Index)
{
for (int i = 0; i < sub.Value.Length; i++)
{
array += (sub.Value[i] << (8 * i));
}
}
}
}
}
return array;
}
#endregion
}
public class KeyCodeModel : ModelBase
{
private string cMDstring;
public string CMDstring
{
get { return cMDstring; }
set
{
cMDstring = value;
RaisePropertyChanged(() => CMDstring);
}
}
private string cMDHexValue;
public string CMDHexValue
{
get { return cMDHexValue; }
set
{
cMDHexValue = value;
RaisePropertyChanged(() => CMDHexValue);
}
}
private List<SubIndex> subIndex;
public List<SubIndex> SubIndex
{
get { return subIndex; }
set
{
subIndex = value;
if (subIndex.Count > 0)
{
for (int i = 0; i < SubIndex.Count; i++)
{
vs.Add(subIndex[i].Index.ToString());
describelist.Add(subIndex[i].Description);
typelist.Add(subIndex[i].Type);
sizelist.Add(subIndex[i].Size);
readWritelist.Add(subIndex[i].ReadWrite);
saveToFlashlist.Add(subIndex[i].SaveToFlash);
funcCbklist.Add(subIndex[i].FuncCbk);
maxlist.Add(subIndex[i].Max);
minlist.Add(subIndex[i].Min);
}
}
RaisePropertyChanged(() => SubIndex);
}
}
private List<string> subIndexModelIndex;
public List<string> SubIndexModelIndex
{
get { return vs; }
set
{
subIndexModelIndex = value;
RaisePropertyChanged(() => SubIndexModelIndex);
}
}
private List<string> description;
public List<string> Description
{
get { return describelist; }
set
{
description = value;
RaisePropertyChanged(() => Description);
}
}
private List<string> type;
public List<string> Type
{
get { return typelist; }
set
{
type = value;
RaisePropertyChanged(() => Type);
}
}
private List<int> size;
public List<int> Size
{
get { return sizelist; }
set
{
size = value;
RaisePropertyChanged(() => Size);
}
}
private List<string> readWrite;
public List<string> ReadWrite
{
get { return readWritelist; }
set
{
readWrite = value;
RaisePropertyChanged(() => ReadWrite);
}
}
private List<string> saveToFlash;
public List<string> SaveToFlash
{
get { return saveToFlashlist; }
set
{
saveToFlash = value;
RaisePropertyChanged(() => SaveToFlash);
}
}
private List<string> funcCbk;
public List<string> FuncCbk
{
get { return funcCbklist; }
set
{
funcCbk = value;
RaisePropertyChanged(() => FuncCbk);
}
}
private List<int> max;
public List<int> Max
{
get { return maxlist; }
set
{
max = value;
RaisePropertyChanged(() => Max);
}
}
private List<int> min;
public List<int> Min
{
get { return minlist; }
set
{
min = value;
RaisePropertyChanged(() => Min);
}
}
List<string> vs = new List<string>();
List<string> describelist = new List<string>();
List<string> typelist = new List<string>();
List<int> sizelist = new List<int>();
List<string> readWritelist = new List<string>();
List<string> saveToFlashlist = new List<string>();
List<string> funcCbklist = new List<string>();
List<int> maxlist = new List<int>();
List<int> minlist = new List<int>();
public KeyCodeModel(string cmdString, string cmdHexValue, List<SubIndex> subindex)
{
CMDstring = cmdString;
CMDHexValue = cmdHexValue;
SubIndex = subindex;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace MCUCommunication.Protocols
{
public class KeyCodeTableModel
{
public static KeyCodeTable KeyCodeTables { get; set; }
public KeyCodeTableModel()
{
KeyCodeTables = new KeyCodeTable();
}
}
[Serializable]
[XmlRoot("KeyCodeTable")]
public class KeyCodeTable
{
[XmlElement("KeyCode")]
public List<KeyCode> KeyCode { get; set; }
}
[Serializable]
public class KeyCode
{
public string KeyCodeName { get; set; }
public string KeyCodeValue { get; set; }
[XmlElement("SubIndexModel")]
public SubIndexModel SubIndexModel { get; set; }
}
[Serializable]
public class SubIndexModel
{
[XmlElement("SubIndex")]
public List<SubIndex> SubIndex { get; set; }
}
[Serializable]
public class SubIndex
{
public int Index { get; set; }
public string Type { get; set; }
public int Size { get; set; }
public byte[] Value { get; set; }
public string ReadWrite { get; set; }
public string SaveToFlash { get; set; }
public string FuncCbk { get; set; }
public int Max { get; set; }
public int Min { get; set; }
//public int DefautValue { get; set; }
public string Description { get; set; }
}
}
using MCUCommunication.Protocols;
using MCUCommunication.USBCAN.ZLGCAN;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static MCUCommunication.CommunicationHelper;
namespace DCMotorControlSystem.Commoms.Protocols
{
public delegate void DownLoadDelegate(byte[] buffer);
public delegate void DownLoadScheduleDelegate(float percentage);
public class SDO
{
///
/// Direction of the SDO transfer
///
public enum Direction
{
SDO_READ = 0, //读
SDO_WRITE = 1, //写
}
///
/// Possible SDO states used by simple handshake statemachine
///
public enum SDO_STATE
{
SDO_INIT, //初始化
SDO_SENT, //发送
SDO_HANDSHAKE, //握手
SDO_FINISHED, //完成
SDO_ERROR, //错误
}
///
/// Expitided data buffer, if transfer is 4 bytes or less, its here
///
public readonly byte node;
public CommunicatinType communicatinType;
//FIX me i was using all these from outside this class
//should see if that is really needed or if better accessors are required
//may be readonly access etc.
public byte[] databuffer = null;
public SDO_STATE state;
public UInt16 index;
public byte subindex;
public UInt32 expitideddata;
public bool expitided = false;
public bool ReadFlag;
public bool WriteFlag;
static List<SDO> activeSDO = new List<SDO>();
private Action<SDO> completedcallback;
private Direction dir;
public UInt32 totaldata;
private CanOpenSDOLib can;
public bool lasttoggle = false;
private DateTime timeout;
private ManualResetEvent finishedevent;
public static event DownLoadDelegate downLoadDelegateEvent;
public static event DownLoadScheduleDelegate downLoadScheduleEvent;
//private debuglevel dbglevel;
///
/// Construct a new SDO object
///
/// a libCanopenSimple object that will give access to the hardware
/// The note to talk to (UInt16)
/// The index in the object dictionary to access
/// The subindex in the object dictionary to access
/// Direction of transfer
/// Optional, completed callback (or null if not required)
/// A byte array of data to be transfered to or from if more than 4 bytes
public SDO(CanOpenSDOLib can, byte node, UInt16 index, byte subindex, Direction dir, Action<SDO> completedcallback, byte[] databuffer)
{
this.can = can;
this.index = index;
this.subindex = subindex;
this.node = node;
this.dir = dir;
this.completedcallback = completedcallback;
this.databuffer = databuffer;
finishedevent = new ManualResetEvent(false);
state = SDO_STATE.SDO_INIT;
//dbglevel = can.dbglevel;
}
///
/// Add this SDO object to the active list
///
public void sendSDO()
{
lock (activeSDO)
activeSDO.Add(this);
}
public static bool isEmpty()
{
return activeSDO.Count == 0;
}
///
/// Has the SDO transfer finished?
///
/// True if the SDO has finished and fired its finished event
public bool WaitOne()
{
return finishedevent.WaitOne();
}
///
/// SDO pump, call this often
///
public static void kick_SDO()
{
List<SDO> tokill = new List<SDO>();
lock (activeSDO)
{
foreach (SDO s in activeSDO)
{
//s.kick_SDOp();
if (s.state == SDO_STATE.SDO_FINISHED || s.state == SDO_STATE.SDO_ERROR)
{
tokill.Add(s);
}
}
}
foreach (SDO s in tokill)
{
lock (activeSDO)
activeSDO.Remove(s);
s.SDOFinish();
}
}
///
/// State machine for a specific SDO instance
///
public void kick_SDOp()
{
if (state != SDO_STATE.SDO_INIT && DateTime.Now > timeout)
{
state = SDO_STATE.SDO_ERROR;
Console.WriteLine("SDO Timeout Error on {0:x4}/{1:x2} {2:x8}", this.index, this.subindex, expitideddata);
if (completedcallback != null)
completedcallback(this);
return;
}
if (state == SDO_STATE.SDO_INIT)
{
timeout = DateTime.Now + new TimeSpan(0, 0, 5);
state = SDO_STATE.SDO_SENT;
if (dir == Direction.SDO_READ)
{
byte cmd = 0x40;
byte[] payload = new byte[4] { 0, 0, 0, 0 };
sendpacket(cmd, payload);
}
if (dir == Direction.SDO_WRITE)
{
bool wpsent = false;
byte cmd = 0;
expitided = true;
switch (databuffer.Length)
{
case 1:
cmd = 0x2f;
break;
case 2:
cmd = 0x2b;
break;
case 3:
cmd = 0x27;
break;
case 4:
cmd = 0x23;
break;
default:
//Bigger than 4 bytes we use segmented transfer
cmd = 0x21;
expitided = false;
byte[] payload = new byte[4];
payload[0] = (byte)databuffer.Length;
payload[1] = (byte)(databuffer.Length >> 8);
payload[2] = (byte)(databuffer.Length >> 16);
payload[3] = (byte)(databuffer.Length >> 24);
expitideddata = (UInt32)databuffer.Length;
totaldata = 0;
wpsent = true;
sendpacket(cmd, payload);
break;
}
if (wpsent == false)
sendpacket(cmd, databuffer);
}
}
}
///
/// Send a SDO packet, with command and payload, should be only called from SDO state machine
///
/// SDO command byte
/// Data payload to send
public bool sendpacket(byte cmd, byte[] payload)
{
CanPacket p = new CanPacket();
p.cob = (UInt16)(0x600 + node);
p.len = 8;
p.data = new byte[8];
p.data[0] = cmd;
p.data[1] = (byte)index;
p.data[2] = (byte)(index >> 8);
p.data[3] = subindex;
int sendlength = 4;
if (payload.Length < 4)
sendlength = payload.Length;
for (int x = 0; x < sendlength; x++)
{
p.data[4 + x] = payload[x];
}
//if (dbglevel == debuglevel.DEBUG_ALL)
//Console.WriteLine(String.Format("Sending a new SDO packet: {0}", p.ToString()));
return can.SendPacket(communicatinType,p);
}
///
/// Send a SDO packet, with command and payload, should be only called from SDO state machine
///
/// SDO command byte
/// Data payload to send
public bool sendpacket(byte cmd, byte[] payload, ushort index, byte subindex)
{
CanPacket p = new CanPacket();
p.cob = (UInt16)(0x600 + node);
p.len = 8;
p.data = new byte[8];
p.data[0] = cmd;
p.data[1] = (byte)index;
p.data[2] = (byte)(index >> 8);
p.data[3] = subindex;
int sendlength = 4;
if (payload.Length < 4)
sendlength = payload.Length;
for (int x = 0; x < sendlength; x++)
{
p.data[4 + x] = payload[x];
}
//if (dbglevel == debuglevel.DEBUG_ALL)
//Console.WriteLine(String.Format("Sending a new SDO packet: {0}", p.ToString()));
return can.SendPacket(communicatinType, p);
}
///
/// Segmented transfer update function, should be only called from SDO state machine
///
/// SDO command byte
/// Data payload
private bool sendpacketsegment(byte cmd, byte[] payload)
{
CanPacket p = new CanPacket();
p.cob = (UInt16)(0x600 + node);
p.len = 8;
p.data = new byte[8];
p.data[0] = cmd;
for (int x = 0; x < payload.Length; x++)
{
p.data[1 + x] = payload[x];
}
//if (dbglevel == debuglevel.DEBUG_ALL)
//Console.WriteLine(String.Format("Sending a new segmented SDO packet: {0}", p.ToString()));
return can.SendPacket(communicatinType, p);
}
///
/// Force finish the SDO and trigger its finished event
///
public void SDOFinish()
{
can.SDOcallbacks.Remove((UInt16)(this.node + 0x580));
finishedevent.Set();
}
///
/// SDO Instance processor, process current SDO reply and decide what to do next
///
/// SDO Canpacket to process
///
public bool SDOProcess(CanPacket cp)
{
int SCS = cp.data[0] >> 5; //7-5
int n = (0x03 & (cp.data[0] >> 2)); //3-2 data size for normal packets
int e = (0x01 & (cp.data[0] >> 1)); // expidited flag
int s = (cp.data[0] & 0x01); // data size set flag
int sn = (0x07 & (cp.data[0] >> 1)); //3-1 data size for segment packets
int t = (0x01 & (cp.data[0] >> 4)); //toggle flag
int c = 0x01 & cp.data[0]; //More segments to upload?
//ERROR abort
if (SCS == 0x04)
{
expitideddata = (UInt32)(cp.data[4] + (cp.data[5] << 8) + (cp.data[6] << 16) + (cp.data[7] << 24));
databuffer = BitConverter.GetBytes(expitideddata);
state = SDO_STATE.SDO_ERROR;
Console.WriteLine("SDO Error on {0:x4}/{1:x2} {2:x8}", this.index, this.subindex, expitideddata);
if (completedcallback != null)
completedcallback(this);
return true;
}
//Write complete
if (SCS == 0x03)
{
UInt16 index = (UInt16)(cp.data[1] + (cp.data[2] << 8));
byte sub = cp.data[3];
int node = cp.cob - 0x580;
foreach (SDO sdo in activeSDO)
{
if (sdo.node == node)
{
if ((index == sdo.index && sub == sdo.subindex)) //if segments break its here
{
if (expitided == false)
{
state = SDO_STATE.SDO_HANDSHAKE;
requestNextSegment(false);
return false;
}
}
}
}
state = SDO_STATE.SDO_FINISHED;
if (completedcallback != null)
completedcallback(this);
return true;
}
//Write segment complete
if (SCS == 0x01)
{
if (totaldata < expitideddata)
{
lasttoggle = !lasttoggle;
requestNextSegment(lasttoggle);
}
else
{
state = SDO_STATE.SDO_FINISHED;
if (completedcallback != null)
completedcallback(this);
}
}
//if expedited just handle the data
if (SCS == 0x02 && e == 1)
{
//Expidited and length are set so its a regular short transfer
expitideddata = (UInt32)(cp.data[4] + (cp.data[5] << 8) + (cp.data[6] << 16) + (cp.data[7] << 24));
databuffer = BitConverter.GetBytes(expitideddata);
state = SDO_STATE.SDO_FINISHED;
if (completedcallback != null)
{
try
{
completedcallback(this);
}
catch { }
}
return true;
}
if (SCS == 0x02)
{
UInt32 count = (UInt32)(cp.data[4] + (cp.data[5] << 8) + (cp.data[6] << 16) + (cp.data[7] << 24));
Console.WriteLine("RX Segmented transfer start length is {0}", count);
expitideddata = count;
databuffer = new byte[expitideddata];
totaldata = 0;
//Request next segment
if (dir==Direction.SDO_WRITE)
{
dir = Direction.SDO_READ;//这里有一个Bug,但目前上位机只需要上载协议,没有下载,暂时先这么修改 20220531,后期完善
}
requestNextSegment(false); //toggle off on first request
return false;
}
//segmented transfer
UInt32 scount = (UInt32)(7 - sn);
//Segments toggle on
if (SCS == 0x00)
{
Console.WriteLine("RX Segmented transfer update length is {0} -- {1}", scount, totaldata);
if (downLoadScheduleEvent!=null)
{
downLoadScheduleEvent((totaldata+ scount )/ (float)databuffer.Length);
Console.WriteLine("{0}", (totaldata + scount) / (float)databuffer.Length);
}
for (int x = 0; x < scount; x++)
{
if ((totaldata + x) < databuffer.Length)
databuffer[totaldata + x] = cp.data[1 + x];
}
totaldata += 7;
if ((totaldata < expitideddata) && c == 0)
{
lasttoggle = !lasttoggle;
requestNextSegment(lasttoggle);
}
else
{
state = SDO_STATE.SDO_FINISHED;
if (completedcallback != null)
completedcallback(this);
if (downLoadDelegateEvent != null)
{
downLoadDelegateEvent(databuffer);
}
}
}
return false;
}
///
/// Request the next segment in a segmented transfet
///
/// Segmented transfer toggle flag, should alternate for each successive transfer
private void requestNextSegment(bool toggle)
{
timeout = DateTime.Now + new TimeSpan(0, 0, 5);
if (dir == Direction.SDO_READ)
{
byte cmd = 0x60;//0x61
if (toggle)
cmd |= 0x10;
sendpacket(cmd, new byte[4],0,0);
}
else
{
byte cmd = 0x00;
if (toggle)
cmd |= 0x10;
int bytecount = (int)(databuffer.Length - totaldata); //11 - 7
byte[] nextdata;
if (bytecount >= 7)
{
bytecount = 7;
}
nextdata = new byte[bytecount];
for (int x = 0; x < bytecount; x++)
{
if (databuffer.Length > (totaldata + x))
nextdata[x] = databuffer[totaldata + x];
}
if (totaldata + 7 >= databuffer.Length)
{
cmd |= 0x01; //END of packet sequence
}
if (bytecount != 7)
{
int n = 7 - bytecount;
n = n << 1;
cmd |= (byte)n;
}
sendpacketsegment(cmd, nextdata);
totaldata += (uint)bytecount;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MCUCommunication.Protocols
{
public class SDOErr
{
private static readonly Lazy<SDOErr> lazy = new Lazy<SDOErr>(() => new SDOErr());
public static SDOErr Instance => lazy.Value;
#region SDO错误字典
public static Dictionary<string, string> SDOErrTable = new Dictionary<string, string>
{
{"05030000","触发位没有交替改变"},
{"05040000","SDO协议超时"},
{"05040001","非法或未知的Client/Server命令字"},
{"05040002","无效的块大小(仅Block Transfer模式"},
{"05040003","无效的序号(仅Block Transfer模式"},
{"05040004","CRC错误(仅Block Transfer模式"},
{"05040005","内存溢出" },
{"06010000","对象不支持访问" },
{"06010001","试图读只写对象" },
{"06010002","试图写只读对象" },
{"06020000","对象字典中对象不存在" },
{"06040041","对象不能够映射到PDO" },
{"06040042","映射的对象的数目和长度超出PDO长度" },
{"06040043","一般性参数不兼容" },
{"06040047","一般性设备内部不兼容" },
{"06060000","硬件错误导致对象访问失败" },
{"06070010","数据类型不匹配,服务参数长度不匹配" },
{"06070012","数据类型不匹配,服务参数长度太大" },
{"06070013","数据类型不匹配,服务参数长度太短" },
{"06090011","子索引不存在" },
{"06090030","超出参数的值范围(写访问)" },
{"06090031","写入参数数值太大" },
{"06090032","写入参数数值太小" },
{"06090036","最大值小于最小值" },
{"060A0023","资源不可用:SDO连接" },
{"08000000","一般性错误" },
{"08000020","数据不能传送或保存到应用" },
{"08000021","由于本地控制导致数据不能传送或保存到应用" },
{"08000022","由于当前设备状态导致数据不能传送或保存到应用" },
{"08000023","对象字典动态产生错误或对象字典不存在" },
};
#endregion
#region 构造函数
public SDOErr()
{
}
#endregion
#region 方法
#region 通过错误码查询错误描述
public string GetSDOErrSubscribersFormErrNum(string ErrNum)
{
if (SDOErrTable.ContainsKey(ErrNum))
{
return SDOErrTable[ErrNum];
}
else
{
return null;
}
}
#endregion
#endregion
}
}
基本写完了,最后只需要先调用CANDeviceStart(),打开CAN,然后调用CANDataSend,进行发送数据。如果接收到数据,USBCANReceiveData接收委托事件关联就会响应。