1.  添加注释
2. 添加粘包分包解码器
3. 修改数据单元信息体获取逻辑
4. 修改接收数据转换后的string列表,展示为16进制数时,小于16的应该在前面补0,否则之后计算会出错
已修改22个文件
已添加3个文件
436 ■■■■ 文件已修改
src/main/kotlin/com/flightfeather/obd/common/utils/FileUtil.kt 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/common/utils/TimeUtil.kt 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/repository/impl/ObdDataDaoImpl.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/MessageManager.kt 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/ObdByteDataDecoder.kt 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/ServerHandler.kt 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/SocketServerClient.kt 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/CarLogOutData.kt 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/CarRegisterData.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/DataUnit.kt 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/EngineDataFlow.kt 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/ObdData.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/ObdPackageData.kt 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/RealTimeData.kt 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/ReplacementData.kt 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/SupplementDataFlow.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/TimeCalibrationData.kt 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/DataPackageDecoderImpl.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/DataUnitDecoderImpl.kt 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/RealTimeDataDecoderImpl.kt 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/eunm/ObdCommandUnit.kt 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/eunm/ObdDataType.kt 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/log4j2.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/obd/Test.kt 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/obd/socket/MessageManagerTest.kt 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/common/utils/FileUtil.kt
@@ -17,11 +17,13 @@
    private var closeThread: Thread? = null
    private var fw: FileWriter? = null
    private var bw: BufferedWriter? = null
    private var oldTime: Date
    init {
        val fileName = "data-${SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())}.txt"
        val path = "$basePath$fileName"
        file = File(path)
        oldTime = Date()
    }
    companion object{
@@ -43,7 +45,10 @@
            println("----创建文件:${file.absolutePath}")
        }
        //文件最大512Kb,超过后新建文档
        if (file.length() + str.toByteArray().size > 512 * 1024) {
        if (file.length() + str.toByteArray().size > 512 * 1024 || TimeUtil.isNextDay(oldTime, Date())) {
            //超过一天后,更新当前时间
            oldTime = Date()
            val fileName = "data-${SimpleDateFormat("yyyy-MM-dd-hh-mm-ss").format(Date())}.txt"
            val path = "$basePath$fileName"
            file = File(path)
src/main/kotlin/com/flightfeather/obd/common/utils/TimeUtil.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.flightfeather.obd.common.utils
import java.util.*
/**
 * @author riku
 * Date: 2019/9/16
 */
class TimeUtil {
    companion object {
        /**
         * æ˜¯å¦æ˜¯ç¬¬äºŒå¤©æˆ–更新的时间
         */
        fun isNextDay(oldTime: Date, newTime: Date): Boolean {
            val oldC = Calendar.getInstance().apply {
                time = oldTime
            }
            val newC = Calendar.getInstance().apply {
                time = oldTime
            }
            return when {
                newC[Calendar.YEAR] > oldC[Calendar.YEAR] -> true
                newC[Calendar.YEAR] == oldC[Calendar.YEAR] -> newC[Calendar.DAY_OF_YEAR] > oldC[Calendar.DAY_OF_YEAR]
                else -> false
            }
        }
    }
}
src/main/kotlin/com/flightfeather/obd/repository/impl/ObdDataDaoImpl.kt
@@ -50,7 +50,7 @@
    override fun saveObdData(packageData: ObdPackageData): Boolean {
        val obdData = ObdData().apply {
            obdVin = packageData.vinCode
            obdVin = packageData.deviceCode
        }
        when (packageData.commandUnit) {
            ObdCommandUnit.CarRegister.value -> {
src/main/kotlin/com/flightfeather/obd/socket/MessageManager.kt
@@ -1,17 +1,16 @@
package com.flightfeather.obd.socket
import com.flightfeather.obd.common.utils.FileUtil
import com.flightfeather.obd.lightshare.bean.BaseJson
import com.flightfeather.obd.lightshare.bean.ObdDataVo
import com.flightfeather.obd.repository.ObdDataRepository
import com.flightfeather.obd.socket.bean.ObdPackageData
import com.flightfeather.obd.socket.decoder.VehicleDataDecoder
import com.google.gson.Gson
import com.flightfeather.obd.socket.decoder.impl.DataPackageDecoderImpl
import io.netty.channel.ChannelHandlerContext
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.util.FileSystemUtils
import java.text.SimpleDateFormat
import java.util.*
import javax.annotation.PostConstruct
import javax.annotation.Resource
/**
 * å¤„理socket接收的消息
@@ -30,6 +29,7 @@
    lateinit var obdDataRepository: ObdDataRepository
    val vehicleDataDecoder = VehicleDataDecoder()
    val dataPackageDecoder = DataPackageDecoderImpl()
    @PostConstruct
    fun init() {
@@ -38,29 +38,57 @@
    }
    fun dealStringMsg(msg: String, ctx: ChannelHandlerContext?) {
//        try {
//            val baseJson = Gson().fromJson<BaseJson>(msg, BaseJson::class.java)
//            when (baseJson.cmdCode) {
//                2001 -> {
//                    val data = Gson().fromJson(msg, ObdDataVo::class.java)
//                    DeviceSession.saveDevice(data.obdVin, ctx)
//                    instance.obdDataRepository.saveObdData(data)
//                }
//            }
//        } catch (e: Throwable) {
//        }
        saveToTxt(msg)
        if (bccCheck(msg)) {
            //解包
        val packageData = vehicleDataDecoder.decode(msg)
            //保存
            DeviceSession.saveDevice(packageData.deviceCode, ctx)
            saveToDataBase(packageData)
        } else {
            println("------数据BCC校验失败,舍弃 [${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())}]")
        }
    }
    fun dealByteArrayMsg(msg: ByteArray, ctx: ChannelHandlerContext?) {
        val b = ByteArray(20) {19}
        println(b)
    }
    /**
     * ä¿å­˜è‡³txt文本
     */
    fun saveToTxt(msg: String) {
        val data = "data=> $msg"
        val data = "[${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())}]data=> $msg"
        FileUtil.instance?.saveObdData(data)
    }
    /**
     * ä¿å­˜è‡³æ•°æ®åº“
     */
    fun saveToDataBase(packageData: ObdPackageData) {
        instance.obdDataRepository.saveObdData(packageData)
    }
    /**
     * BCC(异或校验)
     */
    fun bccCheck(msg: String):Boolean {
        val list = mutableListOf<String>().apply {
            addAll(dataPackageDecoder.toStringList(msg))
            //去除2 ä½èµ·å§‹ç¬¦
            removeAt(0)
            removeAt(0)
        }
        //取得数据包中的bcc校验结果
        val oldBcc = list[list.size - 1].toInt(16)
        //去除校验结果
        list.removeAt(list.size-1)
        //计算bcc校验结果
        var newBcc = 0x00
        list.forEach {
            newBcc = newBcc.xor(it.toInt(16))
        }
        //返回校验结果是否正确
        return oldBcc == newBcc
    }
}
src/main/kotlin/com/flightfeather/obd/socket/ObdByteDataDecoder.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,112 @@
package com.flightfeather.obd.socket
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import java.lang.StringBuilder
/**
 * @author riku
 * Date: 2019/9/16
 * æ•°æ®ç²˜åŒ…分包解码器
 */
class ObdByteDataDecoder : ByteToMessageDecoder() {
    companion object {
        const val BASE_LENGTH = 2 + 1 + 17 + 1 + 1 + 2 + 0 + 1
        const val HEAD = 0x23.toByte()
    }
    override fun decode(p0: ChannelHandlerContext?, p1: ByteBuf?, p2: MutableList<Any>?) {
        val dataList = mutableListOf<Byte>()
        p1?.let {
            // å¯è¯»é•¿åº¦å¿…须大于基本长度
            if (it.readableBytes() >= BASE_LENGTH) {
                // é˜²æ­¢socket字节流攻击
                // é˜²æ­¢ï¼Œå®¢æˆ·ç«¯ä¼ æ¥çš„æ•°æ®è¿‡å¤§
                if (it.readableBytes() > 2048) {
                    it.skipBytes(it.readableBytes())
                }
                // è®°å½•包头开始的index
                var beginReader = 0
                while (true) {
                    // èŽ·å–åŒ…å¤´å¼€å§‹çš„index
                    beginReader = it.readerIndex()
                    // æ ‡è®°åŒ…头开始的index
                    it.markReaderIndex()
                    // è¯»åˆ°äº†åè®®çš„开始标志,结束while循环
                    val b = ByteArray(2)
                    it.readBytes(b)
                    if (b[0] == HEAD && b[1] == HEAD) {
                        dataList.add(b[0])
                        dataList.add(b[1])
                        break
                    }
                    // æœªè¯»åˆ°åŒ…头,重置到之前记录的包头开始位置
                    // ç•¥è¿‡1个字节后,再循环开始读取
                    it.resetReaderIndex()
                    it.readByte()
                    //当数据包长度不足时,立刻结束,等待后续数据到达
                    if (it.readableBytes() < BASE_LENGTH) {
                        return
                    }
                }
                ByteArray(1 + 17 + 1 + 1).apply {
                    it.readBytes(this)
                }.forEach {b ->
                    dataList.add(b)
                }
                //数据单元的长度
                val length = getDataUnitLength(it, dataList)
                // åˆ¤æ–­è¯·æ±‚数据单元数据及1个字节的校验码是否到齐
                if (it.readableBytes() < length + 1) {
                    // è¿˜åŽŸè¯»æŒ‡é’ˆ
                    it.readerIndex(beginReader)
                    return
                }
                //读取数据单元和校验码数据
                ByteArray(length + 1).apply {
                    it.readBytes(this)
                }.forEach {b ->
                    dataList.add(b)
                }
                p2?.add(dataList.toByteArray())
            }
        }
    }
    /**
     * èŽ·å–æ•°æ®å•å…ƒé•¿åº¦å¹¶å°†å…¶æ·»åŠ è‡³ç»“æžœåˆ—è¡¨ä¸­
     */
    private fun getDataUnitLength(p1: ByteBuf, dataList: MutableList<Byte>): Int {
        val sb = StringBuilder()
        ByteArray(2).apply {
            p1.readBytes(this)
        }.forEach { b ->
            var a = 0
            a = if (b < 0) {
                b + 256
            } else {
                b.toInt()
            }
            sb.append(a.toString(16))
            dataList.add(b)
        }
        return sb.toString().toInt(16)
    }
}
src/main/kotlin/com/flightfeather/obd/socket/ServerHandler.kt
@@ -35,8 +35,13 @@
                } else {
                    it.toInt()
                }
                print("${a.toString(16)} ")
                sb.append(a.toString(16)).append(" ")
                val s = if (a < 16) {
                    "0${a.toString(16)}"
                } else {
                    a.toString(16)
                }
                print(s)
                sb.append(s).append(" ")
            }
            sb.deleteCharAt(sb.length - 1)
        }
src/main/kotlin/com/flightfeather/obd/socket/SocketServerClient.kt
@@ -1,11 +1,14 @@
package com.flightfeather.obd.socket
import io.netty.bootstrap.ServerBootstrap
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.DelimiterBasedFrameDecoder
import io.netty.handler.codec.bytes.ByteArrayDecoder
import io.netty.handler.codec.bytes.ByteArrayEncoder
import io.netty.handler.codec.string.StringDecoder
@@ -44,8 +47,7 @@
                            p0?.pipeline()
//                                    ?.addLast("decoder", StringDecoder())
//                                    ?.addLast("encoder", StringEncoder())
                                    ?.addLast(ByteArrayDecoder())
                                    ?.addLast(ByteArrayEncoder())
                                    ?.addLast(ObdByteDataDecoder())
                                    ?.addLast(ServerHandler())
                        }
                    })
