riku
2019-09-15 4d44ed185203088052b10a8d1e3526fcbbc88331
obd 数据解码逻辑完成
已修改9个文件
已添加23个文件
1211 ■■■■■ 文件已修改
pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/common/packgeinfo 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/common/utils/FileUtil.kt 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/lightshare/bean/ObdDataVo.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/repository/ObdDataRepository.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/repository/impl/ObdDataDaoImpl.kt 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/MessageManager.kt 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/ServerHandler.kt 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/SocketServerClient.kt 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/CarLogOutData.kt 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/CarRegisterData.kt 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/DataUnit.kt 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/EngineDataFlow.kt 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/ObdData.kt 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/ObdPackageData.kt 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/RealTimeData.kt 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/ReplacementData.kt 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/SupplementDataFlow.kt 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/bean/TimeCalibrationData.kt 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/DataPackageDecoder.kt 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/DataUnitDecoder.kt 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/RealTimeDataDecoder.kt 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/VehicleDataDecoder.kt 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/DataPackageDecoderImpl.kt 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/DataUnitDecoderImpl.kt 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/RealTimeDataDecoderImpl.kt 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/eunm/ObdCommandUnit.kt 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/obd/socket/eunm/ObdDataType.kt 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/log4j2.xml 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/obd/ObdApplicationTests.kt 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/obd/Test.kt 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -27,6 +27,13 @@
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!-- åŽ»é™¤æ—§log依赖 -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
@@ -103,6 +110,12 @@
            <version>1.2.12</version>
        </dependency>
        <!--增加log4j2依赖↓-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
    </dependencies>
    <build>
src/main/kotlin/com/flightfeather/obd/common/packgeinfo
src/main/kotlin/com/flightfeather/obd/common/utils/FileUtil.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
package com.flightfeather.obd.common.utils
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.*
/**
 * @author riku
 * Date: 2019/9/11
 */
class FileUtil {
    private var file: File
    private var basePath:String = "${File.separator}ObdData${File.separator}"
    private var closeThread: Thread? = null
    private var fw: FileWriter? = null
    private var bw: BufferedWriter? = null
    init {
        val fileName = "data-${SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())}.txt"
        val path = "$basePath$fileName"
        file = File(path)
    }
    companion object{
        var instance: FileUtil? = null
        get() {
            if (field == null) {
                field = FileUtil()
            }
            return field
        }
    }
    fun saveObdData(str: String) {
        //检查文档是否存在
        if (!file.exists()) {
            file.parentFile.mkdirs()
            println("----创建文件夹:${file.parentFile.absolutePath}")
            file.createNewFile()
            println("----创建文件:${file.absolutePath}")
        }
        //文件最大512Kb,超过后新建文档
        if (file.length() + str.toByteArray().size > 512 * 1024) {
            val fileName = "data-${SimpleDateFormat("yyyy-MM-dd-hh-mm-ss").format(Date())}.txt"
            val path = "$basePath$fileName"
            file = File(path)
            file.createNewFile()
            //关闭原有输出流
            bw?.run {
                flush()
                close()
            }
            fw?.run {
                flush()
                close()
            }
            //新建输出流
            fw = FileWriter(file, true)
            bw = BufferedWriter(fw)
        }
        //第一次写文档时初始化输出流
        if (bw == null || fw == null) {
            fw = FileWriter(file, true)
            bw = BufferedWriter(fw)
        }
        bw?.run {
            write(str)
            newLine()
            flush()
        }
        readyToShutDownStream(bw, fw)
        println("----写入完成")
    }
    private fun readyToShutDownStream(bw: BufferedWriter?, fw:FileWriter?) {
        //终端上一次的计时线程
        closeThread?.interrupt()
        closeThread = Thread(Runnable {
            //2分钟没有新的数据写入就关闭输出流
            try {
                Thread.sleep(20 * 1000)
            } catch (e: Throwable) {
            }
            bw?.run {
                close()
            }
            this.bw = null
            fw?.run {
                close()
            }
            this.fw = null
        }).also {
            it.start()
        }
    }
}
src/main/kotlin/com/flightfeather/obd/lightshare/bean/ObdDataVo.kt
@@ -22,7 +22,7 @@
    var obdEngineTorque: Double? = null
    var obdFrictionTorque: Double? = null
    var obdEngineRpm: Int? = null
    var obdStartFuelFlow: Double? = null
    var obdEngineFuelFlow: Double? = null
    var obdScrUpstreamNo: Double? = null
    var obdScrDownstreamNo: Double? = null
    var obdRemainReactant: Double? = null
