当前位置:   article > 正文

Python实现——CAN报文转换工具_python cantools dump

python cantools dump

一.CAN报文简介


CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的 现场总线 之一。 在北美和西欧,CAN总线 协议 已经成为 汽车计算机控制系统 和嵌入式工业控制局域网的标准总线,并且拥有以CAN为底层协议专为大型货车和重工机械车辆设计的J1939协议。

CAN总线以报文为单位进行数据传送。CAN报文按照帧格式可分为标准帧和扩展帧,标准帧是具有11位标识符的CAN帧,扩展帧是具有29位标识符的CAN帧。按照帧类型可分为:1.从发送节点向其它节点发送数据;2.远程帧:向其它节点请求发送具有同一识别符的数据帧;3.错误帧:指明已检测到总线错误;4.过载帧:过载帧用以在数据帧(或远程帧)之间提供一附加的延时。共有两种编码格式:Intel格式和Motorola格式,在编码优缺点上,Motorola格式与Intel格式并没有孰优孰劣之分,只不过根据设计者的习惯,由用户自主选择罢了。当然,对于使用者来讲,在进行解析之前,就必须要知道编码的格式是哪一种,否则,就不能保证正确地解析信号的含义。以下就以8位字节编码方式的CAN总线信号为例,详细分析一下两者之间的区别。

Intel编码格式
当一个信号的数据长度不超过1个字节(8位)并且信号在一个字节内实现(即该信号没有跨字节实现):该信号的高位(S_msb)将被放在该字节的高位,信号的低位(S_lsb)将被放在该字节的低位。
当一个信号的数据长度超过1个字节(8位)或者数据长度不超过一个字节但是采用跨字节方式实现时:该信号的高位(S_msb)将被放在高字节(MSB)的高位,信号的低位(S_lsb)将被放在低字节(LSB)的低位。
Motorola编码格式
当一个信号的数据长度不超过1个字节(8位)并且信号在一个字节内实现(即该信号没有跨字节实现):该信号的高位(S_msb)将被放在该字节的高位,信号的低位(S_lsb)将被放在该字节的低位。
当一个信号的数据长度超过1个字节(8位)或者数据长度不超过一个字节但是采用跨字节方式实现时:该信号的高位(S_msb)将被放在低字节(MSB)的高位,信号的低位(S_lsb)将被放在高字节(LSB)的低位。
可以看出,当一个信号的数据长度不超过1Byte时,Intel与Motorola两种格式的编码结果没有什么不同,完全一样。当信号的数据长度超过1Byte时,两者的编码结果出现了明显的不同。

二.CAN报文转换工具需求分析


1、 支持标准帧的CAN报文的转换,扩展帧暂不支持
2、 CAN报文支持Intel、motorola两种编码,先支持motorola格式,后期追加Intel格式
3、 工具具有一定的容错处理能力、报告生成能力
4、 制定统一格式,方便使用者修改测试脚本
5、增加交互模式,键盘输入,控制台输出;例如:
提示语:startBit:length:minValue:maxValue:setValue
输入:35:1:0:1:1
或:35:1:::1
控制台输出:00 00 00 00 08 00 00 00

Intel和Motorola编码举例:




三.交互模式