src/main/kotlin/com/flightfeather/obd/socket/bean/CarLogOutData.kt
@@ -1,14 +1,19 @@
package com.flightfeather.obd.socket.bean
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
import java.util.*
/**
 * @author riku
 * Date: 2019/9/12
 * è½¦è¾†ç™»å‡ºä¿¡æ¯
 * æ•°æ®ç±»åž‹ @see [ObdCommandUnit.CarLogOut]
 *
 * æ•°æ®è¡¨ç¤ºå†…容                   é•¿åº¦ï¼ˆå­—节)                  æ•°æ®ç±»åž‹                    æè¿°åŠè¦æ±‚
 * ç™»å‡ºæ—¶é—´                             6                                   BYTE[6]                   æ—¶é—´å‡åº”采用 GMT+8 æ—¶é—´ï¼Œæ—¶é—´å®šä¹‰ç¬¦åˆ GB/T32960.3-2016 ç¬¬ 6.4 æ¡çš„要求
 * ç™»å‡ºæµæ°´å·                          2                                  WORD                      ç™»å‡ºæµæ°´å·ä¸Žå½“次登入流水号一致。@see [CarRegisterData]
 */
class CarLogOutData(
        var time: Date? = null,
        var serialNum: Int? = null
): DataUnit()
        time: Date?,
        serialNum: Int?
): DataUnit(time, serialNum)
src/main/kotlin/com/flightfeather/obd/socket/bean/CarRegisterData.kt
@@ -1,5 +1,6 @@
package com.flightfeather.obd.socket.bean
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
import java.util.*
/**
@@ -7,14 +8,15 @@
 * Date: 2019/9/12
 *
 * è½¦è¾†ç™»å…¥æ•°æ®æ ¼å¼
 * æ•°æ®ç±»åž‹ @see [ObdCommandUnit.CarRegister]
 *
 * èµ·å§‹å­—节        å†…容                               æè¿°
 * 0                    æ•°æ®é‡‡é›†æ—¶é—´                 æ—¶é—´å®šä¹‰è§ 6.4.4
 * 0                    æ•°æ®é‡‡é›†æ—¶é—´                 æ—¶é—´å‡åº”采用 GMT+8 æ—¶é—´ï¼Œæ—¶é—´å®šä¹‰ç¬¦åˆ GB/T32960.3-2016 ç¬¬ 6.4 æ¡çš„要求
 * 6                    ç™»å…¥æµæ°´å·                   è½¦è½½ç»ˆç«¯æ¯ç™»å…¥ä¸€æ¬¡ï¼Œç™»å…¥æµæ°´å·è‡ªåŠ¨åŠ  1,从 1开始循环累加,最大值为 65531,循环周期为天。
 * 10                  SIM å¡å·                       SIM å¡ ICCID å·ï¼ˆICCID åº”为终端从 SIM å¡èŽ·å–çš„å€¼ï¼Œä¸åº”äººä¸ºå¡«å†™æˆ–ä¿®æ”¹ï¼‰ã€‚
 */