src/main/kotlin/com/flightfeather/obd/repository/ObdDataRepository.kt
@@ -3,6 +3,7 @@
import com.flightfeather.obd.domain.entity.ObdData
import com.flightfeather.obd.domain.mapper.ObdDataMapper
import com.flightfeather.obd.lightshare.bean.ObdDataVo
import com.flightfeather.obd.socket.bean.ObdPackageData
import org.springframework.beans.BeanUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@@ -23,4 +24,7 @@
     * é€šè¿‡æ±½è½¦vin码获取最新数据
     */
    fun getDataByVinCode(vinCode: String, pageNum: Int?, pageSize: Int?): MutableList<ObdDataVo>
    fun saveObdData(packageData: ObdPackageData):Boolean
}
src/main/kotlin/com/flightfeather/obd/repository/impl/ObdDataDaoImpl.kt
@@ -4,6 +4,10 @@
import com.flightfeather.obd.domain.mapper.ObdDataMapper
import com.flightfeather.obd.lightshare.bean.ObdDataVo
import com.flightfeather.obd.repository.ObdDataRepository
import com.flightfeather.obd.socket.bean.EngineDataFlow
import com.flightfeather.obd.socket.bean.ObdPackageData
import com.flightfeather.obd.socket.bean.SupplementDataFlow
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
import com.github.pagehelper.PageHelper
import org.springframework.beans.BeanUtils
import org.springframework.stereotype.Repository
@@ -43,4 +47,77 @@
        return resultList
    }
    override fun saveObdData(packageData: ObdPackageData): Boolean {
        val obdData = ObdData().apply {
            obdVin = packageData.vinCode
        }
        when (packageData.commandUnit) {
            ObdCommandUnit.CarRegister.value -> {
            }
            ObdCommandUnit.RealTimeData.value,
            ObdCommandUnit.ReplacementData.value -> {
                packageData.dataUnit.forEach {
                    when (it) {
                        is com.flightfeather.obd.socket.bean.ObdData -> {
                            obdData.apply {
                                obdTime = it.time
                                obdProtocol = it.obdProtocol
                                obdMil = it.obdMil
                                obdIdCode = it.obdSoftwareCode
                                obdVerificationCode = it.obdCvn
                                obdFaultCodeNum = it.obdFaultCodeNum
                                obdFaultCode = it.obdFaultCode
                            }
                        }
                        is EngineDataFlow -> {
                            obdData.apply {
                                obdLng = it.obdLong
                                obdLat = it.obdLat
                                obdSpeed = it.obdSpeed?.toInt()
                                obdAirPressure = it.obdAirPressure
                                obdEngineTorque = it.obdEngineTorque
                                obdFrictionTorque = it.obdFrictionTorque
                                obdEngineRpm = it.obdEngineRpm?.toInt()
                                obdStartFuelFlow = it.obdEngineFuelFlow
                                obdScrUpstreamNo = it.obdScrUpstreamNo
                                obdScrDownstreamNo = it.obdScrDownstreamNo
                                obdRemainReactant = it.obdRemainReactant
                                obdAirInput = it.obdAirInput
                                obdScrInputTemp = it.obdScrInputTemp
                                obdScrOutputTemp = it.obdScrOutputTemp
                                obdDpf = it.obdDpf
                                obdEngineCoolantTemp = it.obdEngineCoolantTemp
                                obdFuelLevel = it.obdFuelLevel
                                obdLocationStatus = it.obdLocationStatus
                                obdTotalMileage = it.obdTotalMileage
                            }
                        }
                        is SupplementDataFlow -> {
                            obdData.apply {
                                obdEngineTorqueMode = it.obdEngineTorqueMode?.toString()
                                obdAcceleratorPedal = it.obdAcceleratorPedal
                                obdTotalOilConsumption = it.obdTotalOilConsumption
                                obdUreaBoxTemp = it.obdUreaBoxTemp
                                obdUreaVolume = it.obdUreaVolume?.toInt()
                                obdTotalUreaConsume = it.obdTotalUreaConsume
                                obdDpfTemp = it.obdDpfTemp
//                                obdFirmwareVersion =
                            }
                        }
                    }
                }
            }
            ObdCommandUnit.CarLogOut.value -> {
            }
            ObdCommandUnit.TimeCalibration.value -> {
            }
        }
        return obdDataMapper.insert(obdData) == 1
    }
}
src/main/kotlin/com/flightfeather/obd/socket/MessageManager.kt
@@ -1,12 +1,15 @@
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.decoder.VehicleDataDecoder
import com.google.gson.Gson
import io.netty.channel.ChannelHandlerContext
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.util.FileSystemUtils
import javax.annotation.PostConstruct
import javax.annotation.Resource
@@ -26,24 +29,38 @@
    @Autowired
    lateinit var obdDataRepository: ObdDataRepository
    val vehicleDataDecoder = VehicleDataDecoder()
    @PostConstruct
    fun init() {
        instance = this
        instance.obdDataRepository = this.obdDataRepository
    }
    fun dealMsg(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)
    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)
        val packageData = vehicleDataDecoder.decode(msg)
                }
    fun dealByteArrayMsg(msg: ByteArray, ctx: ChannelHandlerContext?) {
        val b = ByteArray(20) {19}
        println(b)
            }
        } catch (e: Throwable) {
//            println("------收到格式错误的数据:$msg")
        }
    fun saveToTxt(msg: String) {
        val data = "data=> $msg"
        FileUtil.instance?.saveObdData(data)
    }
}
src/main/kotlin/com/flightfeather/obd/socket/ServerHandler.kt
@@ -3,6 +3,9 @@
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
import io.netty.util.AttributeKey
import java.lang.StringBuilder
import java.text.SimpleDateFormat
import java.util.*
class ServerHandler : ChannelInboundHandlerAdapter() {
@@ -11,6 +14,7 @@
    override fun channelRegistered(ctx: ChannelHandlerContext?) {
        super.channelRegistered(ctx)
//        ctx?.fireChannelActive()
    }
    override fun channelActive(ctx: ChannelHandlerContext?) {
@@ -19,16 +23,27 @@
    override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
        super.channelRead(ctx, msg)
        println("------收到的原始数据:[ip:${ctx?.channel()?.remoteAddress()}]\r\n$msg")
        if (msg is String) {
            MessageManager().dealMsg(msg, ctx)
        }
//        val attribute = ctx?.channel()?.attr(attributeKey)?.apply {
//            if (get() == null) {
//                set(data.obdVin)
//            }
//        }
        val sb = StringBuilder()
        if (msg is ByteArray) {
            println("------收到的原始数据:[ip:${ctx?.channel()?.remoteAddress()}] ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())}")
            msg.forEach {
                var a = 0
                a = if (it < 0) {
                    it + 256
                } else {
                    it.toInt()
                }
                print("${a.toString(16)} ")
                sb.append(a.toString(16)).append(" ")
            }
            sb.deleteCharAt(sb.length - 1)
        }
        val str = sb.toString()
        if (str.isNotEmpty()) {
            MessageManager().dealStringMsg(str, ctx)
        }
    }
src/main/kotlin/com/flightfeather/obd/socket/SocketServerClient.kt
@@ -4,10 +4,12 @@
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.bytes.ByteArrayDecoder
import io.netty.handler.codec.bytes.ByteArrayEncoder
import io.netty.handler.codec.string.StringDecoder
import io.netty.handler.codec.string.StringEncoder
/**
 * socket长连接服务端
@@ -34,15 +36,19 @@
            return ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel::class.java)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childHandler(object : ChannelInitializer<NioSocketChannel>() {
                        override fun initChannel(p0: NioSocketChannel?) {
                            p0?.pipeline()
                                    ?.addLast(StringDecoder())
//                                    ?.addLast("decoder", StringDecoder())
//                                    ?.addLast("encoder", StringEncoder())
                                    ?.addLast(ByteArrayDecoder())
                                    ?.addLast(ByteArrayEncoder())
                                    ?.addLast(ServerHandler())
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
        } catch (e: Throwable) {
            e.printStackTrace()
        }
src/main/kotlin/com/flightfeather/obd/socket/bean/CarLogOutData.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.flightfeather.obd.socket.bean
import java.util.*
/**
 * @author riku
 * Date: 2019/9/12
 * è½¦è¾†ç™»å‡ºä¿¡æ¯
 *
 */
class CarLogOutData(
        var time: Date? = null,
        var serialNum: Int? = null
): DataUnit()
src/main/kotlin/com/flightfeather/obd/socket/bean/CarRegisterData.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.flightfeather.obd.socket.bean
import java.util.*
/**
 * @author riku
 * Date: 2019/9/12
 *
 * è½¦è¾†ç™»å…¥æ•°æ®æ ¼å¼
 *
 * èµ·å§‹å­—节        å†…容                               æè¿°
 * 0                    æ•°æ®é‡‡é›†æ—¶é—´                 æ—¶é—´å®šä¹‰è§ 6.4.4
 * 6                    ç™»å…¥æµæ°´å·                   è½¦è½½ç»ˆç«¯æ¯ç™»å…¥ä¸€æ¬¡ï¼Œç™»å…¥æµæ°´å·è‡ªåŠ¨åŠ  1,从 1开始循环累加,最大值为 65531,循环周期为天。
 * 10                  SIM å¡å·                       SIM å¡ ICCID å·ï¼ˆICCID åº”为终端从 SIM å¡èŽ·å–çš„å€¼ï¼Œä¸åº”äººä¸ºå¡«å†™æˆ–ä¿®æ”¹ï¼‰ã€‚
 */
class CarRegisterData(
        var time: Date? = null,
        var serialNum: Int? = null,
        var SIMCode: String? = null
): DataUnit()
src/main/kotlin/com/flightfeather/obd/socket/bean/DataUnit.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
package com.flightfeather.obd.socket.bean
/**
 * @author riku
 * Date: 2019/9/12
 */
open class DataUnit {
}
src/main/kotlin/com/flightfeather/obd/socket/bean/EngineDataFlow.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.flightfeather.obd.socket.bean
import java.util.*
/**
 * @author riku
 * Date: 2019/9/15
 * å‘动机数据流
 */
class EngineDataFlow(
        time: Date?,
        serialNum: Int?
) : RealTimeData(time, serialNum) {
    var obdSpeed: Double? = null
    var obdAirPressure: Double? = null
    var obdEngineTorque: Double? = null
    var obdFrictionTorque: Double? = null
    var obdEngineRpm: Double? = null
    var obdEngineFuelFlow: Double? = null
    var obdScrUpstreamNo: Double? = null
    var obdScrDownstreamNo: Double? = null
    var obdRemainReactant: Double? = null
    var obdAirInput: Double? = null
    var obdScrInputTemp: Double? = null
    var obdScrOutputTemp: Double? = null
    var obdDpf: Double? = null
    var obdEngineCoolantTemp: Double? = null
    var obdFuelLevel: Double? = null
    var obdLocationStatus: Int? = null
    var obdLong: Double? = null
    var obdLat: Double? = null
    var obdTotalMileage: Double? = null
}
src/main/kotlin/com/flightfeather/obd/socket/bean/ObdData.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.flightfeather.obd.socket.bean
import com.flightfeather.obd.socket.bean.*
import java.util.*
/**
 * @author riku
 * Date: 2019/9/15
 * å®žæ—¶ä¿¡æ¯[RealTimeData] ä¸­çš„obd数据
 */
class ObdData(
        time: Date?,
        serialNum: Int?
) : RealTimeData(time, serialNum) {
    var obdProtocol: Int? = null
    var obdMil: Int? = null
    var diagnosisSupportStatus: String? = null//诊断支持状态
    var diagnosisReadyStatus: String? = null//诊断就绪状态
    var obdVin: String? = null
    var obdSoftwareCode: String? = null//软件标定识别号
    var obdCvn: String? = null//标定验证码
    var IUPR:String?=null//定义参考 SAE J 1979-DA è¡¨ G11
    var obdFaultCodeNum: Int? = null//故障码总数: æœ‰æ•ˆå€¼èŒƒå›´ï¼š0~253,“0xFE”表示无效。
    var obdFaultCode: String? = null//故障码信息列表: æ¯ä¸ªæ•…障码为四字节,可按故障实际顺序进行排序
}
src/main/kotlin/com/flightfeather/obd/socket/bean/ObdPackageData.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.flightfeather.obd.socket.bean
/**
 * @author riku
 * Date: 2019/9/12
 */
data class ObdPackageData constructor(
        var head: String? = null,
        var commandUnit: Int? = null,
        var vinCode: 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
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package com.flightfeather.obd.socket.bean
import java.util.*
/**
 * @author riku
 * Date: 2019/9/12
 *
 */
open class RealTimeData(
        var time: Date? = null,
        var serialNum: Int? = null
) : ReplacementData()
src/main/kotlin/com/flightfeather/obd/socket/bean/ReplacementData.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package com.flightfeather.obd.socket.bean
/**
 * @author riku
 * Date: 2019/9/12
 * è¡¥å‘信息与实时信息内容一致
 */
open class ReplacementData : DataUnit() {
}
src/main/kotlin/com/flightfeather/obd/socket/bean/SupplementDataFlow.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package com.flightfeather.obd.socket.bean
import java.util.*
/**
 * @author riku
 * Date: 2019/9/15
 * è¡¥å……数据流
 *
 * èµ·å§‹å­—节      æ•°æ®é¡¹                            æ•°æ®ç±»åž‹        å•位          æè¿°åŠè¦æ±‚
 * 0                 å‘动机扭矩模式                BYTE                            0:超速失效
 *                                                                                                  1:转速控制
 *                                                                                                  2:扭矩控制
 *                                                                                                  3:转速/扭矩控制
 * 1                  æ²¹é—¨è¸æ¿                          BYTE               %           æ•°æ®é•¿åº¦ï¼š1bytes
 *                                                                                                  ç²¾åº¦ï¼š0.4%/bit
 *                                                                                                  åç§»é‡ï¼š0
 *                                                                                                  æ•°æ®èŒƒå›´ï¼š0~100%
 *                                                                                                  â€œ0xFF”表示无效
 * 2                ç´¯è®¡æ²¹è€—(总油耗)          DWORD           L           æ•°æ®é•¿åº¦ï¼š4bytes
 *                                                                                                  ç²¾åº¦ï¼š0.5L per bit
 *                                                                                                  åç§»é‡ï¼š0
 *                                                                                                  æ•°æ®èŒƒå›´ï¼š0~2,105,540,607.5L
 *                                                                                                  â€œ0xFF,0xFF,0xFF,0xFF”表示无效
 * 6                  å°¿ç´ ç®±æ¸©åº¦                      BYTE               â„ƒ          æ•°æ®é•¿åº¦ï¼š1bytes
 *                                                                                                  ç²¾åº¦ï¼š1 â„ƒ/bit
 *                                                                                                  åç§»é‡ï¼š-40
 *                                                                                                  æ•°æ®èŒƒå›´ï¼š-40~210℃
 *                                                                                                  â€œ0xFF”表示无效
 * 7                   å®žé™…尿素喷射量              DWORD         ml/h       æ•°æ®é•¿åº¦ï¼š4bytes
 *                                                                                                  ç²¾åº¦ï¼š0.01 ml/h per bit
 *                                                                                                  åç§»é‡ï¼š0
 *                                                                                                  æ•°æ®èŒƒå›´ï¼š0
 *                                                                                                  â€œ0xFF,0xFF,0xFF,0xFF”表示无效
 * 11                   ç´¯è®¡å°¿ç´ æ¶ˆè€—(总尿素消耗)DWORD      g        æ•°æ®é•¿åº¦ï¼š4bytes
 *                                                                                                  ç²¾åº¦ï¼š1 g per bit
 *                                                                                                  åç§»é‡ï¼š0
 *                                                                                                  æ•°æ®èŒƒå›´ï¼š0
 *                                                                                                  â€œ0xFF,0xFF,0xFF,0xFF”表示无效
 * 15                   DPF æŽ’气温度                  WORD             â„ƒ        æ•°æ®é•¿åº¦ï¼š2bytes
 *                                                                                                   ç²¾åº¦ï¼š0.03125 â„ƒ per bit
 *                                                                                                   åç§»é‡ï¼š-273
 *                                                                                                   æ•°æ®èŒƒå›´ï¼š-273~1734.96875℃
 *                                                                                                   â€œ0xFF,0xFF”表示无效
 */
class SupplementDataFlow(
        time: Date?,
        serialNum: Int?
) : RealTimeData(time, serialNum) {
    var obdEngineTorqueMode: Int? = null
    var obdAcceleratorPedal: Double? = null
    var obdTotalOilConsumption: Double? = null
    var obdUreaBoxTemp: Double? = null
    var obdUreaVolume: Double? = null
    var obdTotalUreaConsume: Double? = null
    var obdDpfTemp: Double? = null
}
src/main/kotlin/com/flightfeather/obd/socket/bean/TimeCalibrationData.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
package com.flightfeather.obd.socket.bean
/**
 * @author riku
 * Date: 2019/9/12
 */
class TimeCalibrationData : DataUnit() {
}
src/main/kotlin/com/flightfeather/obd/socket/decoder/DataPackageDecoder.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.flightfeather.obd.socket.decoder
import com.flightfeather.obd.socket.bean.DataUnit
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
/**
 * obd ç¬¬ä¸€å±‚数据包解码器
 * @author riku
 * Date: 2019/9/12
 *
 * æ•°æ®ç¬¬ä¸€å±‚结构定义如下:
 * èµ·å§‹å­—节         å®šä¹‰
 * 0                    èµ·å§‹ç¬¦
 * 2                    å‘½ä»¤å•å…ƒ  ç±»åž‹@see [ObdCommandUnit]
 * 3                    è½¦è¾†è¯†åˆ«å·
 * 4                    ç»ˆç«¯è½¯ä»¶ç‰ˆæœ¬å·
 * 21                   æ•°æ®åŠ å¯†æ–¹å¼
 * 22                   æ•°æ®å•元长度
 * 24                   æ•°æ®å•å…ƒ  è§£ç å™¨@see [DataUnitDecoder]
 * å€’数第1           æ ¡éªŒç 
 */
interface DataPackageDecoder {
    fun getHead(b: List<String>): String?
    fun getCommandUnit(b: List<String>): Int?
    fun getVinCode(b: List<String>): String?
    fun getSoftwareVersion(b: List<String>): Int?
    fun getEncryptionWay(b: List<String>): Int?
    fun getDataLength(b: List<String>): Int
    fun getDataUnit(b: List<String>): List<DataUnit>
    fun getCheckCode(b: List<String>): Int?
    fun toStringList(msg: String): List<String>
}
src/main/kotlin/com/flightfeather/obd/socket/decoder/DataUnitDecoder.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.flightfeather.obd.socket.decoder
import com.flightfeather.obd.socket.bean.*
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
import java.util.*
/**
 * obd ç¬¬äºŒå±‚数据单元解码器
 * @author riku
 * Date: 2019/9/12
 *
 * æ•°æ®å•元按照命令单元的类型共有以下几种类型:
 * å‘½ä»¤å•å…ƒ @see [ObdCommandUnit]
 * ç¼–码(byte)        å®šä¹‰
 * 0x01                 è½¦è¾†ç™»å…¥
 * 0x02                 å®žæ—¶ä¿¡æ¯ä¸ŠæŠ¥
 * 0x03                 è¡¥å‘信息上报
 * 0x04                 è½¦è¾†ç™»å‡º
 * 0x05                 ç»ˆç«¯æ ¡æ—¶
 * 0x06~0x7f       ä¸Šè¡Œæ•°æ®ç³»ç»Ÿé¢„ç•™
 */
interface DataUnitDecoder {
    fun getCarRegisterData(b: List<String>): List<CarRegisterData>
    fun getRealTimeData(b: List<String>): List<RealTimeData>
    fun getReplacementData(b: List<String>): List<ReplacementData>
    fun getCarLogOutData(b: List<String>): List<CarLogOutData>
    fun getTimeCalibrationData(b: List<String>): List<TimeCalibrationData>
    fun decodeDataTime(b: List<String>): Date?
}
src/main/kotlin/com/flightfeather/obd/socket/decoder/RealTimeDataDecoder.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.flightfeather.obd.socket.decoder
import com.flightfeather.obd.socket.bean.*
import com.flightfeather.obd.socket.eunm.ObdDataType
import java.util.*
/**
 * @author riku
 * Date: 2019/9/15
 * æ•°æ®å•å…ƒ[DataUnit]中,类型为实时信息[RealTimeData]的解码器
 * å…¶åˆ†ç±» @see [ObdDataType]
 */
interface RealTimeDataDecoder {
    fun getObdData(time: Date?, serialNum: Int, b: List<String>): ObdData?
    fun getEngineDataFlow(time: Date?, serialNum: Int, b: List<String>): EngineDataFlow?
    fun getSupplementDataFlow(time: Date?, serialNum: Int, b: List<String>): SupplementDataFlow?
    /**
     * æ ¹æ®ä¼ å…¥çš„列表,默认第一位为 [ObdDataType],返回对应的数据列表
     */
    fun getDataListByDataType(list: List<String>): MutableList<String>
}
src/main/kotlin/com/flightfeather/obd/socket/decoder/VehicleDataDecoder.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.flightfeather.obd.socket.decoder
import com.flightfeather.obd.socket.bean.ObdPackageData
import com.flightfeather.obd.socket.decoder.impl.DataPackageDecoderImpl
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import javax.annotation.PostConstruct
/**
 * è½¦è¾† obd æ•°æ®è§£ç å™¨
 * @author riku
 * Date: 2019/9/12
 */
class VehicleDataDecoder {
    private val dataPackageDecoder: DataPackageDecoder = DataPackageDecoderImpl()
    fun decode(msg: String): ObdPackageData {
        val list = dataPackageDecoder.toStringList(msg)
        dataPackageDecoder.run {
            return ObdPackageData(
                    getHead(list),
                    getCommandUnit(list),
                    getVinCode(list),
                    getSoftwareVersion(list),
                    getEncryptionWay(list),
                    getDataLength(list),
                    getDataUnit(list),
                    getCheckCode(list)
            )
        }
    }
}
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/DataPackageDecoderImpl.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
package com.flightfeather.obd.socket.decoder.impl
import com.flightfeather.obd.socket.bean.DataUnit
import com.flightfeather.obd.socket.decoder.DataPackageDecoder
import com.flightfeather.obd.socket.decoder.DataUnitDecoder
import com.flightfeather.obd.socket.eunm.ObdCommandUnit
/**
 * @author riku
 * Date: 2019/9/12
 */
class DataPackageDecoderImpl : DataPackageDecoder {
    private val dataUnitDecoder: DataUnitDecoder = DataUnitDecoderImpl()
    //fixme 2019.9.12: çŽ°åœ¨æœ‰ä¸¤ç§æƒ…å†µï¼Œ1:接收到的字符串是byte转码前的,那么其就表示字符的ASCII码;2:另一种就是接收到的是转码后的字符串。目前待定
    override fun getHead(b: List<String>): String? {
        return if (b.size >= 2) {
            "${b[0]}${b[1]}"
        } else {
            null
        }
    }
    override fun getCommandUnit(b: List<String>): Int? = if (b.size >= 3) {
        b[2].toIntOrNull(16)
    } else {
        null
    }
    override fun getVinCode(b: List<String>): String? {
        if (b.size < 20) return null
        //fixme åº”该是需要将b当作ASCII码,再获取转换后的字符串
        val s = StringBuilder()
        for (i in 3..19) {
            s.append(b[i].toIntOrNull(16)?.toChar())
        }
        return s.toString()
    }
    override fun getSoftwareVersion(b: List<String>): Int? = if (b.size >= 21) {
        b[20].toIntOrNull(16)
    } else {
        null
    }
    override fun getEncryptionWay(b: List<String>): Int? = if (b.size >= 21) {
        b[21].toIntOrNull(16)
    } else {
        null
    }
    override fun getDataLength(b: List<String>): Int {
        if (b.size < 24) return 0
        val hexNum = "${b[22]}${b[23]}"
        return hexNum.toIntOrNull(16) ?: 0
    }
    override fun getDataUnit(b: List<String>): List<DataUnit> {
        if (getDataLength(b) == 0 || b.size < 26) {
            return emptyList()
        }
        val unit = mutableListOf<String>()
        for (i in 24..b.size - 2) {
            unit.add(b[i])
        }
        dataUnitDecoder.run {
            return when (getCommandUnit(b)) {
                ObdCommandUnit.CarRegister.value -> getCarRegisterData(unit)
                ObdCommandUnit.RealTimeData.value -> getRealTimeData(unit)
                ObdCommandUnit.ReplacementData.value -> getReplacementData(unit)
                ObdCommandUnit.CarLogOut.value -> getCarLogOutData(unit)
                ObdCommandUnit.TimeCalibration.value -> getTimeCalibrationData(unit)
                else -> emptyList()
            }
        }
    }
    override fun getCheckCode(b: List<String>): Int? {
        return if (b.isNotEmpty()) {
            b[b.size - 1].toIntOrNull(16)
        } else {
            null
        }
    }
    override fun toStringList(msg: String): List<String> {
        return msg.split(" ")
    }
}
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/DataUnitDecoderImpl.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package com.flightfeather.obd.socket.decoder.impl
import com.flightfeather.obd.socket.bean.*
import com.flightfeather.obd.socket.decoder.DataUnitDecoder
import com.flightfeather.obd.socket.decoder.RealTimeDataDecoder
import com.flightfeather.obd.socket.eunm.ObdDataType
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.lang.StringBuilder
import java.util.*
import javax.annotation.PostConstruct
/**
 * @author riku
 * Date: 2019/9/15
 */
class DataUnitDecoderImpl : DataUnitDecoder {
    private var realTimeDataDecoder: RealTimeDataDecoder = RealTimeDataDecoderImpl()
    override fun getCarRegisterData(b: List<String>): List<CarRegisterData> {
        val time = decodeDataTime(b)
        val serialNum = "${b[6]}${b[7]}".toInt(16)
        val simCode = StringBuilder()
        for (i in 10 until b.size) {
            simCode.append(b[i].toInt(16).toChar())
        }
        return mutableListOf(CarRegisterData(time, serialNum, simCode.toString()))
    }
    override fun getRealTimeData(b: List<String>): List<RealTimeData> {
        val time = decodeDataTime(b)
        //国标法: èµ·å§‹å­—节为7 å¼€å§‹çš„2个字节表示 æµæ°´å·
//        val serialNum = "${b[7]}${b[8]}".toInt(16)
        //上海法: èµ·å§‹å­—节为6 å¼€å§‹çš„2个字节表示 æµæ°´å·
        val serialNum = "${b[6]}${b[7]}".toInt(16)
        val dataList = mutableListOf<String>().apply { addAll(b) }
        //删去前6位时间
        for (i in 0..5) {
            if (dataList.isNotEmpty()) dataList.removeAt(0)
        }
        //fixme 2019.9.15 ç›®å‰ä¸¤ç§è§„则有冲突,并且只从数据内容无法区分。暂时使用 å›½æ ‡æ³•
        /*
        * åˆ åŽ»æµæ°´å·ï¼Œå¾—åˆ°ä¿¡æ¯æ ‡å¿—ä¸Žä¿¡æ¯ä½“çš„ç»„åˆ
        * (国标法:去除前6位时间后,起始字节为1 å¼€å§‹çš„2个字节表示 æµæ°´å·)
        */
//        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)
        val resultList = mutableListOf<RealTimeData>()
        /*
         * æœ€ç»ˆå¾—到的数据结构为 [ ä¿¡æ¯ç±»åž‹æ ‡å¿—, ä¿¡æ¯ä½“, ...... , ä¿¡æ¯ç±»åž‹æ ‡å¿—, ä¿¡æ¯ä½“ ]
         */
        while (dataList.isNotEmpty()) {
            //得到最前面的一组信息类型+信息体列表
            val data = realTimeDataDecoder.getDataListByDataType(dataList)
            if (data.isNotEmpty()) {
                val r = when (data[0].toInt(16)) {
                    ObdDataType.ObdData.value -> realTimeDataDecoder.getObdData(time, serialNum, data)
                    ObdDataType.EngineDataFlow.value -> realTimeDataDecoder.getEngineDataFlow(time, serialNum, data)
                    ObdDataType.SupplementDataFlow.value -> realTimeDataDecoder.getSupplementDataFlow(time, serialNum, data)
                    else -> null
                }
                if (r != null) {
                    resultList.add(r)
                }
                for (i in 0 until data.size) {
                    if (dataList.isNotEmpty()) dataList.removeAt(0)
                }
            }
        }
        return resultList
    }
    override fun getReplacementData(b: List<String>): List<ReplacementData> = getRealTimeData(b)
    override fun getCarLogOutData(b: List<String>): List<CarLogOutData> {
        val time = decodeDataTime(b)
        val serialNum = "${b[6]}${b[7]}".toInt(16)
        return mutableListOf(CarLogOutData(time, serialNum))
    }
    override fun getTimeCalibrationData(b: List<String>): List<TimeCalibrationData> = emptyList()
    override fun decodeDataTime(b: List<String>): Date? {
        if (b.size < 6) {
            return null
        }
        val year = b[0].toInt(16) + 2000
        if (year < 2000 || year > 2099) return null
        val month = b[1].toInt(16)
        val day = b[2].toInt(16)
        val hour = b[3].toInt(16)
        val min = b[4].toInt(16)
        val sec = b[5].toInt(16)
        val cal  = Calendar.getInstance().apply {
            set(year, month - 1, day, hour, min, sec)
        }
        return cal.time
    }
}
src/main/kotlin/com/flightfeather/obd/socket/decoder/impl/RealTimeDataDecoderImpl.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,163 @@
package com.flightfeather.obd.socket.decoder.impl
import com.flightfeather.obd.socket.bean.EngineDataFlow
import com.flightfeather.obd.socket.bean.ObdData
import com.flightfeather.obd.socket.bean.SupplementDataFlow
import com.flightfeather.obd.socket.decoder.RealTimeDataDecoder
import com.flightfeather.obd.socket.eunm.ObdDataType
import org.springframework.stereotype.Component
import java.util.*
/**
 * @author riku
 * Date: 2019/9/15
 */
class RealTimeDataDecoderImpl : RealTimeDataDecoder {
    override fun getObdData(time: Date?, serialNum: Int, b: List<String>): ObdData? {
        val dataList = mutableListOf<String>().apply { addAll(b) }
        if (b.isNotEmpty()) {
            //去除 ä¿¡æ¯ç±»åž‹æ ‡å¿—
            dataList.removeAt(0)
        }
        if (dataList.size >= 96) {
            val vin = StringBuilder()
            for (i in 6..22) {
                vin.append(dataList[i].toIntOrNull(16)?.toChar())
            }
            val softwareCode = StringBuilder()
            for (i in 23..40) {
                softwareCode.append(dataList[i].toIntOrNull(16)?.toChar())
            }
            val cvn = StringBuilder()
            for (i in 41..58) {
                cvn.append(dataList[i].toIntOrNull(16)?.toChar())
            }
            val IUPR = StringBuilder()
            for (i in 59..94) {
                //todo 2019.9.15 å› æš‚不清楚格式与含义,直接保存
                IUPR.append(dataList[i])
            }
            var faultCodeNum = dataList[95].toIntOrNull() ?: 0
            val faultCode = StringBuilder()
            if (faultCodeNum < 0 || faultCodeNum > 253) {
                faultCodeNum = 254//代表无效
            } else {
                for (i in 96..(95 + 4 * faultCodeNum)) {
                    faultCode.append(dataList[i])
                }
            }
            return ObdData(time, serialNum).apply {
                obdProtocol = dataList[0].toIntOrNull(16)
                obdMil = dataList[1].toIntOrNull(16)
                diagnosisSupportStatus = "${dataList[2]}${dataList[3]}".toIntOrNull(16)?.toString(2)
                diagnosisReadyStatus = "${dataList[4]}${dataList[5]}".toIntOrNull(16)?.toString(2)
                obdVin = vin.toString()
                obdSoftwareCode = softwareCode.toString()
                obdCvn = cvn.toString()
                this.IUPR = IUPR.toString()
                obdFaultCodeNum = faultCodeNum
                obdFaultCode = faultCode.toString()
            }
        } else {
            return null
        }
    }
    override fun getEngineDataFlow(time: Date?, serialNum: Int, b: List<String>): EngineDataFlow? {
        val dataList = mutableListOf<String>().apply { addAll(b) }
        if (b.isNotEmpty()) {
            //去除 ä¿¡æ¯ç±»åž‹æ ‡å¿—
            dataList.removeAt(0)
        }
        return if (dataList.size >= 37) {
             EngineDataFlow(time, serialNum).apply {
                obdSpeed = "${dataList[0]}${dataList[1]}".toIntOrNull(16)?.toDouble()?.times((1 / 256).toDouble())
                obdAirPressure = dataList[2].toIntOrNull(16)?.toDouble()?.times(0.5)
                obdEngineTorque = dataList[3].toIntOrNull(16)?.minus(125)?.toDouble()?.div(100)
                obdFrictionTorque = dataList[4].toIntOrNull(16)?.minus(125)?.toDouble()?.div(100)
                obdEngineRpm = "${dataList[5]}${dataList[6]}".toIntOrNull(16)?.toDouble()?.times(0.125)
                obdEngineFuelFlow = "${dataList[7]}${dataList[8]}".toIntOrNull(16)?.toDouble()?.times(0.05)
                obdScrUpstreamNo = "${dataList[9]}${dataList[10]}".toIntOrNull(16)?.minus(200)?.toDouble()?.times(0.05)
                obdScrDownstreamNo = "${dataList[11]}${dataList[12]}".toIntOrNull(16)?.minus(200)?.toDouble()?.times(0.05)
                obdRemainReactant = dataList[13].toIntOrNull(16)?.toDouble()?.times(0.4)?.div(100)
                obdAirInput = "${dataList[14]}${dataList[15]}".toIntOrNull(16)?.toDouble()?.times(0.05)
                obdScrInputTemp = "${dataList[16]}${dataList[17]}".toIntOrNull(16)?.minus(273)?.toDouble()?.times(0.03125)
                obdScrOutputTemp = "${dataList[18]}${dataList[19]}".toIntOrNull(16)?.minus(273)?.toDouble()?.times(0.03125)
                obdDpf = "${dataList[20]}${dataList[21]}".toIntOrNull(16)?.toDouble()?.times(0.1)
                obdEngineCoolantTemp = dataList[22].toIntOrNull(16)?.minus(40)?.toDouble()
                obdFuelLevel = dataList[23].toIntOrNull(16)?.toDouble()?.times(0.4)
                obdLocationStatus = dataList[24].toIntOrNull(16)
                obdLong = "${dataList[25]}${dataList[26]}${dataList[27]}${dataList[28]}".toIntOrNull(16)?.toDouble()?.times(0.000001)
                obdLat = "${dataList[29]}${dataList[30]}${dataList[31]}${dataList[32]}".toIntOrNull(16)?.toDouble()?.times(0.000001)
                obdTotalMileage = "${dataList[33]}${dataList[34]}${dataList[35]}${dataList[36]}".toIntOrNull(16)?.toDouble()?.times(0.1)
            }
        } else {
            null
        }
    }
    override fun getSupplementDataFlow(time: Date?, serialNum: Int, b: List<String>): SupplementDataFlow? {
        val dataList = mutableListOf<String>().apply { addAll(b) }
        if (b.isNotEmpty()) {
            //去除 ä¿¡æ¯ç±»åž‹æ ‡å¿—
            dataList.removeAt(0)
        }
        return if (dataList.size >= 17) {
            SupplementDataFlow(time, serialNum).apply {
                obdEngineTorqueMode = dataList[0].toIntOrNull(16)
                obdAcceleratorPedal = dataList[1].toIntOrNull(16)?.toDouble()?.times(0.4)?.div(100)
                obdTotalOilConsumption = "${dataList[2]}${dataList[3]}${dataList[4]}${dataList[5]}".toIntOrNull(16)?.toDouble()?.times(0.5)
                obdUreaBoxTemp  = dataList[6].toIntOrNull(16)?.toDouble()?.minus(40)
                obdUreaVolume  = "${dataList[7]}${dataList[8]}${dataList[9]}${dataList[10]}".toIntOrNull(16)?.toDouble()?.times(0.01)
                obdTotalUreaConsume  = "${dataList[11]}${dataList[12]}${dataList[13]}${dataList[14]}".toIntOrNull(16)?.toDouble()
                obdDpfTemp  = "${dataList[15]}${dataList[16]}".toIntOrNull(16)?.minus(273)?.toDouble()?.times(0.03125)
            }
        } else {
            null
        }
    }
    override fun getDataListByDataType(list: List<String>): MutableList<String> {
        if (list.isEmpty()) return mutableListOf()
        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])
                }
                val faultCodeNum = list[96].toIntOrNull() ?: 0//故障码总数
                //根据故障码总数,每个故障码4个字节,得到故障码总字节数
                if (faultCodeNum in 0..253) {
                    for (i in 97..(96 + faultCodeNum * 4)) {
                        resultList.add(list[i])
                    }
                }
            }
            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])
                }
            }
        }
        return resultList
    }
}
src/main/kotlin/com/flightfeather/obd/socket/eunm/ObdCommandUnit.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.flightfeather.obd.socket.eunm
/**
 * obd å‘½ä»¤å•å…ƒ
 * @author riku
 * Date: 2019/9/12
 */