代码如下:
  1. import sys
  2. print("----------------欢迎使用CAN报文转换工具交互模式----------------")
  3. print("请输入CAN信号,格式为:startBit:length:minValue:maxValue:setValue")
  4. print("例如:32:1:0:1:1")
  5. print("或者省略minValue和maxValue:35:1:::1")
  6. print("信号输入结束请再按一次回车")
  7. #十进制转换成二进制list
  8. def octToBin(octNum, bit):
  9. while(octNum != 0):
  10. bit.append(octNum%2)
  11. octNum = int(octNum/2)
  12. for i in range(64-len(bit)):
  13. bit.append(0)
  14. sig = []
  15. startBit = []
  16. length = []
  17. setValue = []
  18. #输入CAN信号
  19. while True:
  20. input_str = input()
  21. if not len(input_str):
  22. break
  23. if(input_str.count(":")<4):
  24. print("输入格式错误,参数缺少setValue,请重新输入!")
  25. continue
  26. if(input_str.split(":")[4]==""):
  27. print("setValue参数不能为空,请重新输入!")
  28. continue
  29. sig.append(input_str)
  30. #解析CAN信号
  31. for i in range(len(sig)):
  32. startBit.append(int(sig[i].split(":")[0]))
  33. length.append(int(sig[i].split(":")[1]))
  34. setValue.append(int(sig[i].split(":")[4]))
  35. #CAN数组存放CAN报文值
  36. CAN = []
  37. for i in range(64):
  38. CAN.append(-1)
  39. for i in range(len(startBit)):
  40. #长度超过1Byte的情况,暂不支持
  41. if(length[i]>16):
  42. print("CAN信号长度超过2Byte,暂不支持!!!")
  43. sys.stdin.readline()
  44. sys.exit()
  45. #长度未超过1Byte的情况且未跨字节的信号
  46. if((startBit[i]%8 + length[i])<=8):
  47. for j in range(length[i]):
  48. bit = []
  49. #setValue的二进制值按字节位从低到高填
  50. octToBin(setValue[i],bit)
  51. #填满字节长度值
  52. if(CAN[startBit[i]+j]==-1):
  53. CAN[startBit[i]+j] = bit[j]
  54. #字节存在冲突
  55. else:
  56. print(sig[i] + "字节位存在冲突,生成CAN报文失败!!!")
  57. sys.stdin.readline()
  58. sys.exit()
  59. #跨字节的信号
  60. else:
  61. #高位位数和低位位数
  62. highLen = 8 - startBit[i]%8
  63. lowLen = length[i] - highLen
  64. bit = []
  65. #setValue的二进制值按字节位从低到高填
  66. octToBin(setValue[i],bit)
  67. #先填进信号的高位
  68. for j1 in range(highLen):
  69. if(CAN[startBit[i]+j1]==-1):
  70. CAN[startBit[i]+j1] = bit[j1]
  71. #字节存在冲突
  72. else:
  73. print(sig[i] + "字节位存在冲突,生成CAN报文失败!!!")
  74. sys.stdin.readline()
  75. sys.exit()
  76. #再填进信号的低位
  77. for j2 in range(lowLen):
  78. if(CAN[(int(startBit[i]/8)-1)*8+j2]==-1):
  79. CAN[(int(startBit[i]/8)-1)*8+j2] = bit[highLen+j2]
  80. #字节存在冲突
  81. else:
  82. print(sig[i] + "字节位存在冲突,生成CAN报文失败!!!")
  83. sys.stdin.readline()
  84. sys.exit()
  85. #剩余位默认值设为0
  86. for i in range(64):
  87. if(CAN[i]==-1):
  88. CAN[i] = 0
  89. #----------------将二进制list每隔8位转换成十六进制输出----------------
  90. #其中,map()将list中的数字转成字符串,按照Motorola格式每隔8位采用了逆序
  91. # ''.join()将二进制list转换成二进制字符串,int()将二进制字符串转换成十进制
  92. #hex()再将十进制转换成十六进制,upper()转换成大写,两个lstrip()将"0X"删除,
  93. #zfill()填充两位,输出不换行,以空格分隔
  94. print(hex(int(''.join(map(str,CAN[7::-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  95. print(hex(int(''.join(map(str,CAN[15:7:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  96. print(hex(int(''.join(map(str,CAN[23:15:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  97. print(hex(int(''.join(map(str,CAN[31:23:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  98. print(hex(int(''.join(map(str,CAN[39:31:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  99. print(hex(int(''.join(map(str,CAN[47:39:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  100. print(hex(int(''.join(map(str,CAN[55:47:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  101. print(hex(int(''.join(map(str,CAN[63:55:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))

运行截图:


错误提示:


四.配置项模式


配置文件如下:

  1. ##注释
  2. ::start
  3. #编码格式:0=Intel;1=Motorola
  4. encodeType=1
  5. #帧格式:0=标准帧;1=扩展帧;
  6. canMode=0
  7. #帧类型:0=数据帧;...
  8. canType=0
  9. #默认初始值(0~1)
  10. defaultValue=0
  11. #MSG定义
  12. msgName=BCM_FrP01
  13. msgID=0x2CD
  14. #长度(BYTE)
  15. msgLength=8
  16. #signal定义
  17. #sigName=name:startBit:length:minValue:maxValue:setValue
  18. #sigName=ReverseSw:25:6:0:1:13
  19. #sigName=Trunk_BackDoor_Sts:33:2:0:1:2
  20. #sigName=DRVUnlockState:37:2:0:1:3
  21. #sigName=HeadLampLowBeam:40:8:0:1:60
  22. #sigName=HoodStatus:51:1:0:1:0
  23. #sigName=HeadLampHighBeam:52:1:0:1:0
  24. #sigName=RLDoorStatus:59:1:0:1:0
  25. #sigName=RRDoorStatus:58:1:0:1:0
  26. #sigName=PsgDoorStatus:57:2:0:1:0
  27. sigName=One:0:8:0:255:165
  28. sigName=Two:24:12:0:4095:1701
  29. sigName=Three:54:5:0:31:25
  30. ::end
  31. ::start
  32. #编码格式:0=Intel;1=Motorola
  33. encodeType=1
  34. #帧格式:0=标准帧;1=扩展帧;
  35. canMode=0
  36. #帧类型:0=数据帧;...
  37. canType=0
  38. #默认初始值(0~1)
  39. defaultValue=0
  40. #MSG定义
  41. msgName=BCM_FrP
  42. msgID=0x2CD
  43. #长度(BYTE)
  44. msgLength=8
  45. #signal定义
  46. #sigName=name:startBit:length:minValue:maxValue:setValue
  47. #sigName=ReverseSw:25:6:0:1:13
  48. #sigName=Trunk_BackDoor_Sts:33:2:0:1:2
  49. #sigName=DRVUnlockState:37:2:0:1:3
  50. #sigName=HeadLampLowBeam:40:8:0:1:60
  51. #sigName=HoodStatus:51:1:0:1:0
  52. #sigName=HeadLampHighBeam:52:1:0:1:0
  53. #sigName=RLDoorStatus:59:1:0:1:0
  54. #sigName=RRDoorStatus:58:1:0:1:0
  55. #sigName=PsgDoorStatus:57:2:0:1:0
  56. sigName=One:35:1:0:1:1
  57. ::end

代码如下:

  1. #!/usr/bin/python
  2. defaultValue = 0
  3. sigName = []
  4. startBit = []
  5. length = []
  6. minValue = []
  7. maxValue = []
  8. setValue = []
  9. #CAN数组存放CAN报文值
  10. CAN = []
  11. logFile = open("log.txt","w")
  12. def parseConfig():
  13. config = open("Config.txt","r")
  14. count = 0
  15. isError = False
  16. for line in config:
  17. line = line.strip()
  18. #注释
  19. if(line.find("#")>=0):
  20. continue
  21. #开始标记
  22. elif(line.find("::start")>=0):
  23. count = count + 1
  24. isError = False
  25. if(count>1):
  26. sigName.clear()
  27. startBit.clear()
  28. length.clear()
  29. setValue.clear()
  30. continue
  31. else:
  32. continue
  33. elif(isError == True):
  34. continue
  35. #编码格式
  36. elif(line.find("encodeType")>=0):
  37. encodeType = line.split("=")[1]
  38. if(encodeType != "1"):
  39. isError = True
  40. print(str(count) + ". CAN报文生成失败!!!目前仅支持Motorola编码格式,暂不支持Intel编码格式!")
  41. logFile.write("%d. CAN报文生成失败!!!目前仅支持Motorola编码格式,暂不支持Intel编码格式!\n" % count)
  42. continue
  43. #帧格式
  44. elif(line.find("canMode")>=0):
  45. canMode = line.split("=")[1]
  46. if(canMode != "0"):
  47. isError = True
  48. print(str(count) + ". CAN报文生成失败!!!目前仅支持标准帧,暂不支持扩展帧!")
  49. logFile.write("%d. CAN报文生成失败!!!目前仅支持标准帧,暂不支持扩展帧!\n" % count)
  50. continue
  51. #帧类型
  52. elif(line.find("canType")>=0):
  53. canType = line.split("=")[1]
  54. if(canType != "0"):
  55. isError = True
  56. print(str(count) + ". CAN报文生成失败!!!目前仅支持数据帧,暂不支持其他帧!")
  57. logFile.write("%d. CAN报文生成失败!!!目前仅支持数据帧,暂不支持其他帧!\n" % count)
  58. continue
  59. #默认初始值
  60. elif(line.find("defaultValue")>=0):
  61. global defaultValue
  62. defaultValue = int(line.split("=")[1])
  63. #MSG名称
  64. elif(line.find("msgName")>=0):
  65. msgName = line.split("=")[1]
  66. #MSGID
  67. elif(line.find("msgID")>=0):
  68. msgID = line.split("=")[1]
  69. #MSG长度
  70. elif(line.find("msgLength")>=0):
  71. msgLength = line.split("=")[1]
  72. #signal定义
  73. elif(line.find("sigName")>=0):
  74. sigName.append(line.split(":")[0].split("=")[1])
  75. startBit.append(int(line.split(":")[1]))
  76. length.append(int(line.split(":")[2]))
  77. #minValue.append(int(line.split(":")[3]))
  78. #maxValue.append(int(line.split(":")[4]))
  79. setValue.append(int(line.split(":")[5]))
  80. elif(line.find("::end")>=0):
  81. rV,errMsg = getCANMessage()
  82. if(rV == "-1"):
  83. isError = True
  84. print(str(count) + ". CAN报文生成失败!!!" + errMsg)
  85. logFile.write("%d. CAN报文生成失败!!!%s\n" % (count,errMsg))
  86. continue
  87. print(str(count) + ". CAN报文生成成功!!!")
  88. logFile.write("%d. CAN报文生成成功!!!\n" % count)
  89. #----------------------------输出标题信息----------------------------
  90. print("msgName\t\tmsgID\t\tmsgLen\t\tmsgData")
  91. logFile.write("msgName\t\tmsgID\t\tmsgLen\t\tmsgData\n")
  92. if(len(msgName)<8):
  93. print(msgName + "\t\t",end="")
  94. logFile.write("%s\t\t" % msgName)
  95. else:
  96. print(msgName + "\t",end="")
  97. logFile.write("%s\t" % msgName)
  98. print(msgID + "\t\t",end="")
  99. logFile.write("%s\t\t" % msgID)
  100. print(msgLength + "\t\t",end="")
  101. logFile.write("%s\t\t" % msgLength)
  102. #----------------将二进制list每隔8位转换成十六进制输出----------------
  103. #其中,map()将list中的数字转成字符串,按照Motorola格式每隔8位采用了逆序
  104. # ''.join()将二进制list转换成二进制字符串,int()将二进制字符串转换成十进制
  105. #hex()再将十进制转换成十六进制,upper()转换成大写,两个lstrip()将"0X"删除,
  106. #zfill()填充两位,输出不换行,以空格分隔
  107. print(hex(int(''.join(map(str,CAN[7::-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  108. print(hex(int(''.join(map(str,CAN[15:7:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  109. print(hex(int(''.join(map(str,CAN[23:15:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  110. print(hex(int(''.join(map(str,CAN[31:23:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  111. print(hex(int(''.join(map(str,CAN[39:31:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  112. print(hex(int(''.join(map(str,CAN[47:39:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  113. print(hex(int(''.join(map(str,CAN[55:47:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  114. print(hex(int(''.join(map(str,CAN[63:55:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  115. logFile.write("%s " % hex(int(''.join(map(str,CAN[7::-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  116. logFile.write("%s " % hex(int(''.join(map(str,CAN[15:7:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  117. logFile.write("%s " % hex(int(''.join(map(str,CAN[23:15:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  118. logFile.write("%s " % hex(int(''.join(map(str,CAN[31:23:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  119. logFile.write("%s " % hex(int(''.join(map(str,CAN[39:31:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  120. logFile.write("%s " % hex(int(''.join(map(str,CAN[47:39:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  121. logFile.write("%s " % hex(int(''.join(map(str,CAN[55:47:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  122. logFile.write("%s\n" % hex(int(''.join(map(str,CAN[63:55:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  123. config.close()
  124. #十进制转换成二进制list
  125. def octToBin(octNum, bit):
  126. while(octNum != 0):
  127. bit.append(octNum%2)
  128. octNum = int(octNum/2)
  129. for i in range(64-len(bit)):
  130. bit.append(0)
  131. #获取CAN报文值
  132. def getCANMessage():
  133. CAN.clear()
  134. for i in range(64):
  135. CAN.append(-1)
  136. for i in range(len(startBit)):
  137. #长度超过1Byte的情况,暂不支持
  138. if(length[i]>16):
  139. errMsg = " CAN信号长度超过2Byte,暂不支持!!!"
  140. #print(sigName[i] + errMsg)
  141. return "-1",errMsg
  142. #长度未超过1Byte的情况且未跨字节的信号
  143. if((startBit[i]%8 + length[i])<=8):
  144. for j in range(length[i]):
  145. bit = []
  146. #setValue的二进制值按字节位从低到高填
  147. octToBin(setValue[i],bit)
  148. #填满字节长度值
  149. if(CAN[startBit[i]+j]==-1):
  150. CAN[startBit[i]+j] = bit[j]
  151. #字节存在冲突
  152. else:
  153. errMsg = " 字节位存在冲突,生成CAN报文失败!!!"
  154. #print(sigName[i] + errMsg)
  155. return "-1",errMsg
  156. #跨字节的信号
  157. else:
  158. #高位位数和低位位数
  159. highLen = 8 - startBit[i]%8
  160. lowLen = length[i] - highLen
  161. bit = []
  162. #setValue的二进制值按字节位从低到高填
  163. octToBin(setValue[i],bit)
  164. #先填进信号的高位
  165. for j1 in range(highLen):
  166. if(CAN[startBit[i]+j1]==-1):
  167. CAN[startBit[i]+j1] = bit[j1]
  168. #字节存在冲突
  169. else:
  170. errMsg = " 字节位存在冲突,生成CAN报文失败!!!"
  171. #print(sigName[i] + errMsg)
  172. return "-1",errMsg
  173. #再填进信号的低位
  174. for j2 in range(lowLen):
  175. if(CAN[(int(startBit[i]/8)-1)*8+j2]==-1):
  176. CAN[(int(startBit[i]/8)-1)*8+j2] = bit[highLen+j2]
  177. #字节存在冲突
  178. else:
  179. errMsg = " 字节位存在冲突,生成CAN报文失败!!!"
  180. #print(sigName[i] + errMsg)
  181. return "-1",errMsg
  182. #剩余位设为默认值
  183. for i in range(64):
  184. if(CAN[i]==-1):
  185. CAN[i] = defaultValue
  186. #若无错误则返回正确值
  187. return "0","success!"
  188. if __name__ == "__main__":
  189. #调用parseConfig()函数开始执行程序
  190. parseConfig()

运行结果:

  1. 1. CAN报文生成成功!!!
  2. msgName msgID msgLen msgData
  3. BCM_FrP01 0x2CD 8 A5 00 06 A5 00 06 40 00
  4. 2. CAN报文生成成功!!!
  5. msgName msgID msgLen msgData
  6. BCM_FrP 0x2CD 8 00 00 00 00 08 00 00 00











声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/1011011
推荐阅读
相关标签
  

闽ICP备14008679号