class CarRegisterData(
        var time: Date? = null,
        var serialNum: Int? = null,
        time: Date?,
        serialNum: Int?,
        var SIMCode: String? = null
): DataUnit()
): DataUnit(time, serialNum)
src/main/kotlin/com/flightfeather/obd/socket/bean/DataUnit.kt
@@ -1,8 +1,16 @@
package com.flightfeather.obd.socket.bean
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
import java.util.*
/**
 * @author riku
 * Date: 2019/9/12
 *
 * æ•°æ®å•å…ƒ
 * æ ¹æ®å‘½ä»¤å•å…ƒ @see [ObdCommandUnit] çš„分类,不同类型的结构不同,见各子类
 */
open class DataUnit {
}
open class DataUnit(
        var time: Date?,
        var serialNum: Int?
)
src/main/kotlin/com/flightfeather/obd/socket/bean/EngineDataFlow.kt
@@ -1,11 +1,13 @@
package com.flightfeather.obd.socket.bean
import com.flightfeather.obd.socket.eunm.ObdDataType
import java.util.*
/**
 * @author riku
 * Date: 2019/9/15
 * å‘动机数据流
 * å®žæ—¶ä¿¡æ¯[RealTimeData] ä¸­çš„发动机数据流
 * æ•°æ®ç±»åž‹ @see [ObdDataType.EngineDataFlow]
 */