enum class ObdCommandUnit constructor(val value: Int) {
    CarRegister(1),
    RealTimeData(2),
    ReplacementData(3),
    CarLogOut(4),
    TimeCalibration(5)
}
src/main/kotlin/com/flightfeather/obd/socket/eunm/ObdDataType.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.flightfeather.obd.socket.eunm
import com.flightfeather.obd.socket.bean.*
/**
 * @author riku
 * Date: 2019/9/15
 * å®žæ—¶ä¿¡æ¯[RealTimeData]和补发信息[ReplacementData]中的数据类型
 * ç±»åž‹ç¼–码                     è¯´æ˜Ž
 * 0x01                           OBD ä¿¡æ¯
 * 0x02                          æ•°æ®æµä¿¡æ¯
 * 0x03-0x7F                é¢„ç•™
 * 0x80                          è¡¥å……数据流
 * 0x81~0xFE                ç”¨æˆ·è‡ªå®šä¹‰
 */
enum class ObdDataType constructor(val value: Int){
    ObdData(1),
    EngineDataFlow(2),
    SupplementDataFlow(128)
}
src/main/resources/application.yml
@@ -17,6 +17,7 @@
  not-empty: false
  identity: MYSQL
#数据库分页配置
pagehelper:
    helperDialect: mysql
    reasonable: true