class EngineDataFlow(
        time: Date?,
src/main/kotlin/com/flightfeather/obd/socket/bean/ObdData.kt
@@ -1,12 +1,14 @@
package com.flightfeather.obd.socket.bean
import com.flightfeather.obd.socket.bean.*
import com.flightfeather.obd.socket.eunm.ObdDataType
import java.util.*
/**
 * @author riku
 * Date: 2019/9/15
 * å®žæ—¶ä¿¡æ¯[RealTimeData] ä¸­çš„obd数据
 * æ•°æ®ç±»åž‹ @see [ObdDataType.ObdData]
 */
class ObdData(
        time: Date?,
src/main/kotlin/com/flightfeather/obd/socket/bean/ObdPackageData.kt
@@ -1,16 +1,40 @@
package com.flightfeather.obd.socket.bean
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
/**
 * @author riku
 * Date: 2019/9/12
 * æ•°æ®åŒ…结构
 *
 * èµ·å§‹å­—节                     å®šä¹‰                  æ•°æ®ç±»åž‹                        æè¿°åŠè¦æ±‚
 * 0                               èµ·å§‹ç¬¦                STRING                       å›ºå®šä¸º ASCII å­—符’##’,用“0x23,0x23”表示
 * 2                               å‘½ä»¤å•å…ƒ             BYTE                            å‘½ä»¤å•元定义 @see [ObdCommandUnit]
 * 3                            è½¦è¾†è¯†åˆ«å·           STRING                        è½¦è¾†è¯†åˆ«ç æ˜¯è¯†åˆ«çš„唯一标识,由 17 ä½å­—码组成,字码应符合 GB16735 ä¸­ 4.5 çš„规定
 * 20                         ç»ˆç«¯è½¯ä»¶ç‰ˆæœ¬å·       BYTE                           ç»ˆç«¯è½¯ä»¶ç‰ˆæœ¬å·æœ‰æ•ˆå€¼èŒƒå›´ 0~255
 * 21                           æ•°æ®åŠ å¯†æ–¹å¼         BYTE                           0x01:数据不加密;
 *                                                                                                  0x02:数据经过 RSA ç®—法加密;
 *                                                                                                  0x03:数据经过国密 SM2 ç®—法加密;
 *                                                                                                  â€œ0xFE”标识异常,“0xFF”表示无效,其他预留
 * 22                           æ•°æ®å•元长度          WORD                        æ•°æ®å•元长度是数据单元的总字节数,有效范围:0~65531
 * 24                           æ•°æ®å•å…ƒ                                                    è§æ•°æ®å•元格式和定义 @see [DataUnit]
 * å€’数第 1                   æ ¡éªŒç  BYTE                                             é‡‡ç”¨ BCC(异或校验)法,校验范围聪明星单元的第一个字节开始,同后一个字节异或,直到校验码前一字节为止,校验码占用一个字节
 */
data class ObdPackageData constructor(
        //起始符
        var head: String? = null,
        //命令单元
        var commandUnit: Int? = null,
        var vinCode: String? = null,
        //车辆识别号(指obd数据采集设备的编号)
        var deviceCode: String? = null,
        //终端软件版本号
        var softwareVersion: Int? = null,
        //数据加密方式
        var encryptionWay: Int? = null,
        //数据单元长度
        var dataLength: Int? = null,
        //数据单元
        var dataUnit: List<DataUnit>,
        //校验码
        var checkCode: Int? = null
)
src/main/kotlin/com/flightfeather/obd/socket/bean/RealTimeData.kt
@@ -1,13 +1,26 @@
package com.flightfeather.obd.socket.bean
import java.util.*
import com.flightfeather.obd.socket.eunm.ObdDataType
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
/**
 * @author riku
 * Date: 2019/9/12
 *
 * å®žæ—¶ä¿¡æ¯
 * æ•°æ®ç±»åž‹ @see[ObdCommandUnit.RealTimeData]
 *
 * æ•°æ®è¡¨ç¤ºå†…容                          é•¿åº¦ï¼ˆå­—节)             æ•°æ®ç±»åž‹                    æè¿°åŠè¦æ±‚
 * æ•°æ®é‡‡é›†æ—¶é—´                               6                           BYTE[6]                    æ—¶é—´å‡åº”采用 GMT+8 æ—¶é—´ï¼Œæ—¶é—´å®šä¹‰ç¬¦åˆ GB/T32960.3-2016 ç¬¬ 6.4 æ¡çš„要求
 * ä¿¡æ¯æµæ°´å·                                   2                           WORD                       ä»¥å¤©ä¸ºå•位,每包实时信息流水号唯一,从 1 å¼€å§‹ç´¯åŠ 
 * ä¿¡æ¯ç±»åž‹æ ‡å¿—(n)                       1                           BYTE                         ä¿¡æ¯ç±»åž‹æ ‡å¿— @see [ObdDataType]
 * ä¿¡æ¯ä½“(n)                                                                                                æ ¹æ®ä¿¡æ¯ç±»åž‹ä¸åŒï¼Œé•¿åº¦å’Œæ•°æ®ç±»åž‹ä¸åŒ
 * â€¦â€¦                                                                                                               â€¦â€¦
 * ä¿¡æ¯ç±»åž‹æ ‡å¿—(m)                      1                            BYTE[6]                   ä¿¡æ¯ç±»åž‹æ ‡å¿—
 * ä¿¡æ¯ä½“(m)                                                                                               æ ¹æ®ä¿¡æ¯ç±»åž‹ä¸åŒï¼Œé•¿åº¦å’Œæ•°æ®ç±»åž‹ä¸åŒ
 */
open class RealTimeData(
        var time: Date? = null,
        var serialNum: Int? = null
) : ReplacementData()
        time: Date?,
        serialNum: Int?
) : ReplacementData(time, serialNum)
src/main/kotlin/com/flightfeather/obd/socket/bean/ReplacementData.kt
@@ -1,9 +1,15 @@
package com.flightfeather.obd.socket.bean
import java.util.*
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
/**
 * @author riku
 * Date: 2019/9/12
 * è¡¥å‘信息与实时信息内容一致
 * è¡¥å‘信息与实时信息内容一致 @see [RealTimeData]
 * æ•°æ®ç±»åž‹ @see [ObdCommandUnit.ReplacementData]
 */
open class ReplacementData : DataUnit() {
}
open class ReplacementData (
        time: Date?,
        serialNum: Int?
): DataUnit(time, serialNum)
src/main/kotlin/com/flightfeather/obd/socket/bean/SupplementDataFlow.kt
@@ -1,11 +1,13 @@
package com.flightfeather.obd.socket.bean
import java.util.*
import com.flightfeather.obd.socket.eunm.ObdDataType
/**
 * @author riku
 * Date: 2019/9/15
 * è¡¥å……数据流
 * æ•°æ®ç±»åž‹ @see [ObdDataType.SupplementDataFlow]
 *
 * èµ·å§‹å­—节      æ•°æ®é¡¹                            æ•°æ®ç±»åž‹        å•位          æè¿°åŠè¦æ±‚
 * 0                 å‘动机扭矩模式                BYTE                            0:超速失效
src/main/kotlin/com/flightfeather/obd/socket/bean/TimeCalibrationData.kt
@@ -1,8 +1,16 @@
package com.flightfeather.obd.socket.bean
import java.util.*
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
/**
 * @author riku
 * Date: 2019/9/12
 *
 * è½¦è½½ç»ˆç«¯æ ¡æ—¶çš„æ•°æ®å•元为空。
 * æ•°æ®ç±»åž‹ @see [ObdCommandUnit.TimeCalibration]
 *
 */
class TimeCalibrationData : DataUnit() {
}
class TimeCalibrationData(
        time: Date? = null,
        serialNum: Int? = null
) : DataUnit(time, serialNum)
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/DataPackageDecoderImpl.kt
@@ -13,7 +13,7 @@
    private val dataUnitDecoder: DataUnitDecoder = DataUnitDecoderImpl()
    //fixme 2019.9.12: çŽ°åœ¨æœ‰ä¸¤ç§æƒ…å†µï¼Œ1:接收到的字符串是byte转码前的,那么其就表示字符的ASCII码;2:另一种就是接收到的是转码后的字符串。目前待定
    // æŽ¥æ”¶åˆ°çš„字符串是byte转码前的,其就表示字符的ASCII码;
    override fun getHead(b: List<String>): String? {
        return if (b.size >= 2) {
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/DataUnitDecoderImpl.kt
@@ -35,10 +35,10 @@
        val time = decodeDataTime(b)
        //国标法: èµ·å§‹å­—节为7 å¼€å§‹çš„2个字节表示 æµæ°´å·
//        val serialNum = "${b[7]}${b[8]}".toInt(16)
        val serialNum = "${b[7]}${b[8]}".toInt(16)
        //上海法: èµ·å§‹å­—节为6 å¼€å§‹çš„2个字节表示 æµæ°´å·
        val serialNum = "${b[6]}${b[7]}".toInt(16)
//        val serialNum = "${b[6]}${b[7]}".toInt(16)
        val dataList = mutableListOf<String>().apply { addAll(b) }
        //删去前6位时间
@@ -51,15 +51,15 @@
        * åˆ åŽ»æµæ°´å·ï¼Œå¾—åˆ°ä¿¡æ¯æ ‡å¿—ä¸Žä¿¡æ¯ä½“çš„ç»„åˆ
        * (国标法:去除前6位时间后,起始字节为1 å¼€å§‹çš„2个字节表示 æµæ°´å·)
        */
//        if (dataList.size >= 2) dataList.removeAt(1)
//        if (dataList.size >= 2) dataList.removeAt(1)
        if (dataList.size >= 2) dataList.removeAt(1)
        if (dataList.size >= 2) dataList.removeAt(1)
        /*
        * åˆ åŽ»æµæ°´å·ï¼Œå¾—åˆ°ä¿¡æ¯æ ‡å¿—ä¸Žä¿¡æ¯ä½“çš„ç»„åˆ
        * (上海法:去除前6位时间后,起始字节为0 å¼€å§‹çš„2个字节表示 æµæ°´å·)
        */
        if (dataList.isNotEmpty()) dataList.removeAt(0)
        if (dataList.isNotEmpty()) dataList.removeAt(0)
//        if (dataList.isNotEmpty()) dataList.removeAt(0)
//        if (dataList.isNotEmpty()) dataList.removeAt(0)
        val resultList = mutableListOf<RealTimeData>()
@@ -84,6 +84,9 @@
                for (i in 0 until data.size) {
                    if (dataList.isNotEmpty()) dataList.removeAt(0)
                }
            }else if (dataList.isNotEmpty()) {
                //按照规则没有取到信息体,并且原数据不为空,表示原数据格式错误,退出循环
//                break
            }
        }
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/RealTimeDataDecoderImpl.kt
@@ -123,13 +123,15 @@
    override fun getDataListByDataType(list: List<String>): MutableList<String> {
        if (list.isEmpty()) return mutableListOf()
        //fixme 2019.9.16 ä¸ç®¡ç¬¬ä¸€ä½çš„信息类型标志是否正确,至少返回一个值,此方法在信息体结构没有严格按照标准,
        //fixme  å³åœ¨åŽä¸€ä¸ªä¿¡æ¯ç±»åž‹æ ‡å¿—和前一个信息体之间有无效字符时,能够将其忽略,但不确定这种处理方式是否正确和必要
        val resultList = mutableListOf<String>().apply {
            //添加 ä¿¡æ¯ç±»åž‹æ ‡å¿—
            add(list[0])
        }
        when (list[0].toIntOrNull(16)) {
            ObdDataType.ObdData.value -> {
                //从起始字节 1 å¼€å§‹ï¼Œå›ºå®šæœ‰97个字节的数据
                for (i in 1..96) {
                    resultList.add(list[i])
@@ -144,12 +146,14 @@
                }
            }
            ObdDataType.EngineDataFlow.value -> {
                //从起始字节 1 å¼€å§‹ï¼Œå›ºå®šæœ‰37个字节的数据
                for (i in 1..37) {
                    resultList.add(list[i])
                }
            }
            ObdDataType.SupplementDataFlow.value -> {
                //从起始字节 1 å¼€å§‹ï¼Œå›ºå®šæœ‰17个字节的数据
                for (i in 1..17) {
                    resultList.add(list[i])
src/main/kotlin/com/flightfeather/obd/socket/eunm/ObdCommandUnit.kt
@@ -1,9 +1,20 @@
package com.flightfeather.obd.socket.eunm
import com.flightfeather.obd.socket.bean.*
/**
 * obd å‘½ä»¤å•å…ƒ
 * @author riku
 * Date: 2019/9/12
 *
 * obd å‘½ä»¤å•å…ƒ
 *
 * ç¼–码                   å®šä¹‰                     æ–¹å‘
 * 0x01             è½¦è¾†ç™»å…¥                    ä¸Šè¡Œ @see [CarRegisterData]
 * 0x02            å®žæ—¶ä¿¡æ¯ä¸ŠæŠ¥             ä¸Šè¡Œ @see [com.flightfeather.obd.socket.bean.RealTimeData]
 * 0x03            è¡¥å‘信息上报             ä¸Šè¡Œ @see [com.flightfeather.obd.socket.bean.ReplacementData]
 * 0x04            è½¦è¾†ç™»å‡º                    ä¸Šè¡Œ @see [CarLogOutData]
 * 0x05            ç»ˆç«¯æ ¡æ—¶                    ä¸Šè¡Œ @see [TimeCalibrationData]
 * 0x06~0x7F ä¸Šè¡Œæ•°æ®ç³»ç»Ÿé¢„ç•™      ä¸Šè¡Œ
 */
enum class ObdCommandUnit constructor(val value: Int) {
    CarRegister(1),
src/main/kotlin/com/flightfeather/obd/socket/eunm/ObdDataType.kt
@@ -4,12 +4,13 @@
/**
 * @author riku
 * Date: 2019/9/15
 *
 * å®žæ—¶ä¿¡æ¯[RealTimeData]和补发信息[ReplacementData]中的数据类型
 * ç±»åž‹ç¼–码                     è¯´æ˜Ž
 * 0x01                           OBD ä¿¡æ¯
 * 0x02                          æ•°æ®æµä¿¡æ¯
 * 0x01                           OBD ä¿¡æ¯    @see [com.flightfeather.obd.socket.bean.ObdData]
 * 0x02                          æ•°æ®æµä¿¡æ¯  @see[com.flightfeather.obd.socket.bean.EngineDataFlow]
 * 0x03-0x7F                é¢„ç•™
 * 0x80                          è¡¥å……数据流
 * 0x80                          è¡¥å……数据流   @see[com.flightfeather.obd.socket.bean.SupplementDataFlow]
 * 0x81~0xFE                ç”¨æˆ·è‡ªå®šä¹‰
 */
enum class ObdDataType constructor(val value: Int){
src/main/resources/log4j2.xml
@@ -56,7 +56,7 @@
    </appenders>
    <!--root é»˜è®¤åŠ è½½-->
    <loggers>
        <root level="DEBUG">
        <root level="INFO">
            <appender-ref ref="Console"/>
            <!--<appender-ref ref="allLog"/>-->
            <appender-ref ref="debugLog"/>
src/test/kotlin/com/flightfeather/obd/Test.kt
@@ -19,9 +19,12 @@
    @Test
    fun foo2() {
        val b = ByteArray(1){97}
        val s = "a"
        println()
        val b = ByteArray(2)
        b[0] = 0x01
        b[1] = 0x80.toByte()
        println("${b[0].toString(16)}${b[1].toInt()}")
        println("${b[0]}${b[1]}".toInt(16))
    }
    @Test
@@ -85,4 +88,15 @@
        }
        println(b.toString(16))
    }
    @Test
    fun foo10() {
        val s = "2 31 37 36 39 31 35 33 31 39 30 39 31 32 30 30 30 36 1 1 0 42 13 9 f 12 33 3b 2 0 8a 1b 0 36 2e 0 23 60 11 b4 0 c8 0 b4 0 0 66 0 0 0 0 0 0 73 0 0 0 b9 4 75 0 2e d8 ed 0 0 0 0 80 0 bc 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"
        val ascii = s.split(" ")
        var bcc = 0x00
        ascii.forEach {
            bcc = bcc.xor(it.toInt(16))
        }
        println(bcc.toString(16))
    }
}
src/test/kotlin/com/flightfeather/obd/socket/MessageManagerTest.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.flightfeather.obd.socket
import org.junit.Test
import org.junit.Assert.*
/**
 * @author riku
 * Date: 2019/9/16
 */
class MessageManagerTest {
    private val messageManager = MessageManager()
    @Test
    fun bccCheck() {
        val s = "23 23 2 31 37 36 39 31 35 33 31 39 30 39 31 32 30 30 30 36 1 1 0 42 13 9 f 12 34 8 2 0 8b 1c 0 d8 0 0 12 d8 9 6c 0 c8 0 b4 0 0 66 0 0 0 0 0 0 73 0 0 0 b9 4 32 0 2e d8 c4 0 0 0 0 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 f6"
        println(messageManager.bccCheck(s))
    }
}