src/main/resources/log4j2.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
    6个优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
    å¦‚果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出
    è®¾ç½®ä¸ºOFF è¡¨ç¤ºä¸è®°å½•log4j2本身的日志,
 -->
<!-- status:用来指定log4j本身的打印日志级别,monitorInterval:指定log4j自动重新配置的监测间隔时间 -->
<configuration status="INFO" monitorInterval="30">
    <!-- è‡ªå·±è®¾ç½®å±žæ€§ï¼ŒåŽé¢é€šè¿‡${}来访问 -->
    <properties>
        <property name="LOG_HOME">../obdLogs</property>
    </properties>
    <appenders>
        <!--Appender 1. è¾“出到Console控制台,指定输出格式和过滤器等级为INFO -->
        <Console name="Console" target="SYSTEM_OUT">
            <!--ThresholdFilter指定日志消息的输出最低层次-->
            <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </Console>
        <!--Appender 2. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是日志文件大于3KB,只保存最新的10个日志-->
        <File name="allLog" fileName="${LOG_HOME}/all.log">
            <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>
        <!--Appender 3. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是日志文件大于3KB,只保存最新的10个日志-->
        <RollingFile name="debugLog" fileName="${LOG_HOME}/debug.log" filePattern="${LOG_HOME}/debug-%i.log">
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="[%-5level][%d{yyyy-MM-dd HH:mm:ss}][%F:%L] - %m%n"/>
            <SizeBasedTriggeringPolicy size="3KB"/>
            <!-- DefaultRolloverStrategy ä¸­çš„参数max,可以限制 SizeBasedTriggeringPolicy中size超出后,只保留max个存档-->
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
        <!--Appender 4. è¾“出到滚动保存的文件, è§¦å‘保存日志文件的条件是每分钟第一次的日志事件。ERROR日志是按分钟产生日志 -->
        <RollingFile name="errorLog" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/error-%d{yyyy-MM-dd_HH-mm}.log">
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="[%-5level][%d{yyyy-MM-dd HH:mm:ss}][%C:%F:%L] - %m%n"/>
            <TimeBasedTriggeringPolicy/>
        </RollingFile>
        <RollingFile name="RollingFile" fileName="${LOG_HOME}/rar.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <!--日志文件最大值 ç¬¬äºŒå¤©åŽ‹ç¼©-->
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
        </RollingFile>
    </appenders>
    <!--root é»˜è®¤åŠ è½½-->
    <loggers>
        <root level="DEBUG">
            <appender-ref ref="Console"/>
            <!--<appender-ref ref="allLog"/>-->
            <appender-ref ref="debugLog"/>
            <appender-ref ref="errorLog"/>
            <appender-ref ref="RollingFile"/>
        </root>
    </loggers>
</configuration>
src/test/kotlin/com/flightfeather/obd/ObdApplicationTests.kt
@@ -6,6 +6,7 @@
import com.google.gson.Gson
import org.junit.Test
import org.junit.runner.RunWith
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner
@@ -35,4 +36,14 @@
        println(map.cmdCode)
    }
    @Test
    fun logTest() {
        val log = LoggerFactory.getLogger(javaClass)
        log.trace("trace")
        log.debug("debug")
        log.warn("warn")
        log.info("info")
        log.error("error")
    }
}
src/test/kotlin/com/flightfeather/obd/Test.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
package com.flightfeather.obd
import org.junit.Test
import java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.*
/**
 * @author riku
 * Date: 2019/9/12
 */
class Test {
    @Test
    fun foo1() {
        val b = ByteArray(20) {8}
        println(b[18])
    }
    @Test
    fun foo2() {
        val b = ByteArray(1){97}
        val s = "a"
        println()
    }
    @Test
    fun foo3() {
        val hexNum = "ff"
        val length = hexNum.toInt(16)
        println(length)
    }
    @Test
    fun foo4() {
        val h = "0101"
        val byte = 0xff
        val b = h.toInt(16)
        println(b)
    }
    @Test
    fun foo5() {
        val s = "23 23 30 30 30 30 30 35 33 31 36 30 38 30 31 30 35 33 36"
        val sb = StringBuilder()
        val ascii = s.split(" ")
        ascii.forEach {
            sb.append(it.toIntOrNull(16)?.toChar())
        }
        println(sb.toString())
    }
    @Test
    fun foo6() {
        val cal  = Calendar.getInstance().apply {
            set(2019, 9, 15, 23, 16, 59)
        }
        println(SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(cal.time))
    }
    @Test
    fun foo7() {
        val a = "0AAF".toIntOrNull(16)?.toString(2)
        println(a)
    }
    @Test
    fun foo8() {
        val byte = ByteArray(2)
        byte[0] = 0x23
        byte[1] = 0x23
        byte[0].toString(16)
    }
    @Test
    fun foo9() {
        val b = 128.toByte()
        var a = 0
        if (b < 0) {
            a = b + 256
        } else {
            a = b.toInt()
        }
        println(b.toString(16))
    }
}