yy1717
2019-12-30 fc56e0a4ae100692b21b53ec71d56c831073f8e0
首次加入ndk
3个文件已修改
45个文件已添加
7618 ■■■■■ 已修改文件
.idea/codeStyles/Project.xml 134 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/build.gradle 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/CMakeLists.txt 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/Geometry.cpp 390 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/Geometry.h 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/common/apptimer.cpp 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/common/apptimer.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/common/net.cpp 482 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/common/net.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/common/serial_port.cpp 306 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/common/serial_port.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/defs.h 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/driver_test.cpp 616 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/driver_test.h 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/jni_log.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/mcu/mcu_if.cpp 306 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/mcu/mcu_if.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/native-lib.cpp 464 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/native-lib.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/rtk_module/parse_gps.cpp 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/rtk_module/parse_gps.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/rtk_module/rtk.cpp 421 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/rtk_module/rtk.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/rtk_platform/parse_net.cpp 994 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/rtk_platform/parse_net.h 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/rtk_platform/platform.cpp 361 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/rtk_platform/platform.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/driving_curve.cpp 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/driving_curve.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/error_list.cpp 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/error_list.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/park_bottom.cpp 380 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/park_bottom.h 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/park_edge.cpp 274 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/park_edge.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/stop_and_start.cpp 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/stop_and_start.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/turn_a90.cpp 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/test_items/turn_a90.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/utils/crc16.cpp 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/utils/crc16.h 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/utils/num.cpp 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/cpp/utils/num.h 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/java/com/anyun/exam/lib/RemoteService.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/java/com/anyun/exam/lib/util/ByteUtil.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/java/com/anyun/exam/lib/util/DESUtil.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/java/com/anyun/exam/lib/util/NetUtils.java 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/src/main/java/com/anyun/exam/lib/util/Speaker.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/codeStyles/Project.xml
@@ -1,29 +1,113 @@
<component name="ProjectCodeStyleConfiguration">
  <code_scheme name="Project" version="173">
    <Objective-C-extensions>
      <file>
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
      </file>
      <class>
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
      </class>
      <extensions>
        <pair source="cpp" header="h" fileNamingConvention="NONE" />
        <pair source="c" header="h" fileNamingConvention="NONE" />
      </extensions>
    </Objective-C-extensions>
    <codeStyleSettings language="XML">
      <arrangement>
        <rules>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>xmlns:android</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>xmlns:.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*:id</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*:name</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>name</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>style</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
              <order>ANDROID_ATTRIBUTE_ORDER</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>.*</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
        </rules>
      </arrangement>
    </codeStyleSettings>
  </code_scheme>
</component>
lib/build.gradle
@@ -12,6 +12,16 @@
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
        ndk {
            // Specifies the ABI configurations of your native
            // libraries Gradle should build and package with your APK.
            abiFilters 'armeabi-v7a','arm64-v8a'
        }
    }
    buildTypes {
@@ -21,6 +31,11 @@
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
}
dependencies {
lib/src/main/cpp/CMakeLists.txt
New file
@@ -0,0 +1,63 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
        native-lib
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        native-lib.cpp
        common/serial_port.cpp
        common/net.cpp
        common/apptimer.cpp
        rtk_platform/parse_net.cpp
        rtk_platform/platform.cpp
        rtk_module/parse_gps.cpp
        Geometry.cpp
        driver_test.cpp
        mcu/mcu_if.cpp
        test_items/error_list.cpp
        test_items/park_edge.cpp
        test_items/park_bottom.cpp
        test_items/stop_and_start.cpp
        test_items/driving_curve.cpp
        test_items/turn_a90.cpp
        rtk_module/rtk.cpp
        utils/crc16.cpp
        utils/num.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
        log-lib
        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
        native-lib
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})
lib/src/main/cpp/Geometry.cpp
New file
@@ -0,0 +1,390 @@
//
// Created by YY on 2019/4/30.
//
#include "defs.h"
#include "Geometry.h"
#include <stdbool.h>
#include <stdint.h>
#include <cmath>
#include <jni.h>
#include <malloc.h>
#include <initializer_list>
#include "jni_log.h"
using namespace std;
const double EPSILON = 1e-6;
inline bool isEqual(double a, double b)
{
    return (fabs(a - b) <= EPSILON);
}
inline double toRadians(double degree)
{
    return (degree * M_PI) / 180.0;
}
inline double toDegree(double radians)
{
    return (radians * 180.0) / M_PI;
}
void MakeLine(Line *line, const PointF *p1, const PointF *p2)
{
    line->X1 = p1->X;
    line->Y1 = p1->Y;
    line->X2 = p2->X;
    line->Y2 = p2->Y;
}
void MakePolygon(Polygon *polygon, std::initializer_list<PointF> point_set)
{
    int n = 0;
    polygon->point = (PointF *)malloc(point_set.size() * sizeof(PointF));
    for (auto ptr : point_set) {
        polygon->point[n++] = ptr;
    }
    polygon->num = n;
}
void MakeHidePoint(PointF *point, const PointF *bp, const Line *bl)
{
    point->X = (bl->X1 + bl->X2) - bp->X;
    point->Y = (bl->Y1 + bl->Y2) - bp->Y;
}
void CleanPolygon(Polygon *polygon)
{
    if (polygon->point != NULL) {
        free(polygon->point);
        polygon->point = NULL;
    }
    polygon->num = 0;
}
Relation IntersectionOf(const Polygon *polygon1, const Polygon *polygon2)
{
    bool inside = false, outside = false, tangent = false;
    if (polygon1->num == 0 || polygon2->num == 0) {
        return GM_None;
    }
    for (int idx = 0; idx < polygon1->num; ++idx) {
        Relation relation = IntersectionOf(polygon1->point[idx], polygon2);
        if (relation == GM_Containment) {
            inside = true;
        } else if (relation == GM_None) {
            outside = true;
        } else {
            tangent = true;
        }
        if (inside && outside) {
            break;
        }
    }
    if (inside && outside) {
        return GM_Intersection;
    } else if (tangent) {
        return GM_Tangent;
    } else if (inside) {
        return GM_Containment;
    }
    return GM_None;
}
Relation IntersectionOf(Line line, const Polygon *polygon)
{
    if (polygon->num == 0) {
        return GM_None;
    }
    if (polygon->num == 1) {
        return IntersectionOf(polygon->point[0], line);
    }
    bool tangent = false;
    for (int index = 0; index < polygon->num; index++) {
        int index2 = (index + 1) % polygon->num;
        Line line2;
        line2.X1 = polygon->point[index].X;
        line2.Y1 = polygon->point[index].Y;
        line2.X2 = polygon->point[index2].X;
        line2.Y2 = polygon->point[index2].Y;
//        LOGD("line1(%d %d - %d %d) line2(%d %d - %d %d)", line.X1, line.Y1, line.X2, line.Y2,
//             line2.X1, line2.Y1, line2.X2, line2.Y2);
        Relation relation = IntersectionOf(line, line2);
//        LOGD("relation = %d", relation);
        if (relation == GM_Intersection) {
            return relation;
        }
        if (relation == GM_Tangent) {
            tangent = true;
        }
    }
    PointF point2;
    point2.X = line.X1;
    point2.Y = line.Y1;
    return tangent ? GM_Tangent : IntersectionOf(point2, polygon);
}
Relation IntersectionOf(PointF point, const Polygon *polygon)
{
    switch (polygon->num)
    {
        case 0:
            return GM_None;
        case 1:
            if (isEqual(polygon->point[0].X, point.X) &&
                isEqual(polygon->point[0].Y, point.Y)) {
                return GM_Tangent;
            }
            else {
                return GM_None;
            }
        case 2: {
            Line line2;
            line2.X1 = polygon->point[0].X;
            line2.Y1 = polygon->point[0].Y;
            line2.X2 = polygon->point[1].X;
            line2.Y2 = polygon->point[1].Y;
            return IntersectionOf(point, line2);
        }
        default:
            break;
    }
    int counter = 0;
    int i;
    PointF p1;
    int n = polygon->num;
    p1.X = polygon->point[0].X;
    p1.Y = polygon->point[0].Y;
    if (isEqual(point.X, p1.X) && isEqual(point.Y, p1.Y)) {
        return GM_Tangent;
    }
    for (i = 1; i <= n; i++) {
        PointF p2;
        p2.X = polygon->point[i % n].X;
        p2.Y = polygon->point[i % n].Y;
        if (isEqual(point.X, p2.X) && isEqual(point.Y, p2.Y)) {
            return GM_Tangent;
        }
        if (point.Y > fmin(p1.Y, p2.Y)) {
            if (point.Y <= fmax(p1.Y, p2.Y)) {
                if (point.X <= fmax(p1.X, p2.X)) {
                    if (p1.Y != p2.Y) {
                        double xinters = (point.Y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y) + p1.X;
                        if (isEqual(p1.X, p2.X) || point.X <= xinters)
                            counter++;
                    }
                }
            }
        }
        p1 = p2;
    }
    return (counter % 2 == 1) ? GM_Containment : GM_None;
}
Relation IntersectionOf(PointF point, Line line)
{
    double bottomY = fmin(line.Y1, line.Y2);
    double topY = fmax(line.Y1, line.Y2);
    bool heightIsRight = point.Y >= bottomY &&
                         point.Y <= topY;
    //Vertical line, slope is divideByZero error!
    if (isEqual(line.X1, line.X2)) {
        if (isEqual(point.X, line.X1) && heightIsRight) {
            return GM_Tangent;
        } else {
            return GM_None;
        }
    }
    double slope = (line.X2 - line.X1) / (line.Y2 - line.Y1);
    bool onLine = isEqual(line.Y1 - point.Y, slope * (line.X1 - point.X));
    if (onLine && heightIsRight) {
        return GM_Tangent;
    } else {
        return GM_None;
    }
}
Relation IntersectionOf(Line line1, Line line2)
{
    //  Fail if either line segment is zero-length.
    if ((isEqual(line1.X1, line1.X2) && isEqual(line1.Y1, line1.Y2)) || (isEqual(line2.X1, line2.X2) && isEqual(line2.Y1, line2.Y2)))
        return GM_None;
    if ((isEqual(line1.X1, line2.X1) && isEqual(line1.Y1, line2.Y1)) || (isEqual(line1.X2, line2.X1) && isEqual(line1.Y2, line2.Y1)))
        return GM_Intersection;
    if ((isEqual(line1.X1, line2.X2) && isEqual(line1.Y1, line2.Y2)) || (isEqual(line1.X2, line2.X2) && isEqual(line1.Y2, line2.Y2)))
        return GM_Intersection;
    //  (1) Translate the system so that point A is on the origin.
    line1.X2 -= line1.X1; line1.Y2 -= line1.Y1;
    line2.X1 -= line1.X1; line2.Y1 -= line1.Y1;
    line2.X2 -= line1.X1; line2.Y2 -= line1.Y1;
    //  Discover the length of segment A-B.
    double distAB = sqrt(line1.X2 * line1.X2 + line1.Y2 * line1.Y2);
    //  (2) Rotate the system so that point B is on the positive X axis.
    double theCos = line1.X2 / distAB;
    double theSin = line1.Y2 / distAB;
    double newX = line2.X1 * theCos + line2.Y1 * theSin;
    line2.Y1 = line2.Y1 * theCos - line2.X1 * theSin;
    line2.X1 = newX;
    newX = line2.X2 * theCos + line2.Y2 * theSin;
    line2.Y2 = line2.Y2 * theCos - line2.X2 * theSin;
    line2.X2 = newX;
    //  Fail if segment C-D doesn't cross line A-B.
    if ((line2.Y1 < 0 && line2.Y2 < 0) || (line2.Y1 >= 0 && line2.Y2 >= 0)) {
        return GM_None;
    }
    //  (3) Discover the position of the intersection point along line A-B.
    double posAB = line2.X2 + (line2.X1 - line2.X2) * line2.Y2 / (line2.Y2 - line2.Y1);
    //  Fail if segment C-D crosses line A-B outside of segment A-B.
    if (posAB < 0 || posAB > distAB) {
        return GM_None;
    }
    //  (4) Apply the discovered position to line A-B in the original coordinate system.
    return GM_Intersection;
}
double DistanceOf(PointF point1, PointF point2)
{
    return sqrt((point1.X-point2.X)*(point1.X-point2.X) + (point1.Y-point2.Y)*(point1.Y-point2.Y));
}
double DistanceOf(PointF point, Line line)
{
//    float a = sqrt((point.X-line.X1)*(point.X-line.X1) + (point.Y-line.Y1)*(point.Y-line.Y1));
//    float b = sqrt((point.X-line.X2)*(point.X-line.X2) + (point.Y-line.Y2)*(point.Y-line.Y2));
    double c = sqrt((line.X1-line.X2)*(line.X1-line.X2) + (line.Y1-line.Y2)*(line.Y1-line.Y2));
//    float p = (a+b+c)/2;
//    dis = 2 * sqrt(p*(p-a)*(p-b)*(p-c)) / c;
    return fabs(point.X*line.Y1 + point.Y*line.X2 + line.X1*line.Y2 - line.X2*line.Y1 - line.X1*point.Y - point.X*line.Y2) / c;
}
/**********************************************************
 * base 和 dest的第二点重合时形成的夹角
 * @param base
 * @param dest
 * @return
 */
double CalculateAngle(Line base, Line dest)
{
    double angle = 0;
    double dx = base.X2 - dest.X2;
    double dy = base.Y2 - dest.Y2;
    dest.X1 += dx;
    dest.Y1 += dy;
    double c2 = pow((dest.X1 - base.X1), 2) + pow((dest.Y1 - base.Y1), 2);
    double a2 = pow((base.X1 - base.X2), 2) + pow((base.Y1 - base.Y2), 2);
    double b2 = pow((dest.X1 - base.X2), 2) + pow((dest.Y1 - base.Y2), 2);
    angle = acos((a2 + b2 - c2) / (2 * sqrt(a2) * sqrt(b2)));
    return toDegree(angle);
}
PointF rotatePoint(PointF oldPoint, PointF centre, double degree) {
    PointF newPoint;
    newPoint.X = (oldPoint.X-centre.X)*cos(toRadians(degree)) - (oldPoint.Y-centre.Y)*sin(toRadians(degree)) + centre.X;
    newPoint.Y = (oldPoint.X-centre.X)*sin(toRadians(degree)) + (oldPoint.Y-centre.Y)*cos(toRadians(degree)) + centre.Y;
    return newPoint;
}
// All in
bool InsidePolygon(const Polygon *t1, const Polygon *t2) {
    for (int i = 0; i < t1->num; ++i) {
        if (IntersectionOf(t1->point[i], t2) != GM_Containment) {
            return false;
        }
    }
    return true;
}
// Any part inside
bool PartInsidePolygon(const Polygon *t1, const Polygon *t2) {
    for (int i = 0; i < t1->num; ++i) {
        if (IntersectionOf(t1->point[i], t2) == GM_Containment) {
            return true;
        }
    }
    return false;
}
// All out
bool OutsidePolygon(const Polygon *t1, const Polygon *t2) {
    for (int i = 0; i < t1->num; ++i) {
        if (IntersectionOf(t1->point[i], t2) != GM_None) {
            return false;
        }
    }
    return true;
}
/***************************************************************
 * @brief p3位于由p1->p2构成的射线,左侧还是右侧,同向,反向
 * @param p1
 * @param p2
 * @param p3
 * @return 0 - straight, 1 - left, -1 - right, 2 - front, -2 - back
 */
int IntersectionOfLine(PointF p1, PointF p2, PointF p3)
{
    double lr = (p1.X-p3.X)*(p2.Y-p3.Y) - (p1.Y-p3.Y)*(p2.X-p3.X);
    if (fabs(lr) <= EPSILON) {
        double fb = (p2.X-p1.X)*(p3.X-p1.X) + (p2.Y-p1.Y)*(p3.Y-p1.Y);
        if (fabs(fb) <= EPSILON)
            return 0;
        else if (fb > 0)
            return 2;
        else
            return -2;
    } else if (lr > 0) {
        return 1;
    } else {
        return -1;
    }
}
lib/src/main/cpp/Geometry.h
New file
@@ -0,0 +1,58 @@
//
// Created by YY on 2019/4/30.
//
#ifndef GUI_GEOMETRY_H
#define GUI_GEOMETRY_H
#include <stdint.h>
#include <initializer_list>
enum Relation
{
    GM_None,
    GM_Tangent,
    GM_Intersection,
    GM_Containment
};
typedef struct PointF_ {
    double X;
    double Y;
} PointF;
typedef struct Line_ {
    double X1;
    double Y1;
    double X2;
    double Y2;
} Line;
typedef struct Polygon_ {
    int num;
    PointF *point;
} Polygon;
inline double toRadians(double degree);
inline double toDegree(double radians);
inline bool isEqual(double a, double b);
void MakeLine(Line *line, const PointF *p1, const PointF *p2);
void MakePolygon(Polygon *polygon, std::initializer_list<PointF> point_set);
void CleanPolygon(Polygon *polygon);
void MakeHidePoint(PointF *point, const PointF *bp, const Line *bl);
Relation IntersectionOf(const Polygon *polygon1, const Polygon *polygon2);
Relation IntersectionOf(Line line, const Polygon *polygon);
Relation IntersectionOf(PointF point, const Polygon *polygon);
Relation IntersectionOf(PointF point, Line line);
Relation IntersectionOf(Line line1, Line line2);
double DistanceOf(PointF point1, PointF point2);
double DistanceOf(PointF point, Line line);
double CalculateAngle(Line base, Line dest);
PointF rotatePoint(PointF oldPoint, PointF centre, double degree);
bool InsidePolygon(const Polygon *t1, const Polygon *t2);
bool PartInsidePolygon(const Polygon *t1, const Polygon *t2);
bool OutsidePolygon(const Polygon *t1, const Polygon *t2);
int IntersectionOfLine(PointF p1, PointF p2, PointF p3);
#endif //GUI_GEOMETRY_H
lib/src/main/cpp/common/apptimer.cpp
New file
@@ -0,0 +1,188 @@
//POSIX.1b Timer
#include <jni.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
#include "../jni_log.h"
#include "apptimer.h"
#define MAX_TIMER    32
static struct {
    timer_t timerId;
    void (*func)(union sigval sig);
    int value;
    uint8_t *user_data;
} AppTimer[MAX_TIMER];
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static int createTimer(timer_t *timerId, void (*func)(union sigval sig),
                       int value,
                       uint8_t *usr_data, int usr_data_length,
                       uint8_t **usr_data_ptr) {
    struct sigevent sev;
    pthread_attr_t attr;
    // Register printMsg to SIGALRM
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
    memset(&sev, 0, sizeof(sev));
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = func;
    sev.sigev_notify_attributes = &attr;
    if (usr_data != NULL && usr_data_length != 0) {
        if ((sev.sigev_value.sival_ptr = malloc(usr_data_length)) != NULL) {
            *usr_data_ptr = (uint8_t *) sev.sigev_value.sival_ptr;
            memcpy(sev.sigev_value.sival_ptr, usr_data, usr_data_length);            //Copy usr data
            sev.sigev_value.sival_int = usr_data_length;
        } else {
            return -1;
        }
    } else {
        sev.sigev_value.sival_int = value;
    }
    /* create timer */
    if (timer_create(CLOCK_REALTIME, &sev, timerId) == -1) {
        return -1;
    }
//    LOGD("timer_create\n");
    return 0;
}
static int setTimer(timer_t timerId, int timeMSec) {
    struct itimerspec its;
    /* Start the timer */
    its.it_value.tv_sec = timeMSec / 1000;
    its.it_value.tv_nsec = (timeMSec % 1000) * 1000000;
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;
    if (timer_settime(timerId, 0, &its, NULL) == -1) {
        return -1;
    }
//    LOGD("timer_settime\n");
    return 0;
}
void AppTimer_Init(void) {
    memset(AppTimer, 0, sizeof(AppTimer));
    pthread_mutex_init(&mutex, NULL);
}
void AppTimer_add(void (*func)(union sigval), int timeMS) {
    int i;
//    LOGD("AppTimer_add\n");
    pthread_mutex_lock(&mutex);
    for (i = 0; i < MAX_TIMER; i++) {
        if (AppTimer[i].func == NULL) {
            if (createTimer(&(AppTimer[i].timerId), func, 0, NULL, 0, &(AppTimer[i].user_data)) == 0) {
                AppTimer[i].func = func;
                if (setTimer(AppTimer[i].timerId, timeMS) !=
                    0) {            //Set timer fail, delele it
                    timer_delete(AppTimer[i].timerId);
                    if (AppTimer[i].user_data != NULL) {
                        free(AppTimer[i].user_data);
                        AppTimer[i].user_data = NULL;
                    }
                    AppTimer[i].func = NULL;
                }
            }
            break;
        }
    }
    pthread_mutex_unlock(&mutex);
}
void AppTimer_add(void (*func)(union sigval), int timeMS, uint8_t *data, int length) {
    int i;
//    LOGD("AppTimer_add\n");
    pthread_mutex_lock(&mutex);
    for (i = 0; i < MAX_TIMER; i++) {
        if (AppTimer[i].func == NULL) {
            if (createTimer(&(AppTimer[i].timerId), func, 0, data, length, &(AppTimer[i].user_data)) == 0) {
                AppTimer[i].func = func;
                if (setTimer(AppTimer[i].timerId, timeMS) !=
                    0) {            //Set timer fail, delele it
                    timer_delete(AppTimer[i].timerId);
                    if (AppTimer[i].user_data != NULL) {
                        free(AppTimer[i].user_data);
                        AppTimer[i].user_data = NULL;
                    }
                    AppTimer[i].func = NULL;
                }
            }
            break;
        }
    }
    pthread_mutex_unlock(&mutex);
}
void AppTimer_add(void (*func)(union sigval), int timeMS, int value) {
    int i;
//    LOGD("AppTimer_add\n");
    pthread_mutex_lock(&mutex);
    for (i = 0; i < MAX_TIMER; i++) {
        if (AppTimer[i].func == NULL) {
            if (createTimer(&(AppTimer[i].timerId), func, value, NULL, 0, &(AppTimer[i].user_data)) == 0) {
                AppTimer[i].func = func;
                if (setTimer(AppTimer[i].timerId, timeMS) !=
                    0) {            //Set timer fail, delele it
                    timer_delete(AppTimer[i].timerId);
                    if (AppTimer[i].user_data != NULL) {
                        free(AppTimer[i].user_data);
                        AppTimer[i].user_data = NULL;
                    }
                    AppTimer[i].func = NULL;
                }
            }
            break;
        }
    }
    pthread_mutex_unlock(&mutex);
}
void AppTimer_delete(void (*func)(union sigval)) {
    int i;
//    LOGD("AppTimer_delete\n");
    pthread_mutex_lock(&mutex);
    for (i = 0; i < MAX_TIMER; i++) {
        if (AppTimer[i].func == func) {
            timer_delete(AppTimer[i].timerId);
            if (AppTimer[i].user_data != NULL) {
                free(AppTimer[i].user_data);
                AppTimer[i].user_data = NULL;
            }
            AppTimer[i].func = NULL;
        }
    }
    pthread_mutex_unlock(&mutex);
}
uint32_t AppTimer_GetTickCount(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
lib/src/main/cpp/common/apptimer.h
New file
@@ -0,0 +1,23 @@
#ifndef _APPTIMER_H_
#define _APPTIMER_H_
#include <stdint.h>
#include <signal.h>
#define D_SEC(n)        ((n)*1000UL)
#define D_MIN(n)        ((n)*1000UL*60UL)
#define D_HOUR(n)        ((n)*1000UL*60UL*60UL)
extern void AppTimer_Init(void);
extern void AppTimer_add(void (*func)(union sigval), int timeMS);
extern void AppTimer_add(void (*func)(union sigval), int timeMS, uint8_t *data, int length);
extern void AppTimer_add(void (*func)(union sigval), int timeMS, int value);
extern void AppTimer_delete(void (*func)(union sigval));
extern uint32_t AppTimer_GetTickCount(void);
#endif
lib/src/main/cpp/common/net.cpp
New file
@@ -0,0 +1,482 @@
//
// Created by YY on 2019/9/29.
//
#include <netdb.h>
#include <cstring>
#include <netinet/tcp.h>
#include <unistd.h>
#include <netinet/in.h>
#include <cstdio>
#include <cerrno>
#include <sys/select.h>
#include <arpa/inet.h>
#include <regex>
#include <pthread.h>
#include "net.h"
#include "../jni_log.h"
using namespace std;
/*************************************************************
    host_name:domain name
    net_addr:return numbers-and-dots notation
*/
int GetHostIP(const char *host_name, char *net_addr)
{
    struct hostent *hptr;
    char **pptr;
    char str[32];
    bool found_first = false;
    if ((hptr = gethostbyname(host_name)) == NULL) {
        LOGE("gethostbyname error\n");
        return -1;
    }
    LOGD("official hostname: %s\n", hptr->h_name);
    for (pptr = hptr->h_aliases; *pptr != NULL; pptr++) {
        LOGD("alias:%s\n", *pptr);
    }
    switch(hptr->h_addrtype)
    {
        case AF_INET:
        {
            for (pptr = hptr->h_addr_list; *pptr != NULL; pptr++) {
                LOGD("addrsss:%s\n", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
                if (!found_first) {
                    strcpy(net_addr, str);
                    found_first = true;
                }
            }
            break;
        }
        case AF_INET6:
        default:
            LOGD("unknown address type\n");
            break;
    }
    if (found_first == true) {
        return 0;
    }
    return -2;
}
static bool is_domain_name(const char *ip)
{
    regex pattern0("^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$");
    string target(ip);
    return !regex_match(target, pattern0);
}
static int socket_set_keepalive(int fd)
{
    int alive, idle, cnt, intv;
//    int value, value_len;
//    getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &value_len);
//    LOGD("keepalive 0 %d", value);
//    value = 128;
//    setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
//    LOGD("keepalive 1 %d", value);
//    getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &value_len);
//    LOGD("keepalive 1 %d", value);
    /* Set: use keepalive on fd, default 0 */
    alive = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &alive, sizeof(alive)) != 0)
    {
        LOGE("TCP Set keepalive error");
        return -1;
    }
    /* 20 Seconds not data, send keeplive packet, default 7200 */
    idle = 20;
    if (setsockopt (fd, SOL_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)) != 0)
    {
        LOGE("TCP Set keepalive idle error");
        return -1;
    }
    /* If not recv respond, After 5 seconds retry, default 75 */
    intv = 5;
    if (setsockopt (fd, SOL_TCP, TCP_KEEPINTVL, &intv, sizeof(intv)) != 0)
    {
        LOGE("TCP Set keepalive intv error");
        return -1;
    }
    /* If try 9 times and fail, we consider the tcp is disconnected, default 9 */
    cnt = 9;
    if (setsockopt (fd, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)) != 0)
    {
        LOGE("TCP Set keepalive cnt error");
        return -1;
    }
/*    int timeout = 10000;        // 10秒
    if (setsockopt (fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout)) != 0)
    {
        LOGE("TCP Set keepalive timeout error");
        return -1;
    }*/
    LOGD("TCP Set keepalive OK");
    return 0;
}
static int tcp_connect(char *ip, uint16_t port)
{
    struct sockaddr_in server_sockaddr;
    int soc;
    int ret, fds_ret;
    struct timeval tv;
    fd_set rdfds;
    long arg;
    int error_value;
    socklen_t error_value_len;
    LOGI("%s", __func__);
    error_value_len = sizeof( error_value );
    if((soc = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
        LOGE("%s: socket", __func__);
        return -1;
    }
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_addr.s_addr = inet_addr(ip);//htonl(ip);//inet_addr("192.168.1.37");
    server_sockaddr.sin_port = htons(port);
    bzero(&server_sockaddr.sin_zero, 8);
    // Set non-blocking
    if( (arg = fcntl(soc, F_GETFL, NULL)) < 0)
    {
        LOGE("%s: fcntl( F_GETFL ) error", __func__);
        goto TCP_CONNECT_1;
    }
    if( fcntl(soc, F_SETFL, arg | O_NONBLOCK) < 0)
    {
        LOGE( "%s: fcntl( F_SETFL ) error", __func__);
        goto TCP_CONNECT_1;
    }
    ret = connect(soc, (struct sockaddr*)&server_sockaddr, sizeof(struct sockaddr));
    if(ret < 0)
    {
        if(errno != EINPROGRESS)        //It usually return this error!
        {
            /*
             * connect error: Connection refused
             *
             * perror( "connect error" );
             */
            goto TCP_CONNECT_1;
        }
    }
    else if(ret == 0)
    {
        goto TCP_CONNECT_0;
    }
    tv.tv_sec = 10;        //Connect Timeout
    tv.tv_usec = 0;
    FD_ZERO(&rdfds);
    FD_SET(soc, &rdfds);
    fds_ret = select(soc + 1, NULL, &rdfds, NULL, &tv);
    if(fds_ret < 0)
    {
        LOGE( "%s: TCP select error", __func__);
        goto TCP_CONNECT_1;
    }
    else if(fds_ret == 0)
    {
        LOGE("%s: TCP Connect Timeout %ld", __func__, tv.tv_sec);
        goto TCP_CONNECT_1;
    }
    else if(FD_ISSET(soc, &rdfds))
    {
        if(getsockopt(soc, SOL_SOCKET, SO_ERROR, &error_value, &error_value_len) < 0)
        {
            goto TCP_CONNECT_1;
        }
    }
    else
    {
        LOGE("%s: some error occur in tcp_connect()", __func__);
        goto TCP_CONNECT_1;
    }
    TCP_CONNECT_0:
//    if ( fcntl( soc, F_SETFL, arg ) < 0 )
//    {
//        perror( "fcntl( F_SETFL ) error" );
//        goto TCP_CONNECT_1;
//    }
    if ( error_value != 0 )
    {
        /*
         * Error: Connection refused
         *
         * fprintf( stderr, "Error: %s", strerror( error_value ) );
         */
        goto TCP_CONNECT_1;
    }
    if(socket_set_keepalive(soc) != 0)
    {
        goto TCP_CONNECT_1;
    }
    arg &= ~O_NONBLOCK;
    if( fcntl(soc, F_SETFL, arg) < 0)
    {
        LOGE( "%s: fcntl( F_SETFL ) error", __func__);
        goto TCP_CONNECT_1;
    }
    LOGI("%s: tcp connected %s: %d", __func__, ip, port);
    return( soc );
    TCP_CONNECT_1:
    close( soc );
    return( -1 );
}
static int udp_connect(const char *ip, uint16_t port) {
    struct sockaddr_in server_sockaddr;
    int soc;
    int opt;
    int arg;
    if ((soc = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
        return -1;
    }
    /*Enable send broadcast packet*/
    opt = 1;
    if (setsockopt(soc, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0) {
        perror("setsockopt");
        close(soc);
        return -1;
    }
    // Set non-blocking
    if( (arg = fcntl(soc, F_GETFL, NULL)) < 0 ) {
        close(soc);
        return -1;
    }
    if ( fcntl(soc, F_SETFL, arg | O_NONBLOCK) < 0 ) {
        close(soc);
        return -1;
    }
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_addr.s_addr = inet_addr(ip);//htonl(INADDR_ANY);
    server_sockaddr.sin_port = htons(port);
    bzero(&server_sockaddr.sin_zero, 8);
    return soc;
}
int WriteTCP(int fd, const uint8_t * buf, uint32_t len)
{
    int ret = 0;
    int fds_ret;
    struct timeval tv;
    fd_set rdfds;
    if (fd < 0)
        return -2;
    /*******************************************************
    TCP send with nonblock, if occur failure(such as line disconnect...),
    will send timeout, so terminate TCP.
    */
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    FD_ZERO(&rdfds); //clean
    FD_SET(fd, &rdfds); //set
    fds_ret = select(fd + 1, NULL, &rdfds, NULL, &tv);
    if (fds_ret < 0) {
        LOGE("tcp error send select error");
        return -1;
    } else if(fds_ret == 0) {
        LOGE("tcp error Occur failure(such as line disconnect)");
        //Occur failure(such as line disconnect)
        ret = -1;
    } else if(FD_ISSET(fd, &rdfds)) {
        ret = send(fd, buf, len, 0);
        if(ret == -1) {
            LOGE("tcp error TCP Send Error");
        }
    } else {
        LOGE("tcp error tcp send has error\n");
    }
    return ret;
}
int ReadTCP(int fd, uint8_t * buf, uint32_t len)
{
    if (fd < 0) {
        return -2;
    }
    int recvLen = recv(fd, buf, len, 0);        //read socket data from server
    if (recvLen == 0) {
        LOGW("tcp error TCP disconnected 0 errno = %d", errno);
        return -1;
    } else if (recvLen < 0) {
        if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
            return 0;
        } else {
            LOGW("tcp error TCP disconnected errno = %d", errno);;
            return -1;
        }
    }
    return recvLen;
}
int ConnectTCP(const char *ip, uint16_t port)
{
    char net_addr[32] = {0};
    if (is_domain_name(ip)) {
        if (GetHostIP(ip, net_addr) != 0) {
            return -3;
        }
    } else {
        strcpy(net_addr, ip);
    }
    return tcp_connect(net_addr, port);
}
void DisconnectTCP(int fd)
{
    LOGI("DisconnectTCP fd = %d", fd);
    if (fd >= 0) {
        shutdown(fd, SHUT_RDWR);
        close(fd);
    }
}
int EstablishUDP(const char *ip, uint16_t port)
{
    char net_addr[32] = {0};
    if (is_domain_name(ip)) {
        if (GetHostIP(ip, net_addr) != 0) {
            return -3;
        }
    } else {
        strcpy(net_addr, ip);
    }
    return udp_connect(net_addr, port);
}
void RemoveUDP(int fd)
{
    close(fd);
}
int WriteUDP(int fd, char *ip, uint16_t port, const uint8_t * buf, uint32_t len)
{
    int ret = 0;
    int fds_ret;
    struct timeval tv;
    fd_set rdfds;
    if (fd < 0)
        return -2;
    /*******************************************************
    TCP send with nonblock, if occur failure(such as line disconnect...),
    will send timeout, so terminate TCP.
    */
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    FD_ZERO(&rdfds); //clean
    FD_SET(fd, &rdfds); //set
    fds_ret = select(fd + 1, NULL, &rdfds, NULL, &tv);
    if (fds_ret < 0)
    {
        LOGE("UDP send select error");
        return -1;
    }
    else if(fds_ret == 0)
    {
        LOGE("Occur failure(such as line disconnect)");
        ret = -1;
    }
    else if(FD_ISSET(fd, &rdfds))
    {
        struct sockaddr_in server_sockaddr;
        server_sockaddr.sin_family = AF_INET;
        server_sockaddr.sin_addr.s_addr = inet_addr(ip);//htonl(INADDR_ANY);
        server_sockaddr.sin_port = htons(port);
        bzero(&server_sockaddr.sin_zero, 8);
        ret = sendto(fd, buf, len, 0, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr_in));
        if(ret == -1)
        {
            LOGE("UDP Send Error");
        }
    }
    else
    {
        LOGE("UDP send has error\n");
    }
    return ret;
}
int ReadUDP(int fd, uint8_t * buf, uint32_t len)
{
    struct sockaddr_in from_addr;
    socklen_t from_len;
    int recvLen = recvfrom(fd, buf, len, 0,
                           (struct sockaddr *) &from_addr,
                           &from_len);//read socket data from server//
    if (recvLen == 0) {
        return 0;
    } else if (recvLen < 0) {
        if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
            return 0;
        } else {
            LOGE("UDP ERROR!! = %d", errno);
            return -1;
        }
    }
    return recvLen;
}
lib/src/main/cpp/common/net.h
New file
@@ -0,0 +1,17 @@
//
// Created by YY on 2019/9/29.
//
#ifndef RTKBASESTATION_NET_H
#define RTKBASESTATION_NET_H
int WriteTCP(int fd, const uint8_t * buf, uint32_t len);
int ReadTCP(int fd, uint8_t * buf, uint32_t len);
int ConnectTCP(const char *ip, uint16_t port);
void DisconnectTCP(int fd);
int EstablishUDP(const char *ip, uint16_t port);
void RemoveUDP(int fd);
int WriteUDP(int fd, char *ip, uint16_t port, const uint8_t * buf, uint32_t len);
int ReadUDP(int fd, uint8_t * buf, uint32_t len);
#endif //RTKBASESTATION_NET_H
lib/src/main/cpp/common/serial_port.cpp
New file
@@ -0,0 +1,306 @@
//
// Created by YY on 2017/9/5.
//
#include <jni.h>
#include <string>
#include <sys/stat.h>
#include <stdbool.h>
#include <stdint.h>
#include <termios.h>
#include <android/log.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include "../jni_log.h"
#include "serial_port.h"
using namespace std;
static volatile int serial_port_fd[2] = {0, 0};
static pthread_mutex_t mutex[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};
int setRTS(int fd, int level)
{
    int status;
    if (ioctl(fd, TIOCMGET, &status) == -1) {
        LOGD("setRTS(): TIOCMGET");
        return 0;
    }
    if (level)
        status |= TIOCM_RTS;
    else
        status &= ~TIOCM_RTS;
    if (ioctl(fd, TIOCMSET, &status) == -1) {
        LOGD("setRTS(): TIOCMSET");
        return 0;
    }
    return 1;
}
/*********************************************************************
 * PUBLIC FUNCTIONS
 */
static int SetSerialPort(int fd, int speed, int databits, char parity, int stopbits, int flowctrl) {
    int status = 0;
    struct termios opt;
    int speed_arr[] = {B921600, B576000, B500000, B460800, B230400, B115200, B38400, B19200,
                       B9600, B4800, B2400, B1200, B300};
    int name_arr[] = {921600, 576000, 500000, 460800, 230400, 115200, 38400, 19200, 9600, 4800,
                      2400, 1200, 300};
    status = tcgetattr(fd, &opt);
    if (status != 0) {
        goto SET_SERIAL_PORT_END;
    }
    status = tcflush(fd, TCIOFLUSH);
    if (status != 0) {
        goto SET_SERIAL_PORT_END;
    }
    //Set baud
    for (int i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
        if (speed == name_arr[i]) {
            cfsetispeed(&opt, speed_arr[i]);
            cfsetospeed(&opt, speed_arr[i]);
            break;
        }
    }
    //Set databit
    opt.c_cflag &= ~CSIZE;
    if (databits == 7) {
        opt.c_cflag |= CS7;
    } else {
        opt.c_cflag |= CS8;
    }
    //Set parity
    switch (parity) {
        case 'E':
        case 'e':
            opt.c_cflag |= PARENB;        /*Enable parity*/
            opt.c_cflag &= ~PARODD;
            opt.c_iflag |= INPCK;        /*Enable pairty checking*/
            break;
        case 'O':
        case 'o':
            opt.c_cflag |= PARENB | PARODD;
            opt.c_iflag |= INPCK;
            break;
        case 'N':
        case 'n':
        default:
            opt.c_cflag &= ~PARENB;
            opt.c_iflag &= ~INPCK;
            break;
    }
    //Set stop
    if (stopbits == 2) {
        opt.c_cflag |= CSTOPB;
    } else {
        opt.c_cflag &= ~CSTOPB;
    }
    opt.c_cflag |= CLOCAL | CREAD;
    //Set raw mode
    opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);                /*Input*/
    opt.c_oflag &= ~OPOST;                                /*Output*/
    opt.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);            //Disable XON/XOFF flowcontrol
    opt.c_iflag &= ~(ICRNL | INLCR);                        //Disable 0x0d and 0x0a convert
    opt.c_cc[VTIME] = 0;//150; //15 seconds 150x100ms = 15s
    opt.c_cc[VMIN] = 1;        //Recv one char, read() will return
    if (flowctrl) {
        opt.c_cflag |= CRTSCTS;
    } else {
        opt.c_cflag &= ~CRTSCTS;                        //Disable hard flowcontrol
    }
    tcflush(fd, TCIOFLUSH);
    status = tcsetattr(fd, TCSANOW, &opt);
    SET_SERIAL_PORT_END:
    if (status != 0) {
        LOGD("Serial Port set fail");
    } else {
        LOGD("Serial Port set success");
    }
//    setRTS(fd, 1);
    return status;
}
static int OpenSerialPort(const char *name) {
    int uart_fd;
//    uart_fd = open(name, O_RDWR /*| O_NONBLOCK/*| O_NOCTTY | O_NDELAY*/);
    uart_fd = open(name, O_RDWR | O_NOCTTY);
    LOGD("open serial port fd = %d", uart_fd);
    return uart_fd;
}
static void CloseSerialPort(int fd) {
    close(fd);
}
int WriteSerialPort(int id, const void *buf, int len) {
    int ret = -1;
    int fds_ret;
    int fd = GetSerialPort(id);
    struct timeval tv;
    fd_set wrfds;
    if (fd <= 0) {
        return -1;
    }
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    FD_ZERO(&wrfds); //clean
    FD_SET(fd, &wrfds); //set
    pthread_mutex_lock(&mutex[id]);
    fds_ret = select(fd + 1, NULL, &wrfds, NULL, &tv);
    if (fds_ret < 0) {
        LOGE("Serial port select error\n");
    }
    else if(fds_ret == 0) {
        LOGE("Serial port write timeout\n");
    }
    else if(FD_ISSET(fd, &wrfds)) {
        ret = write(fd, buf, len);      //serial write data
        if (ret != len ) {
            LOGE("Serial Port Error\n");
            tcflush(fd, TCOFLUSH);
        }
    }
    else {
        LOGE("Serial Port error 2\n");
    }
    pthread_mutex_unlock(&mutex[id]);
    return ret;
}
int GetSerialPort(int id) {
    return serial_port_fd[id];
}
int InitSerialPort(int id, int baud, int dataBits, char parity, int stopBits, int flowctrl)
{
    char name[32];
    if (id == UART_0) {
        strcpy(name, "/dev/ttyHSL0");
    } else if (id == UART_1) {
        strcpy(name, "/dev/ttyHSL1");
    } else {
        return -1;
    }
    UninitSerialPort(id);
    serial_port_fd[id] = OpenSerialPort(name);
    if (serial_port_fd[id] <= 0) {
        return -1;
    }
    if (SetSerialPort(serial_port_fd[id], baud, dataBits, parity, stopBits, flowctrl) != 0) {
        return -2;
    }
    pthread_mutex_init(&mutex[id], NULL);
    return 0;
}
void UninitSerialPort(int id)
{
    if (serial_port_fd[id] > 0) {
        CloseSerialPort(serial_port_fd[id]);
        pthread_mutex_destroy(&mutex[id]);
        serial_port_fd[id] = 0;
    }
}
int ReadSerialPort(int id, uint8_t *out, uint16_t length)
{
    if (serial_port_fd[id] <= 0) return 0;
    return read(serial_port_fd[id], out, length);
}
//extern "C"
//JNIEXPORT jint JNICALL
//Java_com_example_yy_jnicallback_MyService_InitSerialPort(
//        JNIEnv *env,
//        jobject /* this */,
//        jstring name,
//        jint baud,
//        jint dataBits,
//        jbyte parity,
//        jint stopBits) {
//
//    const char *s = env->GetStringUTFChars(name, 0);
//    char item_value[128];
//    strcpy(item_value, s);
//    env->ReleaseStringUTFChars(name, s);
//    LOGD("serial port = %s", item_value);
//
//    if (serial_port_fd > 0) {
//        CloseSerialPort(serial_port_fd);
//        pthread_mutex_destroy(&mutex);
//        serial_port_fd = 0;
//    }
//
//    serial_port_fd = OpenSerialPort(item_value);
//    if (serial_port_fd <= 0) {
//        return -1;
//    }
//
//    if (SetSerialPort(serial_port_fd, baud, dataBits, parity, stopBits) != 0) {
//        return -2;
//    }
//    pthread_mutex_init(&mutex, NULL);
//    return 0;
//}
//
//extern "C"
//JNIEXPORT void JNICALL
//Java_com_example_yy_jnicallback_MyService_MonitSerialPort(
//        JNIEnv *env,
//        jobject /* this */) {
//    if (serial_port_fd > 0) {
//        uint8_t UartRxBuf[4096];
//
///*        uint8_t pkt[] = {0x7E, 0x80, 0x02, 0x00, 0x00, 0x26, 0x00, 0x00, 0x01, 0x38, 0x20, 0x20,
//                         0x55, 0x45, 0x04, 0x4E,
//                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x61, 0x63,
//                         0x0C, 0x06, 0xEA, 0x04,
//                         0xFE, 0x00, 0x00, 0x01, 0x63, 0x00, 0xB4, 0x13, 0x03, 0x05, 0x18, 0x18,
//                         0x52, 0x01, 0x04, 0x00,
//                         0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x4D, 0x7E};
//        WriteSerialPort(serial_port_fd, pkt, sizeof(pkt));*/
//
//        int length = read(serial_port_fd, UartRxBuf, sizeof(UartRxBuf));
//
//        if (length > 0) {
//            Parse(DATA_ACCESS_MCU, UartRxBuf, length);
//        }
//    }
//}
lib/src/main/cpp/common/serial_port.h
New file
@@ -0,0 +1,30 @@
//
// Created by YY on 2017/9/5.
//
#ifndef JNICALLBACK_SERIAL_PORT_H
#define JNICALLBACK_SERIAL_PORT_H
#include <stdint.h>
#define UART_0            0
#define UART_1            1
struct serial_config {
    char name[32];
    int baud;
    int data_bit;
    char verify_bit;
    int stop_bit;
    int flow_ctrl;
};
int InitSerialPort(int id, int baud, int dataBits, char parity, int stopBits, int flowctrl);
void UninitSerialPort(int id);
int GetSerialPort(int id);
int ReadSerialPort(int id, uint8_t *out, uint16_t length);
int WriteSerialPort(int id, const void *buf, int len);
int setRTS(int fd, int level);
#endif //JNICALLBACK_SERIAL_PORT_H
lib/src/main/cpp/defs.h
New file
@@ -0,0 +1,61 @@
#ifndef _DEFS_H_
#define _DEFS_H_
#include <stdint.h>
#include <stdbool.h>
#define ENABLE_DEBUG
#ifdef ENABLE_DEBUG
#define xENABLE_DEBUG_MAIN
#define ENABLE_DEBUG_BT
#define xENABLE_DEBUG_EX_OBD
#define xENABLE_DEBUG_CV520
#endif/*ENABLE_DEBUG*/
/* takes a byte out of a uint32_t : var - uint32_t,  ByteNum - byte to take out (0 - 3) */
#define BREAK_UINT32( var, ByteNum ) \
          (uint8_t)((uint32_t)(((var) >>((ByteNum) * 8)) & 0x00FF))
#define BUILD_UINT32(Byte0, Byte1, Byte2, Byte3) \
          ((uint32_t)((uint32_t)((Byte0) & 0x00FF) \
          + ((uint32_t)((Byte1) & 0x00FF) << 8) \
          + ((uint32_t)((Byte2) & 0x00FF) << 16) \
          + ((uint32_t)((Byte3) & 0x00FF) << 24)))
#define BUILD_UINT16(loByte, hiByte) \
          ((uint16_t)(((loByte) & 0x00FF) + (((hiByte) & 0x00FF) << 8)))
#define HI_UINT16(a) (((a) >> 8) & 0xFF)
#define LO_UINT16(a) ((a) & 0xFF)
#ifndef BV
#define BV(n)      (1ul << (n))
#endif
#ifndef ABS
#define ABS(n)     (((n) < 0) ? -(n) : (n))
#endif
#ifndef MAX
#define MAX(a, b)   ((a >= b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b)   ((a < b) ? (a) : (b))
#endif
const double GLB_EPSILON = 1e-6;
//#define htonl(a)    ((((uint32_t)(a) & 0xff000000) >> 24) | \
//                    (((uint32_t)(a) & 0x00ff0000) >> 8) | \
//                    (((uint32_t)(a) & 0x0000ff00) << 8) | \
//                    (((uint32_t)(a) & 0x000000ff) << 24))
//#define htons(a)    ((((uint16_t)(a) & 0xff00) >> 8) | (((uint16_t)(a) & 0x00ff) << 8))
//#define ntohl(a)    htonl(a)
//#define ntohs(a)    htons(a)
#endif
lib/src/main/cpp/driver_test.cpp
New file
@@ -0,0 +1,616 @@
//
// Created by YY on 2019/10/21.
// Units note: distance - metre
//             speed - metre per second
//             angle - DEGREES
//
#include <cstdlib>
#include <cmath>
#include <semaphore.h>
#include <pthread.h>
#include <cstring>
#include <vector>
#include "driver_test.h"
#include "defs.h"
#include "Geometry.h"
#include "common/apptimer.h"
#include "jni_log.h"
#include "test_items/park_edge.h"
#include "native-lib.h"
#include "test_items/park_bottom.h"
#include "test_items/park_edge.h"
#include "test_items/error_list.h"
#include "test_items/turn_a90.h"
#include "test_items/driving_curve.h"
#include "test_items/stop_and_start.h"
using namespace std;
#define RTK_INVALID         0
#define RTK_SINGLE_POINT   1
#define RTK_DIFF            2
#define RTK_FIX            3
#define RTK_FLOAT            4
#define CAR_MODEL_POINT_NUM             32
enum {
    TEST_NONE,
    TEST_PARK_EDGE,
    TEST_PARK_BOTTOM,
    TEST_TUNE_90,
    TEST_S_CURVE,
    TEST_SLOPE,
    TEST_SIMPLE,
    TEST_END
};
static bool TestStart = false;
static int TestItem = TEST_NONE;
static int CarInArea = 0;
int errs = 0;
vector<int> ErrorList;
static Polygon theParkEdgeMap;         // 侧位停车地图
static Polygon theTurn90Map;
static Polygon theSSMap;
#define CAR_COORD_STORE_SIZE        10
struct car_coord_ {
    uint32_t uptime;
    double azimuth;
    PointF coord;
};
struct car_desc_ {
    double distance;            // 距离主天线的距离
    double angle;               // 从中轴线逆时针形成的角度
};
struct car_desc_ *CarDesc = NULL;
struct car_coord_ *CarCoord = NULL;
static double currSpeed = 0, currAzimuth = 0;
static PointF currCoord;
static int prevDirect = 0, currDirect = 0;
static int car_coord_num = 0, car_coord_in = 0;
#define MOV_AVG_SIZE                1
#define RTK_HISTORY_SIZE            1000
#define CAR_MODEL_CACHE_SIZE      10
struct rtk_info_ {
    int qf;
    time_t uptime;
    double azimuth;
    PointF coord;
};
static struct rtk_info_ *RTKHistory = NULL;
static int rtk_history_num = 0, rtk_history_in = 0;
static pthread_mutex_t add_rtk_history_mutex = PTHREAD_MUTEX_INITIALIZER;
static carModelDesc_t *carModelDescFile = NULL;
static car_model_cache_t carModelCache[CAR_MODEL_CACHE_SIZE];
static int carModelCacheIn, carModelCacheNum;
static void UpdateCarBodyCoord(double azimuth, PointF coord);
static void UpdateCarBodyCoord(double azimuth, PointF coord, car_model_cache_t *carModel, int &carModelIn, int &carModelNum);
static bool FrontTireEnterArea(const Polygon *car, const Polygon *map);
void DriverTestInit(void)
{
    carModelDescFile = (carModelDesc_t *)malloc(sizeof(carModelDesc_t));
    carModelDescFile->body_num = 6;
    carModelDescFile->body[0] = 0;
    carModelDescFile->body[1] = 1;
    carModelDescFile->body[2] = 2;
    carModelDescFile->body[3] = 3;
    carModelDescFile->body[4] = 4;
    carModelDescFile->body[5] = 5;
    carModelDescFile->front_left_tire[0] = 1;
    carModelDescFile->front_left_tire[1] = 1;
    carModelDescFile->front_right_tire[0] = 5;
    carModelDescFile->front_right_tire[1] = 5;
    carModelDescFile->rear_left_tire[0] = 2;
    carModelDescFile->rear_left_tire[1] = 2;
    carModelDescFile->rear_right_tire[0] = 4;
    carModelDescFile->rear_right_tire[1] = 4;
    memset(&carModelCache, 0, sizeof(carModelCache));
    carModelCacheIn = carModelCacheNum = 0;
    CarDesc = (struct car_desc_ *) malloc(sizeof(struct car_desc_) * 6);
    CarDesc[0].distance = 0.2465;
    CarDesc[0].angle = 0;
    CarDesc[1].distance = 0.2635;
    CarDesc[1].angle = 20.7;
    CarDesc[2].distance = 0.14;
    CarDesc[2].angle = 138.9;
    CarDesc[3].distance = 0.1055;
    CarDesc[3].angle = 180.0;
    CarDesc[4].distance = 0.14;
    CarDesc[4].angle = 221.1;
    CarDesc[5].distance = 0.2635;
    CarDesc[5].angle = 339.3;
    memset(&theParkEdgeMap, 0, sizeof(theParkEdgeMap));
    theParkEdgeMap.num = 8;
    theParkEdgeMap.point = (PointF *)malloc(theParkEdgeMap.num * sizeof(PointF));
    theParkEdgeMap.point[0].Y = 28.013;
    theParkEdgeMap.point[0].X = -11.9669;
    theParkEdgeMap.point[1].Y = 27.137;
    theParkEdgeMap.point[1].X = -11.5114;
    theParkEdgeMap.point[2].Y = 27.5039;
    theParkEdgeMap.point[2].X = -10.8069;
    theParkEdgeMap.point[3].Y = 26.4212;
    theParkEdgeMap.point[3].X = -10.2969;
    theParkEdgeMap.point[4].Y = 26.8894;
    theParkEdgeMap.point[4].X = -9.2102;
    theParkEdgeMap.point[5].Y = 28.0027;
    theParkEdgeMap.point[5].X = -9.6513;
    theParkEdgeMap.point[6].Y = 28.3797;
    theParkEdgeMap.point[6].X = -8.9758;
    theParkEdgeMap.point[7].Y = 29.3232;
    theParkEdgeMap.point[7].X = -9.5057;
    memset(&theTurn90Map, 0, sizeof(theTurn90Map));
    theTurn90Map.num = 6;
    theTurn90Map.point = (PointF *)malloc(theTurn90Map.num * sizeof(PointF));
    theTurn90Map.point[0].Y = 10;
    theTurn90Map.point[0].X = 10;
    theTurn90Map.point[1].Y = 12.5;
    theTurn90Map.point[1].X = 10;
    theTurn90Map.point[2].Y = 12.5;
    theTurn90Map.point[2].X = 12.5;
    theTurn90Map.point[3].Y = 15;
    theTurn90Map.point[3].X = 12.5;
    theTurn90Map.point[4].Y = 15;
    theTurn90Map.point[4].X = 15;
    theTurn90Map.point[5].Y = 10;
    theTurn90Map.point[5].X = 15;
    memset(&theSSMap, 0, sizeof(theSSMap));
    theSSMap.num = 9;
    theSSMap.point = (PointF *)malloc(theSSMap.num * sizeof(PointF));
    theSSMap.point[0].Y = 10;
    theSSMap.point[0].X = 10;
    theSSMap.point[8].Y = 10;
    theSSMap.point[8].X = 15;
    theSSMap.point[1].Y = 10;
    theSSMap.point[1].X = 11.5;
    theSSMap.point[4].Y = 10;
    theSSMap.point[4].X = 12.5;
    theSSMap.point[5].Y = 10;
    theSSMap.point[5].X = 13.5;
    theSSMap.point[2].Y = 12;
    theSSMap.point[2].X = 11.5;
    theSSMap.point[3].Y = 12;
    theSSMap.point[3].X = 12.5;
    theSSMap.point[6].Y = 12;
    theSSMap.point[6].X = 13.5;
    theSSMap.point[7].Y = 12;
    theSSMap.point[7].X = 15;
    pthread_mutex_init(&add_rtk_history_mutex, NULL);
    RTKHistory = (struct rtk_info_ *) malloc(RTK_HISTORY_SIZE * sizeof(struct rtk_info_));
    rtk_history_num = rtk_history_in = 0;
    CarCoord = (struct car_coord_ *) malloc(CAR_COORD_STORE_SIZE * sizeof(struct car_coord_));
    car_coord_num = car_coord_in = 0;
    TestStart = true;
    TestItem = TEST_NONE;
    ErrorList.clear();
    TextSpeak("开始测试");
}
void UpdateRTKInfo(struct rtk_info *s)
{
    struct tm test_tm;
    struct timeval tv;
    struct timezone tz;
    gettimeofday(&tv,&tz);
    memset(&test_tm, 0, sizeof(test_tm));
    test_tm.tm_year = 2000 + s->YY - 1900;
    test_tm.tm_mon = s->MM - 1;
    test_tm.tm_mday = s->DD;
    test_tm.tm_hour = s->hh;
    test_tm.tm_min = s->mm;
    test_tm.tm_sec = s->ss;
    pthread_mutex_lock(&add_rtk_history_mutex);
    /*if (s->qf == 3)*/ {
        RTKHistory[rtk_history_in].qf = RTK_FIX;//s->qf;
        RTKHistory[rtk_history_in].uptime = mktime(&test_tm) - tz.tz_minuteswest*60;
        RTKHistory[rtk_history_in].azimuth = s->heading;
        RTKHistory[rtk_history_in].coord.X = s->x;
        RTKHistory[rtk_history_in].coord.Y = s->y;
        rtk_history_in = (rtk_history_in + 1) % RTK_HISTORY_SIZE;
        if (rtk_history_num < RTK_HISTORY_SIZE)
            rtk_history_num++;
    }
    pthread_mutex_unlock(&add_rtk_history_mutex);
//    DEBUG("UpdateRTKInfo qf = %d tm %ld", s->qf, mktime(&test_tm) - tz.tz_minuteswest*60);
}
void UpdateCarCoord(void)
{
    double azimuth;
    PointF coord;
    // 计算最近的几个,滑动平均
    int cnt = 0, valid_cnt = 0;
    int s, sm;
    time_t prev_time = 0;
    time_t last_rtk_time = 0;
    pthread_mutex_lock(&add_rtk_history_mutex);
    s = rtk_history_in;
    sm = rtk_history_num;
    pthread_mutex_unlock(&add_rtk_history_mutex);
    DEBUG("UpdateCarCoord rtk_history_in %d rtk_history_num %d car_coord_num %d", s, sm, car_coord_num);
    // 如果出现QF不是固定解、GPS报文丢失的情况,就按上次的行驶状态做直线行驶
    for (; cnt < sm && valid_cnt < MOV_AVG_SIZE; cnt++) {
        if (s == 0) {
            s = RTK_HISTORY_SIZE - 1;
        } else {
            s--;
        }
        if (cnt == 0) {
            last_rtk_time = RTKHistory[s].uptime;
        }
        if (RTKHistory[s].uptime - last_rtk_time <= 10) {
            if (RTKHistory[s].qf == RTK_FIX) {
                if (valid_cnt != 0) {
                    azimuth += RTKHistory[s].azimuth;
                    coord.X += RTKHistory[s].coord.X;
                    coord.Y += RTKHistory[s].coord.Y;
                } else {
                    azimuth = RTKHistory[s].azimuth;
                    coord.X = RTKHistory[s].coord.X;
                    coord.Y = RTKHistory[s].coord.Y;
                }
                valid_cnt++;
            }
        } else {
            break;
        }
        prev_time = RTKHistory[s].uptime;
    }
    if (valid_cnt >= MOV_AVG_SIZE) {
        azimuth /= MOV_AVG_SIZE;
        coord.X /= MOV_AVG_SIZE;
        coord.Y /= MOV_AVG_SIZE;
        CarCoord[car_coord_in].uptime = AppTimer_GetTickCount();
        CarCoord[car_coord_in].azimuth = azimuth;
        CarCoord[car_coord_in].coord = coord;
        car_coord_in = (car_coord_in + 1) % CAR_COORD_STORE_SIZE;
        if (car_coord_num < CAR_COORD_STORE_SIZE)
            car_coord_num++;
    } else if (car_coord_num >= 3) {
        // 按上次运行轨迹推算一步
        int p1 = ((car_coord_in-1)+CAR_COORD_STORE_SIZE)%CAR_COORD_STORE_SIZE;
        uint32_t tm = AppTimer_GetTickCount() -  CarCoord[p1].uptime;
        double distance = currSpeed * tm / 1000;
        PointF xy;
        xy.X = CarCoord[p1].coord.X + distance * sin(toRadians(CarCoord[p1].azimuth)) * currDirect;
        xy.Y = CarCoord[p1].coord.Y + distance * sin(toRadians(CarCoord[p1].azimuth)) * currDirect;
        CarCoord[car_coord_in].uptime = AppTimer_GetTickCount();
        CarCoord[car_coord_in].azimuth =  CarCoord[p1].azimuth;
        CarCoord[car_coord_in].coord = xy;
        car_coord_in = (car_coord_in + 1) % CAR_COORD_STORE_SIZE;
        if (car_coord_num < CAR_COORD_STORE_SIZE)
            car_coord_num++;
    } else {
        return;
    }
    // 计算车辆轮廓点、速度、前进后退
    if (car_coord_num >= 3) {
        int p1 = ((car_coord_in-1)+CAR_COORD_STORE_SIZE)%CAR_COORD_STORE_SIZE;
        int p2 = ((car_coord_in-2)+CAR_COORD_STORE_SIZE)%CAR_COORD_STORE_SIZE;
        int p3 = ((car_coord_in-3)+CAR_COORD_STORE_SIZE)%CAR_COORD_STORE_SIZE;
        double speed1 = sqrt(pow(CarCoord[p1].coord.X - CarCoord[p2].coord.X, 2) + pow(CarCoord[p1].coord.Y - CarCoord[p2].coord.Y, 2)) * 1000 /
                (double)(CarCoord[p1].uptime - CarCoord[p2].uptime);
        double speed2 = sqrt(pow(CarCoord[p2].coord.X - CarCoord[p3].coord.X, 2) + pow(CarCoord[p2].coord.Y - CarCoord[p3].coord.Y, 2)) * 1000 /
                        (double)(CarCoord[p2].uptime - CarCoord[p3].uptime);
        currSpeed = speed1;
        currAzimuth = CarCoord[p1].azimuth;
        currCoord = CarCoord[p1].coord;
        DEBUG("方向 spd1 %f spd2 %f", speed1, speed2);
        if (speed1 < 0.05 && speed2 < 0.05) {
            // 停车
            currDirect = 0;
//            TextSpeak("停");
        } else if (speed1 < 0.05) {
             currDirect = prevDirect;
        } else {
            // 判断前进还是后退
            double deg = 0.0;
            if (fabs(CarCoord[p1].coord.Y-CarCoord[p2].coord.Y) <= GLB_EPSILON) {
                if (CarCoord[p1].coord.X > CarCoord[p2].coord.X) {
                    deg = 90;
                } else {
                    deg = 270;
                }
                DEBUG("方向 deg %f p1x %f p2x %f", deg, CarCoord[p1].coord.X, CarCoord[p2].coord.X);
            } else if (fabs(CarCoord[p1].coord.X - CarCoord[p2].coord.X) <= GLB_EPSILON) {
                if (CarCoord[p1].coord.Y > CarCoord[p2].coord.Y) {
                    deg = 0;
                } else {
                    deg = 180;
                }
            } else {
                deg = atan(fabs(CarCoord[p1].coord.X - CarCoord[p2].coord.X) /
                           fabs(CarCoord[p1].coord.Y - CarCoord[p2].coord.Y));
                deg = toDegree(deg);
                if (CarCoord[p1].coord.X > CarCoord[p2].coord.X &&
                    CarCoord[p1].coord.Y > CarCoord[p2].coord.Y) {
                } else if (CarCoord[p1].coord.X < CarCoord[p2].coord.X &&
                           CarCoord[p1].coord.Y > CarCoord[p2].coord.Y) {
                    deg = 360 - deg;
                } else if (CarCoord[p1].coord.X < CarCoord[p2].coord.X &&
                           CarCoord[p1].coord.Y < CarCoord[p2].coord.Y) {
                    deg = 180 + deg;
                } else if (CarCoord[p1].coord.X > CarCoord[p2].coord.X &&
                           CarCoord[p1].coord.Y < CarCoord[p2].coord.Y) {
                    deg = 180 - deg;
                }
                DEBUG("方向 deg %f p1x %f p2x %f p1y %f p2y %f", deg, CarCoord[p1].coord.X,
                      CarCoord[p2].coord.X, CarCoord[p1].coord.Y, CarCoord[p2].coord.Y);
            }
            deg = fabs(CarCoord[p1].azimuth - deg);
            if (deg > 180) {
                deg = 360 - deg;
            }
            if (deg < 90) {
                // 前进
                currDirect = 1;
//                    TextSpeak("进");
            } else {
                // 后退
                currDirect = -1;
//                    TextSpeak("退");
            }
            prevDirect = currDirect;
        }
        DEBUG("speed = %f, azimuth = %f coord.X = %f coord.Y = %f dir = %d", speed1, CarCoord[p1].azimuth,
              CarCoord[p1].coord.X, CarCoord[p1].coord.Y, currDirect);
//        UpdateCarBodyCoord(CarCoord[p1].azimuth, CarCoord[p1].coord);
        UpdateCarBodyCoord(CarCoord[p1].azimuth, CarCoord[p1].coord, carModelCache, carModelCacheIn, carModelCacheNum);
//        for (int i = 0; i < theCarModel.num; ++i) {
//            DEBUG("n = %d X = %f Y = %f", i, theCarModel.point[i].X, theCarModel.point[i].Y);
//        }
        int c1 = ((carModelCacheIn-1)+CAR_MODEL_CACHE_SIZE)%CAR_MODEL_CACHE_SIZE;
        Polygon py;
        py.num = carModelCache[c1].point_num;
        py.point = carModelCache[c1].points;
        DrawScreen(&theSSMap, &py);
        if (!TestStart) return;
        if (CarInArea == 0) {
//            if (FrontTireEnterArea(&py, &theParkEdgeMap)) {
//                CarInArea = TEST_PARK_BOTTOM;
//                TestItem = TEST_PARK_BOTTOM;
//                StartParkBottom();
//            }
            CarInArea = TEST_SLOPE;
            TestItem = TEST_SLOPE;
            StartSAS();
        }
        switch (TestItem) {
            case TEST_NONE: {
                break;
            }
            case TEST_PARK_BOTTOM: {
                errs = TestParkBottom(ErrorList, &theParkEdgeMap, GetCarModelCache(0), currSpeed, currDirect);
                if (errs != 0) {
                    StopParkBottom();
                    TestItem = TEST_NONE;
                }
                break;
            }
            case TEST_PARK_EDGE: {
                errs = TestParkEdge(ErrorList, &theParkEdgeMap, GetCarModelCache(0), currSpeed, currDirect);
                if (errs != 0) {
                    StopParkEdge();
                    TestItem = TEST_NONE;
                }
                break;
            }
            case TEST_TUNE_90: {
                errs = TestTurnA90(ErrorList, &theTurn90Map, GetCarModelCache(0), currSpeed, currDirect, currAzimuth);
                if (errs != 0) {
                    StopTurnA90();
                    TestItem = TEST_NONE;
                }
                break;
            }
            case TEST_SLOPE: {
                errs = TestSAS(ErrorList, &theSSMap, GetCarModelCache(0), currSpeed, currDirect);
                if (errs != 0) {
                    StopSAS();
                    TestItem = TEST_NONE;
                }
                break;
            }
            default:
                break;
        }
        if (ErrorList.size() > 0) {
            vector<int>::iterator it = ErrorList.end();
            it--;
            error_list_t list = GetErrorList(*it);
            int scr = 0;
            for (vector<int>::iterator it1 = ErrorList.begin(); it1 != ErrorList.end(); ++it1) {
                error_list_t list = GetErrorList(*it1);
                scr += list.dec_score;
            }
            char buff[256];
            sprintf(buff, "%s, 总计扣分 %d", list.text_desc, scr);
            TextOsd(1, buff);
        }
    }
}
car_model_cache_t *GetCarModelCache(int node)
{
    if (node < carModelCacheNum) {
        node = ((carModelCacheIn-node-1)+CAR_MODEL_CACHE_SIZE)%CAR_MODEL_CACHE_SIZE;
        return &carModelCache[node];
    }
    return NULL;
}
/*******************************************************************
 * @brief 由主天线坐标计算车身点坐标
 * @param azimuth
 * @param coord
 */
static void UpdateCarBodyCoord(double azimuth, PointF coord, car_model_cache_t *carModel, int &in, int &num)
{
    carModel[in].uptime = AppTimer_GetTickCount();
    carModel[in].desc = carModelDescFile;
    carModel[in].point_num = 6;
    if (carModel[in].points == NULL) {
        carModel[in].points = (PointF *) malloc(sizeof(PointF) * carModel[in].point_num);
    }
    for (int i = 0; i < 6; ++i) {
        double tx = coord.X + CarDesc[i].distance*sin(toRadians(azimuth));
        double ty = coord.Y + CarDesc[i].distance*cos(toRadians(azimuth));
        carModel[in].points[i].X = (tx-coord.X)*cos(toRadians(CarDesc[i].angle)) -
                                 (ty-coord.Y)*sin(toRadians(CarDesc[i].angle)) + coord.X;
        carModel[in].points[i].Y = (tx-coord.X)*sin(toRadians(CarDesc[i].angle)) +
                                 (ty-coord.Y)*cos(toRadians(CarDesc[i].angle)) + coord.Y;
    }
    in = (in + 1) % CAR_MODEL_CACHE_SIZE;
    if (num < CAR_MODEL_CACHE_SIZE) {
        num++;
    }
}
static void UpdateCarBodyCoord(double azimuth, PointF coord)
{
//    static double az = 0;
//
//    az += 2.0;
//
//    for (int i = 0; i < theCarModel.num; ++i) {
//        double tx = coord.X + CarDesc[i].distance*sin(toRadians(azimuth));
//        double ty = coord.Y + CarDesc[i].distance*cos(toRadians(azimuth));
//
//        theCarModel.point[i].X = (tx-coord.X)*cos(toRadians(CarDesc[i].angle)) -
//                                 (ty-coord.Y)*sin(toRadians(CarDesc[i].angle)) + coord.X;
//        theCarModel.point[i].Y = (tx-coord.X)*sin(toRadians(CarDesc[i].angle)) +
//                                 (ty-coord.Y)*cos(toRadians(CarDesc[i].angle)) + coord.Y;
//    }
//    UpdateParkTest(&theCarModel, &theTireModel, speed);
//    UpdatePark2Test(&theCarModel, &theTireModel, speed);
//    UpdateTuneAngle90Test(&theCarModel, 0);
    //UpdateSlopeTest(&theCarModel, speed, direct);
}
static bool FrontTireEnterArea(const Polygon *car, const Polygon *map)
{
    if (IntersectionOf(car->point[0], map) == GM_Containment) {
        return true;
    }
    return false;
}
lib/src/main/cpp/driver_test.h
New file
@@ -0,0 +1,52 @@
//
// Created by YY on 2019/10/21.
//
#ifndef RTKDRIVERTEST_DRIVER_TEST_H
#define RTKDRIVERTEST_DRIVER_TEST_H
#include "Geometry.h"
struct rtk_info {
    int YY;
    int MM;
    int DD;
    int hh;
    int mm;
    int ss;
    int dss;
    int qf;
    double heading;
    double x;
    double y;
};
#define TIRE_OUTSIDE    0
#define TIRE_INSIDE     1
// 总数
// 中轴线前点,id 0,中轴线后点id
// 左包围, 右包围
// 左前轮,外内
typedef struct {
    int body_num;
    int body[64];
    int front_left_tire[2];
    int front_right_tire[2];
    int rear_left_tire[2];
    int rear_right_tire[2];
} carModelDesc_t;
typedef struct {
    uint32_t uptime;
    int point_num;
    PointF *points;
    carModelDesc_t *desc;
} car_model_cache_t;
void DriverTestInit(void);
void UpdateRTKInfo(struct rtk_info *s);
void UpdateCarCoord(void);
car_model_cache_t *GetCarModelCache(int node);
#endif //RTKDRIVERTEST_DRIVER_TEST_H
lib/src/main/cpp/jni_log.h
New file
@@ -0,0 +1,28 @@
//
// Created by YY on 2017/8/29.
//
#ifndef JNI_LOG_H
#define JNI_LOG_H
#include <android/log.h>
#define DTAG    "JNI_DEBUG"
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,DTAG,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,DTAG,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,DTAG,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,DTAG,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL,DTAG,__VA_ARGS__) // 定义LOGF类型
#define LOGMCUD(...)  __android_log_print(ANDROID_LOG_DEBUG,"MCU_DEBUG",__VA_ARGS__) // 定义LOGD类型
#define ENABLE_DEBUG_PROTOCOL
#ifdef ENABLE_DEBUG_PROTOCOL
#define DEBUG(...)                      LOGD(__VA_ARGS__)
#else
#define DEBUG(...)
#endif
#endif //JNI_LOG_H
lib/src/main/cpp/mcu/mcu_if.cpp
New file
@@ -0,0 +1,306 @@
//
// Created by YY on 2019/12/21.
//
#include <cstring>
#include <pthread.h>
#include "mcu_if.h"
#include "../common/apptimer.h"
#include "../utils/crc16.h"
#include "../defs.h"
#include "../jni_log.h"
#include "../common/serial_port.h"
#define MCU_UART            UART_1
enum parse_status_t {
    SYNC_HEAD_ONE,
    SYNC_HEAD_TWO,
    GET_ID_HI,
    GET_ID_LO,
    GET_LENGTH_HI,
    GET_LENGTH_LO,
    GET_PAYLOAD,
    GET_CRC16_HI,
    GET_CRC16_LO
};
#define ID_CM_APP_BOOT          0x0001
#define ID_MC_MCU_BOOT          0x8001
#define ID_CM_INQ_MCU_VER       0x0002
#define ID_MC_MCU_VER           0x8002
#define ID_CM_RW_INFO           0x0003
#define ID_MC_RW_INFO_RSP       0x8003
#define ID_CM_MCU_DFU_REQ       0x0004
#define ID_MC_MCU_DFU_RSP       0x8004
#define ID_CM_MCU_DFU_DATA       0x0005
#define ID_CM_MCU_DFU_DATA_CMP  0x0006
#define ID_MC_MCU_DFU_DATA_RSP  0x8006
#define ID_MC_CAR_INFO           0x8007
#define ID_MC_RTK_DATA          0x8008
#define ID_CM_RTK_DATA            0x0008
#define ID_CM_READ_RFCARD         0x0009
#define ID_MC_RFCARD_RSP        0x8009
static parse_status_t parse_status;
static struct {
    uint16_t id;
    uint16_t length;
    uint16_t rx_len;
    uint8_t buffer[4096 + 4];
    uint16_t crc16;
}McuPkt;
static void *UartThread1(void *p);
static void ParseMcuTimeout(union sigval sig);
static void McuCommandEntry(uint16_t id, const uint8_t *data, int lenth);
void ParseMcuInit(void)
{
    parse_status = SYNC_HEAD_ONE;
    AppTimer_delete(ParseMcuTimeout);
    SendMcuCommand(ID_CM_APP_BOOT, NULL, 0);
}
#define PARSE_BUFF_SIZE         4096
static void *UartThread1(void *p) {
    struct serial_config *cfg = (struct serial_config *) p;
    int res = InitSerialPort(MCU_UART, cfg->baud, cfg->data_bit, cfg->verify_bit, cfg->stop_bit, cfg->flow_ctrl);
    DEBUG("Serial %s open %d", cfg->name, res);
    uint8_t RxBuf[PARSE_BUFF_SIZE];
    int RxBufLen = 0;
    if (res == 0)
        ParseMcuInit();
    while (res == 0) {
        int ul = ReadSerialPort(MCU_UART, (uint8_t *)RxBuf + RxBufLen, sizeof(RxBuf) - RxBufLen);
        RxBufLen += ul;
        /*{
            static char buffd[16384];
            buffd[0] = 0;
            int i = 0;
            for (i = 0; i < ul; i++) {
                if ((i % 32) == 0) {
                    sprintf(buffd + strlen(buffd), "\n");
                }
                sprintf(buffd + strlen(buffd), "%02X ", RxBuf[i]);
                if (strlen(buffd) > 800) {
                    DEBUG("%s <- %s...", "UART", buffd);
                    buffd[0] = 0;
                }
            }
            if (strlen(buffd) > 0)
                DEBUG("%s <- %s", "UART", buffd);
        }*/
        if (RxBufLen > 0) {
            DEBUG("RECV LEN %d", RxBufLen);
            ParseMcu(RxBuf, RxBufLen);
            RxBufLen = 0;
        }
    }
    if (res == 0) {
        UninitSerialPort(MCU_UART);
    }
    pthread_exit(NULL);
}
void ParseMcu(const uint8_t *data, int length)
{
    int x = 0;
    while (x < length) {
        uint8_t c = data[x];
        switch (parse_status) {
            case SYNC_HEAD_ONE:
                if (c == 0x55) {
                    parse_status = SYNC_HEAD_TWO;
                    AppTimer_delete(ParseMcuTimeout);
                    AppTimer_add(ParseMcuTimeout, D_SEC(5));
                }
                x++;
                break;
            case SYNC_HEAD_TWO:
                if (c == 0xAA) {
                    parse_status = GET_ID_HI;
                } else if (c != 0x55) {
                    parse_status = SYNC_HEAD_ONE;
                }
                x++;
                break;
            case GET_ID_HI:
                McuPkt.id = c;
                parse_status = GET_ID_LO;
                x++;
                break;
            case GET_ID_LO:
                McuPkt.id <<= 8;
                McuPkt.id += c;
                parse_status = GET_LENGTH_HI;
                x++;
                break;
            case GET_LENGTH_HI:
                McuPkt.rx_len = 0;
                McuPkt.length = c;
                parse_status = GET_LENGTH_LO;
                x++;
                break;
            case GET_LENGTH_LO:
                McuPkt.length <<= 8;
                McuPkt.length += c;
                parse_status = GET_PAYLOAD;
                if (McuPkt.length >= 1500) {
                    DEBUG("Pkt Too large!");
                    parse_status = SYNC_HEAD_ONE;
                    AppTimer_delete(ParseMcuTimeout);
                }
                McuPkt.buffer[0] = HI_UINT16(McuPkt.id);
                McuPkt.buffer[1] = LO_UINT16(McuPkt.id);
                McuPkt.buffer[2] = HI_UINT16(McuPkt.length);
                McuPkt.buffer[3] = LO_UINT16(McuPkt.length);
                x++;
                break;
            case GET_PAYLOAD:
                if (length - x >= McuPkt.length - McuPkt.rx_len) {
                    memcpy(McuPkt.buffer + 4 + McuPkt.rx_len, data + x, McuPkt.length - McuPkt.rx_len);
                    x += McuPkt.length - McuPkt.rx_len;
                    McuPkt.rx_len = McuPkt.length;
                    parse_status = GET_CRC16_HI;
                } else {
                    memcpy(McuPkt.buffer + 4 + McuPkt.rx_len, data + x, length - x);
                    McuPkt.rx_len += length - x;
                    x = length;
                }
                break;
            case GET_CRC16_HI:
                McuPkt.crc16 = c;
                parse_status = GET_CRC16_LO;
                x++;
                break;
            case GET_CRC16_LO: {
                McuPkt.crc16 <<= 8;
                McuPkt.crc16 += c;
                uint16_t crc16 = CRCCCITT(McuPkt.buffer, McuPkt.length + 4, 0, 0);
                DEBUG("mcuif crc16 but 0x%04X exp 0x%04X", McuPkt.crc16, crc16);
                if (McuPkt.crc16 == crc16) {
                    McuCommandEntry(McuPkt.id, McuPkt.buffer + 4, McuPkt.length);
                }
                parse_status = SYNC_HEAD_ONE;
                AppTimer_delete(ParseMcuTimeout);
                x++;
                break;
            }
            default:
                break;
        }
    }
}
void SendMcuCommand(uint16_t id, const uint8_t *data, int length)
{
    uint8_t buffer[2048];
    int x = 0;
    buffer[x++] = 0x55;
    buffer[x++] = 0xAA;
    buffer[x++] = HI_UINT16(id);
    buffer[x++] = LO_UINT16(id);
    buffer[x++] = HI_UINT16(length);
    buffer[x++] = LO_UINT16(length);
    if (data != NULL && length > 0) {
        memcpy(buffer + x, data, length);
        x += length;
    }
    uint16_t crc16 =  CRCCCITT(buffer + 2, length + 4, 0, 0);
    buffer[x++] = HI_UINT16(crc16);
    buffer[x++] = LO_UINT16(crc16);
    WriteSerialPort(MCU_UART, buffer, x);
}
void ConfigMCU(void)
{
    // TODO
    static struct serial_config serialConfig;
    strcpy(serialConfig.name, "/dev/ttyHSL1");
    serialConfig.baud = 115200;
    serialConfig.data_bit = 8;
    serialConfig.verify_bit = 'N';
    serialConfig.stop_bit = 1;
    serialConfig.flow_ctrl = 0;
    pthread_t pid;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
    pthread_create(&pid, &attr, UartThread1, &serialConfig);
}
void SendRtkToMcu(const uint8_t *data, int length)
{
    SendMcuCommand(ID_CM_RTK_DATA, data, length);
}
static void ParseMcuTimeout(union sigval sig) {
    AppTimer_delete(ParseMcuTimeout);
    parse_status = SYNC_HEAD_ONE;
}
static void sendrtk(union sigval sig) {
    uint8_t data[486];
    memset(data, 0x86, sizeof(data));
    SendMcuCommand(ID_CM_RTK_DATA, data, sizeof(data));
    SendMcuCommand(ID_CM_READ_RFCARD, NULL, 0);
    AppTimer_delete(sendrtk);
    AppTimer_add(sendrtk, D_SEC(1));
}
static void McuCommandEntry(uint16_t id, const uint8_t *data, int lenth)
{
    static int ii = 0;
    switch (id) {
        case ID_MC_MCU_BOOT:
            DEBUG("MCU BOOT");
            break;
        case ID_MC_MCU_VER:
            DEBUG("MCU VER");
            break;
        case ID_MC_RW_INFO_RSP:
            break;
        case ID_MC_MCU_DFU_RSP:
            break;
        case ID_MC_MCU_DFU_DATA_RSP:
            break;
        case ID_MC_CAR_INFO:
            DEBUG("ID_MC_CAR_INFO");
            break;
        case ID_MC_RTK_DATA:
            DEBUG("ID_MC_RTK_DATA");
            break;
        case ID_MC_RFCARD_RSP:
            DEBUG("ID_MC_RFCARD_RSP");
            break;
        default:
            break;
    }
}
lib/src/main/cpp/mcu/mcu_if.h
New file
@@ -0,0 +1,16 @@
//
// Created by YY on 2019/12/21.
//
#ifndef RTKDRIVERTEST_MCU_IF_H
#define RTKDRIVERTEST_MCU_IF_H
#include <cstdint>
void ConfigMCU(void);
void SendRtkToMcu(const uint8_t *data, int length);
void ParseMcuInit(void);
void ParseMcu(const uint8_t *data, int length);
void SendMcuCommand(uint16_t id, const uint8_t *data, int length);
#endif //RTKDRIVERTEST_MCU_IF_H
lib/src/main/cpp/native-lib.cpp
New file
@@ -0,0 +1,464 @@
#include <jni.h>
#include <string>
#include <pthread.h>
#include <unistd.h>
#include "common/serial_port.h"
#include "jni_log.h"
#include "common/net.h"
#include "common/apptimer.h"
#include "rtk_platform/parse_net.h"
#include "native-lib.h"
#include "Geometry.h"
#include "rtk_platform/platform.h"
#include "rtk_module/rtk.h"
#include "mcu/mcu_if.h"
#include "driver_test.h"
static JavaVM *sg_jvm = NULL;
static jobject sg_obj = NULL;
const char *RTK_PLATFORM_IP = "47.93.80.84";
const int RTK_PLATFORM_PORT = 12125;
const uint8_t phone[] = {0x20,0x19,0x10,0x15,0x00,0x00,0x00,0x02};
#define IMEI_LENGTH         15
char * GetImei(void)
{
    JNIEnv *env;
    static char imei[IMEI_LENGTH + 1] = {0};
    LOGD("JNI_GetImei");
    if (strlen(imei) == 0) {
        bool ready_in_java_env = false;
        if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
            // Attach主线程
            if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
                LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
                return imei;
            }
        } else {
            ready_in_java_env = true;
        }
        jclass cls = env->GetObjectClass(sg_obj);
        jmethodID fun = env->GetMethodID(cls, "javaGetImei", "()Ljava/lang/String;");
        jstring ret = (jstring) env->CallObjectMethod(sg_obj, fun);
        if (ret != NULL) {
            const char *pret = env->GetStringUTFChars(ret, JNI_FALSE);
            strcpy(imei, pret);
        }
        env->DeleteLocalRef(cls);
        if (!ready_in_java_env) {
            //Detach主线程
            if (sg_jvm->DetachCurrentThread() != JNI_OK) {
                LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
                return imei;
            }
        }
    }
    return imei;
}
int DESEncrypt(const uint8_t *key, int key_length,
        const uint8_t *plaintext, int plaintext_length,
        uint8_t **ciphertext)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    int ciphertext_length = 0;
    *ciphertext = NULL;
    LOGD("JNI_DESEncrypt");
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return 0;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "javaDESEncrypt", "([B[B)[B");
    jbyteArray jni_key = env->NewByteArray(key_length);
    jbyteArray jni_plaintext = env->NewByteArray(plaintext_length);
    env->SetByteArrayRegion(jni_key, 0, key_length, (jbyte *) key);
    env->SetByteArrayRegion(jni_plaintext, 0, plaintext_length, (jbyte *) plaintext);
    jbyteArray jni_ciphertext = (jbyteArray) env->CallObjectMethod(sg_obj, fun, jni_plaintext, jni_key);
    if (jni_ciphertext != NULL) {
        ciphertext_length = env->GetArrayLength(jni_ciphertext);
        uint8_t *buffer = (uint8_t *)malloc(ciphertext_length);
        env->GetByteArrayRegion(jni_ciphertext, 0, ciphertext_length, (jbyte *) buffer);
        *ciphertext = buffer;
    }
    env->DeleteLocalRef(jni_key);
    env->DeleteLocalRef(jni_plaintext);
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
    return ciphertext_length;
}
void SetPlatformKey(const uint8_t *key, int length)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    LOGD("JNI_SetPlatformKey");
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return;
        }
    } else {
        ready_in_java_env = true;
    }
    jbyteArray jni_cmd = env->NewByteArray(length);
    env->SetByteArrayRegion(jni_cmd, 0, length, (jbyte *) key);
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "SetPlatformKey", "([B)V");
    env->CallVoidMethod(sg_obj, fun, jni_cmd);
    env->DeleteLocalRef(jni_cmd);
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
}
void DelPlatformKey(void)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "DeletePlatformKey", "()V");
    env->CallVoidMethod(sg_obj, fun);
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
}
int GetPlatformKey(uint8_t *pkey)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    uint8_t key[64] = {0};
    int key_len = 0;
    LOGD("JNI_GetPlatformKey");
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return 0;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "GetPlatformKey", "()[B");
    jbyteArray jni_cmd = (jbyteArray) env->CallObjectMethod(sg_obj, fun);
    if (jni_cmd != NULL) {
        key_len = env->GetArrayLength(jni_cmd);
        if (key_len <= sizeof(key)) {
            env->GetByteArrayRegion(jni_cmd, 0, key_len, (jbyte *) key);
            memcpy(pkey, key, key_len);
        } else {
            key_len = 0;
        }
    }
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
    return key_len;
}
void SetSharedValue(const char *key, int value)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    LOGD("JNI_SetSharedValue");
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "SetSharedValue", "(Ljava/lang/String;I)V");
    env->CallVoidMethod(sg_obj, fun, env->NewStringUTF(key), value);
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
}
int GetSharedValue(const char *key)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    int value = 0;
    LOGD("JNI_GetSharedValue");
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return value;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "GetSharedValue", "(Ljava/lang/String;)I");
    value = env->CallIntMethod(sg_obj, fun, env->NewStringUTF(key));
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
    return value;
}
void TextSpeak(const char *text)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "TextSpeak", "(Ljava/lang/String;)V");
    std::string cstext = text;
    env->CallVoidMethod(sg_obj, fun, env->NewStringUTF(cstext.c_str()));
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
}
void TextOsd(int type, const char *text)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "TextOsd", "(ILjava/lang/String;)V");
    std::string cstext = text;
    env->CallVoidMethod(sg_obj, fun, type, env->NewStringUTF(cstext.c_str()));
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
}
void DrawScreen(const Polygon *map, const Polygon *car)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "DrawMap", "([[D[[D)V");
    jclass doubleArrCls = env->FindClass("[D");
    jobjectArray retmap = env->NewObjectArray(map->num, doubleArrCls, NULL);
    jobjectArray retcar = env->NewObjectArray(car->num, doubleArrCls, NULL);
    for (int i = 0; i < map->num; ++i) {
        jdoubleArray doubleArr = env->NewDoubleArray(2);
        jdouble tmp[2] = {map->point[i].X, 0 - map->point[i].Y};
        env->SetDoubleArrayRegion(doubleArr, 0, 2, tmp);
        env->SetObjectArrayElement(retmap, i, doubleArr);
        env->DeleteLocalRef(doubleArr);
    }
    for (int i = 0; i < car->num; ++i) {
        jdoubleArray doubleArr = env->NewDoubleArray(2);
        jdouble tmp[2] = {car->point[i].X, 0 - car->point[i].Y};
        env->SetDoubleArrayRegion(doubleArr, 0, 2, tmp);
        env->SetObjectArrayElement(retcar, i, doubleArr);
        env->DeleteLocalRef(doubleArr);
    }
    env->CallVoidMethod(sg_obj, fun, retmap, retcar);
    env->DeleteLocalRef(retmap);
    env->DeleteLocalRef(retcar);
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
}
void DisplayText(const char *string)
{
    DEBUG("DisplayText: %s", string);
}
void CCL(int who)
{
    JNIEnv *env;
    bool ready_in_java_env = false;
    static int c[2] = {0};
    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        // Attach主线程
        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
            return;
        }
    } else {
        ready_in_java_env = true;
    }
    jclass cls = env->GetObjectClass(sg_obj);
    jmethodID fun = env->GetMethodID(cls, "CCL", "(II)V");
    env->CallVoidMethod(sg_obj, fun, c[who], who);
    c[who] = !c[who];
    env->DeleteLocalRef(cls);
    if (!ready_in_java_env) {
        //Detach主线程
        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
        }
    }
}
extern "C"
JNIEXPORT void JNICALL
Java_com_anyun_exam_lib_RemoteService_startNative(JNIEnv *env, jobject thiz) {
    // TODO: implement startNative()
    // 保存全局JVM以便在子线程中使用
    env->GetJavaVM(&sg_jvm);
    // 不能直接赋值(g_obj = ojb)
    sg_obj = env->NewGlobalRef(thiz);
    AppTimer_Init();
    ConfigMCU();
    DriverTestInit();
    ConfigRTKModule();
    InitPlatform(phone, RTK_PLATFORM_IP, RTK_PLATFORM_PORT);
}
lib/src/main/cpp/native-lib.h
New file
@@ -0,0 +1,28 @@
//
// Created by YY on 2019/10/14.
//
#ifndef RTKBASESTATION_NATIVE_LIB_H
#define RTKBASESTATION_NATIVE_LIB_H
#include <cstdint>
#include "Geometry.h"
char * GetImei(void);
int DESEncrypt(const uint8_t *key, int key_length,
               const uint8_t *plaintext, int plaintext_length,
               uint8_t **ciphertext);
void SetPlatformKey(const uint8_t *key, int length);
void DelPlatformKey(void);
int GetPlatformKey(uint8_t *pkey);
void SetSharedValue(const char *key, int value);
int GetSharedValue(const char *key);
void TextSpeak(const char *text);
void TextOsd(int type, const char *text);
void DrawScreen(const Polygon *map, const Polygon *car);
void CCL(int who);
void DisplayText(const char *string);
#endif //RTKBASESTATION_NATIVE_LIB_H
lib/src/main/cpp/rtk_module/parse_gps.cpp
New file
@@ -0,0 +1,273 @@
/*********************************************************
YY add
Parse u-blox GPS nmea data
*/
#include "parse_gps.h"
#include "../jni_log.h"
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
typedef const uint8_t *ptr;
static ptr skip1(ptr s, ptr e)
{
    if(s)
        while(s != e && (*s == ' ' || *s == '\t' || *s == ',' || *s == ';'))
            ++s;
    return s;
}
static ptr match2(ptr s, ptr e)
{
    return s && s != e && (*s == '\r' || *s == '\n') ? s+1 : 0;
}
static ptr matchChar(ptr s, ptr e, uint8_t c)
{
    return s && s != e && toupper(*s) == (int) c ? s+1 : 0;
}
static ptr skipQuote(ptr p, ptr e)
{
    return p && p != e && *p == '"' ? p+1 : p;
}
static ptr getString(ptr p, ptr e, struct nmea_sequence *res)
{
    p = skipQuote(p, e);
    if(p)
    {
        ptr s = p;
        while(p != e && (isalnum(*p) || *p==' ' || *p=='+' || *p=='^' || *p=='-' || *p=='_' || *p=='#' || *p=='.' || *p=='/' || *p==':')) ++p;
        if(p != s)
        {
            res->data = s;
            res->length = p - s;
            return skipQuote(p, e);
        }
    }
    res->data = 0;
    res->length = 0;
    return (p != 0 && (*p == ',' || *p == '*')) ? p : 0;
}
static ptr getHexNumber(ptr p, ptr e, ptr start)
{
    if(p)
    {
        ptr s = p;
        uint16_t r = 0;
        uint8_t check = 0;
        while(p != e && isalnum(*p)) {
            if(isdigit(*p)) {
                r = (r << 4) + (*p++ - '0');
            }
            else {
                r = (r << 4) + (toupper(*p++) - 'A' + 10);
            }
        }
        if(p != s) {
            while(start != s-1) {
                check ^= *start++;
            }
            if (check != r) DEBUG("crc error exp %02X but %02X", check, r);
        }
        return ( p != s && check == r )? p : 0;
    }
    return 0;
}
static ptr getHexNumber2(ptr p, ptr e, ptr start)
{
    if(p)
    {
        ptr s = p;
        uint32_t r = 0;
        while(p != e && isalnum(*p)) {
            if(isdigit(*p)) {
                r = (r << 4) + (*p++ - '0');
            }
            else {
                r = (r << 4) + (toupper(*p++) - 'A' + 10);
            }
        }
        if(p != s) {
        }
        return ( p != s )? p : 0;
    }
    return 0;
}
static ptr findEndOfPacket(ptr s, ptr e)
{
    if(s == e) return 0;
    if(*s == '\r' || *s == '\n')
    {
        return s+1;
    }
    else
    {
        /* expecting ... <cr> */
        ptr p = s;
        while(p != e && (*p == ' ' || *p == '\t')) ++p;
        while(p != e && *p != '\r') ++p;
        return p == e ? 0 : p+1;
    }
}
static ptr getNMEA(ptr s, ptr e, struct nmea *res)
{
    res->nmea_num = 0;
    while(s && s != e && *s != '\r' && *s != '\n' && *s != '*') {
        if(s = matchChar(s, e, ',')) {
            struct nmea_sequence dat;
//            LOGD("g = %d", res->nmea_num);
            s = getString(s, e, &dat);
            if(res->nmea_num < MAX_NAEA_NUM) {
                res->nmea_value[res->nmea_num] = dat;
                res->nmea_num += 1;
            }
        }
        else {
            s = 0;
        }
    }
    return s;
}
static ptr getPARAMS(ptr s, ptr e, struct nmea *res)
{
    res->nmea_num = 0;
    while (s && s != e && *s != '\r' && *s != '\n') {
        while(s != e && (*s == ' ' || *s == '\t')) ++s;
        struct nmea_sequence dat;
        s = getString(s, e, &dat);
        if(res->nmea_num < MAX_NAEA_NUM) {
            res->nmea_value[res->nmea_num] = dat;
            res->nmea_num += 1;
        }
        while (s != e && (*s == ',')) ++s;
    }
    return s;
}
static ptr getNMEA2(ptr s, ptr e, struct nmea *res)
{
    bool first = true;
    res->nmea_num = 0;
    while(s && s != e && *s != '\r' && *s != '\n' && *s != '*') {
        if((first && (s=matchChar(s, e, ';'))) || (s=matchChar(s, e, ','))) {
            first = false;
            struct nmea_sequence dat;
//            LOGD("g = %d", res->nmea_num);
            s = getString(s, e, &dat);
            if(res->nmea_num < MAX_NAEA_NUM) {
                res->nmea_value[res->nmea_num] = dat;
                res->nmea_num += 1;
            }
        }
        else {
            s = 0;
        }
    }
    return s;
}
static ptr findSemicolon(ptr s, ptr e)
{
    while (s && s != e && *s != ';') {
        s++;
    }
    return s;
}
ptr parseGPS(ptr s, ptr e)
{
    ptr p;
    for(; (p = findEndOfPacket(s, e)) != 0; s = p)
    {
        if(p == s+1)
        {
            /* Silently discard one character; no packets are that short */
            continue;
        }
        else
        {//GPGGA   GPGSA   GPGSV    GPRMC    GPVTG    GPGLL    GPZDA
            ptr start = s;
            struct nmea nmeas;
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'G'), e, 'P'), e, 'G'), e, 'G'), e, 'A'), e, &nmeas), e, '*'), e, start), e)) {
                handleGPGGA(&nmeas);
                continue;
            }
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'G'), e, 'P'), e, 'G'), e, 'S'), e, 'A'), e, &nmeas), e, '*'), e, start), e)) {
                handleGPGSA(&nmeas);
                continue;
            }
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'G'), e, 'P'), e, 'G'), e, 'S'), e, 'V'), e, &nmeas), e, '*'), e, start), e)) {
                handleGPGSV(&nmeas);
                continue;
            }
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'G'), e, 'P'), e, 'R'), e, 'M'), e, 'C'), e, &nmeas), e, '*'), e, start), e)) {
                handleGPRMC(&nmeas);
                continue;
            }
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'G'), e, 'P'), e, 'V'), e, 'T'), e, 'G'), e, &nmeas), e, '*'), e, start), e)) {
                handleGPVTG(&nmeas);
                continue;
            }
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'G'), e, 'P'), e, 'G'), e, 'L'), e, 'L'), e, &nmeas), e, '*'), e, start), e)) {
                handleGPGLL(&nmeas);
                continue;
            }
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'G'), e, 'P'), e, 'Z'), e, 'D'), e, 'A'), e, &nmeas), e, '*'), e, start), e)) {
                handleGPZDA(&nmeas);
                continue;
            }
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'P'), e, 'T'), e, 'N'), e, 'L'), e, ','), e, 'P'), e, 'J'), e, 'K'), e, &nmeas), e, '*'), e, start), e)) {
                handlePJK(&nmeas);
                continue;
            }
            if(match2(getHexNumber(matchChar(getNMEA(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '$'), e, 'G'), e, 'P'), e, 'T'), e, 'R'), e, 'A'), e, &nmeas), e, '*'), e, start), e)) {
                handleGPTRA(&nmeas);
                continue;
            }
            if (match2(getHexNumber2(matchChar(getNMEA2(findSemicolon(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(start = matchChar(skip1(s, e), e, '#'), e, 'B'), e, 'E'), e, 'S'), e, 'T'), e, 'P'), e, 'O'), e, 'S'), e, 'A'), e), e, &nmeas), e, '*'), e, start), e)) {
                handleBESTPOSA(&nmeas);
                continue;
            }
            if (match2(getPARAMS(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(skip1(s, e), e, 'P'), e, 'J'), e, 'K'), e, ' '), e, 'P'), e, 'A'), e, 'R'), e, 'A'), e, 'M'), e, 'E'), e, 'T'), e, 'E'), e, 'R'), e, ':'), e, &nmeas), e)) {
                handlePJKParam(&nmeas);
                continue;
            }
            if (match2(getPARAMS(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(matchChar(skip1(s, e), e, 'S'), e, 'T'), e, 'A'), e, 'R'), e, 'T'), e, 'T'), e, 'I'), e, 'M'), e, 'E'), e, ':'), e, &nmeas), e)) {
                handleRTKRebootComp(&nmeas);
                continue;
            }
            handleUnrecognisedNMEA(s, p-s);
        }
    }
    return s;
}
lib/src/main/cpp/rtk_module/parse_gps.h
New file
@@ -0,0 +1,39 @@
#ifndef _PARSE_GPS_H_
#define _PARSE_GPS_H_
#include <stdint.h>
#define MAX_NAEA_NUM                32
struct nmea_sequence
{
  const uint8_t *data;
  uint16_t length;
};
struct nmea
{
    uint16_t nmea_num;
    struct nmea_sequence nmea_value[MAX_NAEA_NUM];
};
const uint8_t *parseGPS(const uint8_t *s, const uint8_t *e);
void handleUnrecognisedNMEA(const uint8_t *data, uint16_t length);
void handleGPGGA(const struct nmea *);
void handleGPGSA(const struct nmea *);
void handleGPGSV(const struct nmea *);
void handleGPRMC(const struct nmea *);
void handleGPVTG(const struct nmea *);
void handleGPGLL(const struct nmea *);
void handleGPZDA(const struct nmea *);
void handlePJK(const struct nmea *);
void handleGPTRA(const struct nmea *);
void handleBESTPOSA(const struct nmea *);
void handlePJKParam(const struct nmea *s);
void handleRTKRebootComp(const struct nmea *s);
#endif
lib/src/main/cpp/rtk_module/rtk.cpp
New file
@@ -0,0 +1,421 @@
//
// Created by YY on 2019/12/23.
//
#include <cstring>
#include <cstdio>
#include <pthread.h>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include "rtk.h"
#include "parse_gps.h"
#include "../common/serial_port.h"
#include "../jni_log.h"
#include "../driver_test.h"
#include "../utils/num.h"
#include "../defs.h"
#include "../common/apptimer.h"
#include "../rtk_platform/platform.h"
#include "../native-lib.h"
#define RTK_MODULE_UART         UART_0
#define PARSE_BUFF_SIZE         4096
const static char FACTORY[] = "FRESET\r\n";
const static char REBOOT[] = "RESET\r\n";
const static char INQ_PJK_PARAM[] = "LOG PJKPARA\r\n";
const static char AY_PJKPARAM[] = "set pjkpara 6378137 298.257223563 29.51245330924 106.4553361945 0 0\r\n";
const static char UNLOGALL[] = "unlogall\r\n";
const static char IFCOM2[] = "interfacemode com2 auto auto on\r\n";
const static char SAVECONFIG[] = "saveconfig\r\n";
const static char *PJKITEMS[] = {"gptra", "ptnlpjk"};
const static char *GPSITEMS[] = {"gpgga", "gprmc", "gpvtg"};
static gpsStatus_t gpsStatus;
static int tra_hh, tra_mm, tra_ss, tra_dss;
struct rtk_info CurrRTKInfo;
static time_t recved_gprmc_time = 0, recved_gpgga_time = 0;;
static void CheckPjkParam(void);
static void CheckPjkParamTimeout(union sigval sig);
static void *UartThread(void *p);
void ConfigRTKModule(void)
{
    // TODO
    memset(&CurrRTKInfo, 0, sizeof(CurrRTKInfo));
    tra_hh = tra_mm = tra_ss = tra_dss = 0;
    memset(&gpsStatus, 0, sizeof(gpsStatus));
    static struct serial_config serialConfig;
    strcpy(serialConfig.name, "/dev/ttyHSL0");
    serialConfig.baud = 115200;
    serialConfig.data_bit = 8;
    serialConfig.verify_bit = 'N';
    serialConfig.stop_bit = 1;
    serialConfig.flow_ctrl = 0;
    pthread_t pid;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
    pthread_create(&pid, &attr, UartThread, &serialConfig);
}
void FactorySettings(void)
{
    WriteSerialPort(RTK_MODULE_UART, FACTORY, strlen(FACTORY));
}
void RebootModule(void)
{
    WriteSerialPort(RTK_MODULE_UART, REBOOT, strlen(REBOOT));
}
void handleRTKRebootComp(const struct nmea *s)
{
    DEBUG("RTK Reboot complete!!");
    SetAYFactoryParam(5);
}
void handlePJKParam(const struct nmea *s) {
//PJK Parameter: A:6378137.000, 1/F:298.257223563, B0:0.000000deg, L0:120.000000, N0:0.000, E0:500000.000.
//PJK Parameter: A:6378137.000, 1/F:298.257223563, B0:29.512453deg, L0:106.455336, N0:0.000, E0:0.000.
    bool setparam = true;
    const char DP1[] = "A:6378137.000";
    const char DP2[] = "1/F:298.257223563";
    const char DP3[] = "B0:29.512453deg";
    const char DP4[] = "L0:106.455336";
    const char DP5[] = "N0:0.000";
    const char DP6[] = "E0:0.000";
    AppTimer_delete(CheckPjkParamTimeout);
    for (int i = 0; i < s->nmea_num; ++i) {
        char out[64] = {0};
        memcpy(out, s->nmea_value[i].data, s->nmea_value[i].length);
        DEBUG("handlePJKParam = %s", out);
    }
    if (s->nmea_num != 6) return;
    if (memcmp(s->nmea_value[0].data, DP1, strlen(DP1))) {
        setparam = true;
    }
    if (memcmp(s->nmea_value[1].data, DP2, strlen(DP2))) {
        setparam = true;
    }
    if (memcmp(s->nmea_value[2].data, DP3, strlen(DP3))) {
        setparam = true;
    }
    if (memcmp(s->nmea_value[3].data, DP4, strlen(DP4))) {
        setparam = true;
    }
    if (memcmp(s->nmea_value[4].data, DP5, strlen(DP5))) {
        setparam = true;
    }
    if (memcmp(s->nmea_value[5].data, DP6, strlen(DP6))) {
        setparam = true;
    }
    if (setparam) {
        SetAYFactoryParam(5);
    }
}
void SetAYFactoryParam(int freq)
{
    DisplayText("配置RTK模块");
    WriteSerialPort(RTK_MODULE_UART, UNLOGALL, strlen(UNLOGALL));
    WriteSerialPort(RTK_MODULE_UART, IFCOM2, strlen(IFCOM2));
    if (freq == 0)
        freq = 5;
    for (int i = 0; i < sizeof(PJKITEMS)/ sizeof(PJKITEMS[0]); ++i) {
        char cmd[64];
        sprintf(cmd, "log com1 %s ontime %0.1f\r\n", PJKITEMS[i], 1.0/(double)freq);
        WriteSerialPort(RTK_MODULE_UART, cmd, strlen(cmd));
    }
    for (int i = 0; i <  sizeof(GPSITEMS)/ sizeof(GPSITEMS[0]); ++i) {
        char cmd[64];
        sprintf(cmd, "log com1 %s ontime 1\r\n", GPSITEMS[i]);
        WriteSerialPort(RTK_MODULE_UART, cmd, strlen(cmd));
    }
    WriteSerialPort(RTK_MODULE_UART, AY_PJKPARAM, strlen(AY_PJKPARAM));
//    WriteSerialPort(RTK_MODULE_UART, SAVECONFIG, strlen(SAVECONFIG));
}
void GetGpsStatus(gpsStatus_t &data)
{
    data = gpsStatus;
}
static void *UartThread(void *p) {
    struct serial_config *cfg = (struct serial_config *) p;
    int res = InitSerialPort(RTK_MODULE_UART, cfg->baud, cfg->data_bit, cfg->verify_bit, cfg->stop_bit, cfg->flow_ctrl);
    DEBUG("Serial %s open %d", cfg->name, res);
    uint8_t RxBuf[PARSE_BUFF_SIZE];
    int RxBufLen = 0;
    if (res == 0) {
        CheckPjkParam();
    }
    while (res == 0) {
        int ul = ReadSerialPort(RTK_MODULE_UART, (uint8_t *)RxBuf + RxBufLen, sizeof(RxBuf) - RxBufLen);
        RxBufLen += ul;
        {
            static char buffd[16384];
            buffd[0] = 0;
            int i = 0;
            for (i = 0; i < ul; i++) {
                if ((i % 32) == 0) {
                    sprintf(buffd + strlen(buffd), "\n");
                }
                sprintf(buffd + strlen(buffd), "%02X ", RxBuf[i]);
                if (strlen(buffd) > 800) {
                    DEBUG("%s <- %s...", "UART", buffd);
                    buffd[0] = 0;
                }
            }
            if (strlen(buffd) > 0)
                DEBUG("%s <- %s", "UART", buffd);
        }
        if (RxBufLen > 0) {
            const uint8_t *ptr = parseGPS(RxBuf, RxBuf + RxBufLen);
            if(ptr != RxBuf) {
                memcpy(RxBuf, ptr, RxBufLen - (ptr - RxBuf));
                RxBufLen -= ptr - RxBuf;
            } else if(RxBufLen == PARSE_BUFF_SIZE) {        //填满了,且没有一个\r,都抛弃
                DEBUG("Parse GPS error");
                RxBufLen = 0;
            }
        }
    }
    if (res == 0) {
        UninitSerialPort(RTK_MODULE_UART);
    }
    pthread_exit(NULL);
}
void handleUnrecognisedNMEA(const uint8_t *data, uint16_t length) {
//    char buff[4096] = {0};
//    memcpy(buff, data, MIN(length, 3000));
//    DEBUG("handleUnrecognisedNMEA: %s", buff);
}
void handleGPGGA(const struct nmea *s)
{
    DEBUG("handleGPGGA num = %d", s->nmea_num);
    if (s->nmea_num >= 10) {
        double lat1, lat2, lon1, lon2, alt;
        int qf;
        qf = str2int(s->nmea_value[5].data, s->nmea_value[5].length);
        if (qf > 0) {
            str2float(&lat1, s->nmea_value[1].data, 2);
            str2float(&lat2, s->nmea_value[1].data+2, s->nmea_value[1].length-2);
            str2float(&lon1, s->nmea_value[3].data, 3);
            str2float(&lon2, s->nmea_value[3].data+3, s->nmea_value[3].length-3);
            lat1 = lat1 + lat2/60.0;
            lon1 = lon1 + lon2/60.0;
            str2float(&alt, s->nmea_value[8].data, s->nmea_value[8].length);
            gpsStatus.gps_status = 1;
            gpsStatus.latitude = (uint32_t)(lat1 * 1000000);
            gpsStatus.longitude = (uint32_t)(lon1 * 1000000);
//            char buff1[32] = {0};
//            char buff2[32] = {0};
//            char buff3[32] = {0};
//
//            memcpy(buff1, s->nmea_value[1].data, s->nmea_value[1].length);
//            memcpy(buff2, s->nmea_value[3].data, s->nmea_value[3].length);
//            memcpy(buff3, s->nmea_value[8].data, s->nmea_value[8].length);
//
//            DEBUG("%s %s %s: lat = %ld  lon = %ld alt = %f", buff1, buff2, buff3,  gpsStatus.latitude,  gpsStatus.longitude, alt);
            gpsStatus.altitude = (int) fabs(alt);
            recved_gpgga_time = AppTimer_GetTickCount();
            if (abs(recved_gpgga_time - recved_gprmc_time) < 500) {
                RequestRtkDownload(gpsStatus.latitude, gpsStatus.longitude, gpsStatus.altitude,
                                   gpsStatus.bcd_time, 1);
            }
        } else {
            gpsStatus.gps_status = 0;
        }
    }
}
void handleGPGSA(const struct nmea *s) {
}
void handleGPGSV(const struct nmea *s) {
}
void handleGPRMC(const struct nmea *s)
{
    DEBUG("handleGPRMC num = %d", s->nmea_num);
    if (s->nmea_num >= 9) {
        int hh = str2int(s->nmea_value[0].data, 2);
        int mm = str2int(s->nmea_value[0].data + 2, 2);
        int ss = str2int(s->nmea_value[0].data + 4, 2);
        int DD = str2int(s->nmea_value[8].data, 2);
        int MM = str2int(s->nmea_value[8].data + 2, 2);
        int YY = str2int(s->nmea_value[8].data + 4, 2);
        gpsStatus.bcd_time[0] = ((YY/10)<<4) + (YY%10);
        gpsStatus.bcd_time[1] = ((MM/10)<<4) + (MM%10);
        gpsStatus.bcd_time[2] = ((DD/10)<<4) + (DD%10);
        gpsStatus.bcd_time[3] = ((hh/10)<<4) + (hh%10);
        gpsStatus.bcd_time[4] = ((ss/10)<<4) + (ss%10);
        gpsStatus.bcd_time[5] = ((mm/10)<<4) + (mm%10);
        recved_gprmc_time = AppTimer_GetTickCount();
        if (abs(recved_gpgga_time - recved_gprmc_time) < 500) {
            RequestRtkDownload(gpsStatus.latitude, gpsStatus.longitude, gpsStatus.altitude,
                    gpsStatus.bcd_time, 1);
        }
    }
}
const char *GPS_SOL_OK = "SOL_COMPUTED";
const char *GPS_SOL_OBS = "INSUFFICIENT_OBS";
const char *GPS_SOL_CS = "COLD_START";
const char *GPS_POS_NONE = "NONE";
const char *GPS_POS_FIX = "FIXEDPOS";
const char *GPS_POS_SIG = "SINGLE";
const char *GPS_POS_PDIFF = "PSRDIFF";
const char *GPS_POS_NARF = "NARROW_FLOAT";
const char *GPS_POS_WIDI = "WIDE_INT";
const char *GPS_POS_NARI = "NARROW_INT";
const char *GPS_POS_SWIDL = "SUPER WIDE-LANE";
void handleBESTPOSA(const struct nmea *s) {
    DEBUG("handleBESTPOSA num = %d", s->nmea_num);
    double lat, lon;
    if (memcmp(s->nmea_value[0].data, GPS_SOL_OK, s->nmea_value[0].length)) {
        str2float(&lat, s->nmea_value[2].data, s->nmea_value[2].length);
        str2float(&lon, s->nmea_value[3].data, s->nmea_value[3].length);
    }
}
void handleGPVTG(const struct nmea *s) {
}
void handleGPGLL(const struct nmea *s) {
}
void handleGPZDA(const struct nmea *s) {
}
void handlePJK(const struct nmea *s) {
    DEBUG("handlePJK num = %d", s->nmea_num);
    CurrRTKInfo.hh = str2int(s->nmea_value[0].data, 2);
    CurrRTKInfo.mm = str2int(s->nmea_value[0].data + 2, 2);
    CurrRTKInfo.ss = str2int(s->nmea_value[0].data + 4, 2);
    CurrRTKInfo.dss = str2int(s->nmea_value[0].data + 7, 2);
    CurrRTKInfo.MM = str2int(s->nmea_value[1].data, 2);
    CurrRTKInfo.DD = str2int(s->nmea_value[1].data + 2, 2);
    CurrRTKInfo.YY = str2int(s->nmea_value[1].data + 4, 2);
    CurrRTKInfo.qf = str2int(s->nmea_value[6].data, s->nmea_value[6].length);
    // NOTE: RTK模块是以南北向为X轴,西东向为Y轴,我们交换下,以符合一般逻辑
    str2float(&CurrRTKInfo.y, s->nmea_value[2].data, s->nmea_value[2].length);
    str2float(&CurrRTKInfo.x, s->nmea_value[4].data, s->nmea_value[4].length);
//    const double by1 = 28.013;
//    const double bx1 = -11.9669;
//
//    const double by2 = 29.3232;
//    const double bx2 = -9.5057;
//
//    static double xx = -10.9669, yy = 28.013;
//
//    CurrRTKInfo.y = yy;
//    CurrRTKInfo.x = xx;
//
//    if (forwardx) {
//        xx += 0.02;
//        yy += 0.02 * (by2 - by1) / (bx2 - bx1);
//    } else {
//        xx -= 0.02;
//        yy -= 0.02 * (by2 - by1) / (bx2 - bx1);
//    }
    if (CurrRTKInfo.hh == tra_hh && CurrRTKInfo.mm == tra_mm && CurrRTKInfo.ss == tra_ss && CurrRTKInfo.dss == tra_dss) {
        UpdateRTKInfo(&CurrRTKInfo);
//        up_num++;
        /*if ((up_num % 5) == 0)*/ {
//            NewMgrEvent(DRIVER_UPDATE_EVT);
        }
    }
}
void handleGPTRA(const struct nmea *s) {
    DEBUG("handleGPTRA num = %d", s->nmea_num);
    tra_hh = str2int(s->nmea_value[0].data, 2);
    tra_mm = str2int(s->nmea_value[0].data + 2, 2);
    tra_ss = str2int(s->nmea_value[0].data + 4, 2);
    tra_dss = str2int(s->nmea_value[0].data + 7, 2);
    str2float(&CurrRTKInfo.heading, s->nmea_value[1].data, s->nmea_value[1].length);
//    CurrRTKInfo.heading = 60;
    if (CurrRTKInfo.hh == tra_hh && CurrRTKInfo.mm == tra_mm && CurrRTKInfo.ss == tra_ss && CurrRTKInfo.dss == tra_dss) {
        UpdateRTKInfo(&CurrRTKInfo);
//        up_num++;
        /*if ((up_num % 5) == 0)*/ {
//            NewMgrEvent(DRIVER_UPDATE_EVT);
        }
    }
}
static void CheckPjkParam(void)
{
    WriteSerialPort(RTK_MODULE_UART, INQ_PJK_PARAM, strlen(INQ_PJK_PARAM));
    AppTimer_delete(CheckPjkParamTimeout);
    AppTimer_add(CheckPjkParamTimeout, D_SEC(3));
}
static void CheckPjkParamTimeout(union sigval sig) {
    AppTimer_delete(CheckPjkParamTimeout);
    DEBUG("RTK Module failure!!");
}
lib/src/main/cpp/rtk_module/rtk.h
New file
@@ -0,0 +1,22 @@
//
// Created by YY on 2019/12/23.
//
#ifndef RTKDRIVERTEST_RTK_H
#define RTKDRIVERTEST_RTK_H
typedef struct {
    uint16_t gps_status;
    uint32_t latitude;
    uint32_t longitude;
    uint16_t altitude;
    uint8_t bcd_time[6];
}gpsStatus_t;
void ConfigRTKModule(void);
void FactorySettings(void);
void RebootModule(void);
void SetAYFactoryParam(int freq);
void GetGpsStatus(gpsStatus_t &data);
#endif //RTKDRIVERTEST_RTK_H
lib/src/main/cpp/rtk_platform/parse_net.cpp
New file
@@ -0,0 +1,994 @@
//
// Created by YY on 2019/10/8.
//
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <malloc.h>
#include <pthread.h>
#include <semaphore.h>
#include "../jni_log.h"
#include "../defs.h"
#include "../native-lib.h"
#include "parse_net.h"
#include "../common/apptimer.h"
#include "../common/serial_port.h"
#include "../common/net.h"
#include "platform.h"
#include "../mcu/mcu_if.h"
using namespace std;
#define PKT_HEAD_CHAR           0x7E
#define MAX_CONTENT_SIZE        1100
#define MAX_MESSAGE_DATA        1023
#define PKT_SYNC_HEAD           0x1
#define PKT_7D                   0x02
#define DEVICE_SERIAL_NUM_SIZE  16
#define PHONE_NUM_SIZE          8
#define DEFAULT_MAX_RESEND_CNT          3
#define DEFAULT_SHORT_RESEND_INTERVAL           D_SEC(1)
#define DEFAULT_LONG_RESEND_INTERVAL           D_SEC(2)
typedef struct {
    uint16_t length:10;
    uint16_t encrypt:3;
    uint16_t is_multi_msg:1;
} message_attrib_t;
typedef struct {
    uint8_t version;
    uint16_t id;
    union {
        message_attrib_t a;
        uint16_t b;
    } attrib;
    uint8_t phone_num[PHONE_NUM_SIZE];
    uint16_t seq;
    uint8_t reserve;
    uint16_t multi_msg_num;
    uint16_t multi_msg_seq;
    uint8_t resend;             //消息重传次数,如果消息无需远端应答,就设置为0,否则就应为大于0的值
    uint32_t resend_interval;
} message_t;
typedef struct {
    uint16_t type : 1;
    uint16_t need_rsp : 1;
    uint16_t reserve : 2;
    uint16_t encrypt : 4;
}ex_message_attrib_t;
#define EXTERNAL_ENCRYPT_SIZE           256
typedef struct {
    uint8_t type;
    uint16_t id;
    union {
        ex_message_attrib_t a;
        uint16_t b;
    }attrib;
    uint16_t seq;
    uint8_t sn[16];
    uint32_t length;
    uint8_t encrypt[EXTERNAL_ENCRYPT_SIZE];             // 单片机和平台的扩展消息都要处理,故按较长的算
} ex_message_t;
typedef struct message_tx_table_ {
    uint8_t access;
    uint16_t id;
    uint16_t seq;
    uint8_t curr_cnt;
    uint8_t max_try_cnt;
    uint32_t resend_interval;
    uint32_t time_out;
    uint32_t length;
    struct message_tx_table_ *prev;
    struct message_tx_table_ *next;
    uint8_t data[0];
} message_tx_table_t;
#define ID_CP_COMMON_RSP        0x0001
#define ID_PC_COMMON_RSP      0x8001
#define ID_CP_HEARTBEAT             0x0002
#define ID_CP_DEVICE_REG            0x0100
#define ID_PC_DEVICE_REG_RSP    0x8100
#define ID_CP_LOGIN_REQ         0x0101
#define ID_CP_RTK_UPLOAD        0x0301
#define ID_CP_RTK_START_REQ     0x0401
#define ID_PC_RTK_DOWNLOAD      0x8401
#define ID_CP_RTK_END_REQ       0x0402
const uint8_t PKT_RESEVER = 0x3C;
static message_tx_table_t * message_tx_table_head[DATA_ACCESS_END];
static uint8_t packet_parse_status[DATA_ACCESS_END];
static uint8_t packet_buffer[DATA_ACCESS_END][MAX_CONTENT_SIZE];
static uint16_t packet_buffer_length[DATA_ACCESS_END];
struct largeMessage_t{
    uint16_t currSeq;
    uint32_t length;
    uint8_t *data;
}largeMessage[DATA_ACCESS_END];
static uint8_t PhoneNumber[PHONE_NUM_SIZE] = {0};
static pthread_mutex_t seq_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t tx_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
static void ParseTimeout(union sigval sig);
static uint32_t GetResendTimeout(uint8_t access, uint8_t curr_resend_cnt, uint32_t base_time);
static uint16_t GetMessageSeq(uint8_t access);
static void PacketEntry(uint8_t access, const uint8_t *data, uint16_t length);
static void MessageEntry(uint8_t access, const message_t *msg, const uint8_t *data, uint32_t length);
static void MakeMessage(message_t *msg, uint8_t version, uint16_t id, uint8_t encrypt, const uint8_t *phone_number, uint8_t reserve, uint8_t resend, uint32_t resend_intval);
static void SendMessage(uint8_t access, const message_t *srcMsg, const uint8_t *data, uint32_t length);
static void SendPacket(uint8_t access, const message_t *msg, const uint8_t *data, uint16_t id);
static message_tx_table_t *FindTxQueue(message_tx_table_t **head, uint16_t id, uint16_t seq);
static message_tx_table_t *FindTxQueue(message_tx_table_t **head, uint32_t tm);
static message_tx_table_t *FindTxQueue(message_tx_table_t **head, message_tx_table_t *item);
static void AddTxQueue(message_tx_table_t **head, uint8_t access, uint16_t id, uint16_t seq, const uint8_t *data, uint32_t length, uint8_t resend, uint32_t resend_intval);
static void RemoveTxQueue(message_tx_table_t **head, uint16_t id, uint16_t seq);
static void RemoveTxQueue(message_tx_table_t **head, uint16_t id);
static void RemoveAllTxQueue(message_tx_table_t **head);
static int SendQueue(message_tx_table_t *item);
static void ResendItemTimeout(message_tx_table_t **head, uint32_t tm);
static uint32_t GetResentTimeoutTxQueue(message_tx_table_t **head);
static void *TxQueueMgrThread(void *p);
static void TriggerResendTxQueue(union sigval sig);
/*
7E 80 89 00 40 87 00 00 01 37 20 20 55 68 00 76 00 9A 41 11 00 22 00 77 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 2C 00 00 02 E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 04 38 36 39 30 37 34 30 33 30 39 34 38 37 30 32 F2 0F 4B 93 32 B6 CE 5C
55 98 64 20 D1 7A C6 B7 DB E4 93 15 C9 59 5C F4 54 63 11 5C 81 EE F8 35 52 12 64 A5 51 40 B8 E5
D7 4D B8 EF 5F C2 2F D4 E5 EA 20 41 E5 C9 F6 BA 64 04 5B 5B 1D F3 9A 09 9D 7E
 */
void Parse(uint8_t access, const uint8_t *buff, uint16_t length) {
    /*{
        static char buffd[16384];
        buffd[0] = 0;
        DEBUG("Parse access = %s Length = %d", access ? "TCP" : "UART", length);
        int i = 0;
        for (i = 0; i < length; i++) {
            if ((i % 32) == 0) {
                sprintf(buffd + strlen(buffd), "\n");
            }
            sprintf(buffd + strlen(buffd), "%02X ", buff[i]);
            if (strlen(buffd) > 800) {
                DEBUG("%s <- %s...", access ? "TCP" : "UART" , buffd);
                buffd[0] = 0;
            }
        }
        if (strlen(buffd) > 0)
            DEBUG("%s <- %s", access ? "TCP" : "UART" , buffd);
    }*/
    for (uint16_t i = 0; i < length; i++) {
        unsigned char dat = buff[i];
        if (packet_parse_status[access] & PKT_SYNC_HEAD) {
            if (dat == PKT_HEAD_CHAR) {
                packet_parse_status[access] &= ~PKT_SYNC_HEAD;
                packet_parse_status[access] &= ~PKT_7D;
                packet_buffer_length[access] = 0;
                AppTimer_add(ParseTimeout, D_SEC(5), access);
            }
        } else {
            if (dat == PKT_HEAD_CHAR) {
                if (packet_buffer_length[access] == 0) {
                    /* 出现0长度载荷的情况,往往是单片机过来的数据[7E..]..7E,7E.....7E的括号内丢失了,和后一个7E头结合在一起*/
                    packet_parse_status[access] &= ~PKT_7D;
                    AppTimer_add(ParseTimeout, D_SEC(5), access);
                } else {
                    AppTimer_delete(ParseTimeout);
                    packet_parse_status[access] = PKT_SYNC_HEAD;
                    PacketEntry(access, packet_buffer[access], packet_buffer_length[access]);
                }
                continue;
            } else if (dat == 0x7D && !(packet_parse_status[access] & PKT_7D)) {
                packet_parse_status[access] |= PKT_7D;
                continue;
            }
            if (packet_parse_status[access] & PKT_7D) {
                packet_parse_status[access] &= ~PKT_7D;
                dat = 0x7D + dat - 1;
            }
            if (packet_buffer_length[access] < MAX_CONTENT_SIZE) {
                packet_buffer[access][ packet_buffer_length[access] ] = dat;
                packet_buffer_length[access]++;
            } else {                                // Too much, discard
                DEBUG("Parse error: pkt too large!");
                AppTimer_delete(ParseTimeout);
                packet_parse_status[access] = PKT_SYNC_HEAD;
            }
        }
    }
}
static void ParseTimeout(union sigval sig) {
    AppTimer_delete(ParseTimeout);
    packet_parse_status[sig.sival_int] = PKT_SYNC_HEAD;
    DEBUG("ParseTimeout %d", sig.sival_int);
}
static uint32_t GetResendTimeout(uint8_t access, uint8_t curr_resend_cnt, uint32_t base_time)
{
    if (access == DATA_ACCESS_MCU) {
        return base_time;
    }
    //T2 = T1 * (n + 1), 首次时间认为是1秒
    uint32_t t = base_time;
    for (int i = 0; i <= curr_resend_cnt; i++) {
        t = t * (i + 1);
    }
    return t;
}
static uint16_t GetMessageSeq(uint8_t access)
{
    static uint16_t TxSeq[DATA_ACCESS_END] = {0};
    uint16_t seq;
    pthread_mutex_lock(&seq_mutex);
    seq = TxSeq[access];
    TxSeq[access]++;
    pthread_mutex_unlock(&seq_mutex);
    return seq;
}
static void PacketEntry(uint8_t access, const uint8_t *data, uint16_t length) {
    uint16_t i, x = 0;
    uint8_t checkByte = 0;
    message_t theMessage;
    for (i = 0; i < length; i++) {
        checkByte ^= data[i];
    }
    if (checkByte != 0 || length <= 16) {
        DEBUG("recv pkt error, checkByte: 0x%02X, length: %d", checkByte, length);
        return;
    }
    length--;               // Ignore check byte
    theMessage.version = data[x++];
    theMessage.id = BUILD_UINT16(data[x + 1], data[x]);
    x += 2;
    theMessage.attrib.b = BUILD_UINT16(data[x + 1], data[x]);
    x += 2;
    memcpy(theMessage.phone_num, data + x, PHONE_NUM_SIZE);
    x += PHONE_NUM_SIZE;
    theMessage.seq = BUILD_UINT16(data[x + 1], data[x]);
    x += 2;
    theMessage.reserve = data[x++];
    theMessage.resend = DEFAULT_MAX_RESEND_CNT;
    if (theMessage.attrib.a.is_multi_msg) {
        if (x + 4 <= length) {
            theMessage.multi_msg_num = BUILD_UINT16(data[x + 1], data[x]);
            theMessage.multi_msg_seq = BUILD_UINT16(data[x + 3], data[x + 2]);
            x += 4;
            if (theMessage.multi_msg_seq == 1) {
                // First multi-msg
                largeMessage[access].currSeq = 1;
                largeMessage[access].length = 0;
                if (largeMessage[access].data != NULL) {
                    largeMessage[access].data = (uint8_t *) realloc(largeMessage[access].data,
                                                                    theMessage.multi_msg_num * MAX_MESSAGE_DATA);
                } else {
                    largeMessage[access].data = (uint8_t *) malloc(
                            theMessage.multi_msg_num * MAX_MESSAGE_DATA);
                }
                memcpy(largeMessage[access].data, data + x, theMessage.attrib.a.length);
                largeMessage[access].length = theMessage.attrib.a.length;
                CommonRespend(access, theMessage.seq, theMessage.id, COMMON_RESP_SUCCESS);
            } else if (largeMessage[access].data != NULL &&
                       theMessage.multi_msg_seq != largeMessage[access].currSeq + 1) {
                //请求重传分包?
                CommonRespend(access, theMessage.seq, theMessage.id, COMMON_RESP_FAIL);
                return;
            } else if (largeMessage[access].data != NULL){
                largeMessage[access].currSeq = theMessage.multi_msg_seq;
                memcpy(largeMessage[access].data + largeMessage[access].length, data + x, theMessage.attrib.a.length);
                largeMessage[access].length += theMessage.attrib.a.length;
                CommonRespend(access, theMessage.seq, theMessage.id, COMMON_RESP_SUCCESS);
                if (theMessage.multi_msg_seq == theMessage.multi_msg_num) {
                    DEBUG("多包接收完毕 %d %d\n", theMessage.multi_msg_seq, largeMessage[access].length);
                    MessageEntry(access, &theMessage, largeMessage[access].data, largeMessage[access].length);
                    free(largeMessage[access].data);
                    largeMessage[access].data = NULL;
                }
            }
        } else {
            // Length error
            return;
        }
    } else {
        MessageEntry(access, &theMessage, data + x, theMessage.attrib.a.length);
    }
}
static void MessageEntry(uint8_t access, const message_t *msg, const uint8_t *data, uint32_t length)
{
    DEBUG("MessageEntry[%d] id = 0x%04X, seq = %d, length = %d", access, msg->id, msg->seq, length);
    switch (msg->id) {
        case ID_PC_COMMON_RSP:
            if (length == 5) {
                uint16_t seq = BUILD_UINT16(data[1], data[0]);
                uint16_t id = BUILD_UINT16(data[3], data[2]);
                uint8_t res = data[4];
                DEBUG("ID_PC_COMMON_RSP seq = %d, id = 0x%04X res = %d", seq, id, res);
                RemoveTxQueue(&message_tx_table_head[DATA_ACCESS_PLATFORM], id);
                if (id == ID_CP_LOGIN_REQ) {
                    DeviceLoginCallback(res);
                }
            }
            break;
        case ID_PC_DEVICE_REG_RSP: {
            DEBUG("ID_PC_DEVICE_REG_RSP");
            RemoveTxQueue(&message_tx_table_head[DATA_ACCESS_PLATFORM], ID_CP_DEVICE_REG);
            if (length >= 3) {
                if (data[2] == 0 && length == 11) {
                    DeviceRegisterCallback(0, data + 3, 8);
                } else {
                    DeviceRegisterCallback(data[2], 0, 0);
                }
            }
            break;
        }
        case ID_PC_RTK_DOWNLOAD:
            DEBUG("ID_PC_RTK_DOWNLOAD");
            // 汇报给单片机
            if (length > 0) {
                SendRtkToMcu(data, length);
            }
            break;
        default:
            break;
    }
    CommonRespend(access, msg->seq, msg->id, COMMON_RESP_FAIL);
}
static void MakeMessage(message_t *msg, uint8_t version, uint16_t id, uint8_t encrypt, const uint8_t *phone_number, uint8_t reserve, uint8_t resend, uint32_t resend_intval)
{
    msg->version = version;
    msg->id = id;
    msg->attrib.b = 0;
    msg->attrib.a.encrypt = encrypt;
    memcpy(msg->phone_num, phone_number, PHONE_NUM_SIZE);
    msg->reserve = reserve;
    msg->resend = resend;
    msg->resend_interval = resend_intval;
}
static void SendMessage(uint8_t access, const message_t *srcMsg, const uint8_t *data, uint32_t length)
{
    uint16_t total_num = (length + MAX_MESSAGE_DATA - 1)  / MAX_MESSAGE_DATA;
    uint32_t x = 0;
    message_t msg = *srcMsg;
    if (length == 0) {
        total_num = 1;
    }
    msg.attrib.a.is_multi_msg = (total_num > 1 ? 1 : 0);
    DEBUG("length = %d, total_num %d\n", length, total_num);
    for (uint16_t curr_num = 1; curr_num <= total_num; curr_num++) {
        uint32_t load = 0;
        DEBUG("curr_num %d\n", curr_num);
        msg.seq = GetMessageSeq(access);
        if (msg.attrib.a.is_multi_msg) {
            msg.multi_msg_num = total_num;
            msg.multi_msg_seq = curr_num;
        }
        load = (length - x > MAX_MESSAGE_DATA) ? MAX_MESSAGE_DATA : (length - x);
        msg.attrib.a.length = load;
        DEBUG("load %d\n", load);
        SendPacket(access, &msg, data + x, msg.id);
        x += load;
    }
}
static void SendPacket(uint8_t access, const message_t *msg, const uint8_t *data, uint16_t id) {
    uint8_t buffer[MAX_CONTENT_SIZE];
    uint8_t buffer2[MAX_CONTENT_SIZE*2];
    uint16_t x = 0;
    DEBUG("SendPacket len = %d", msg->attrib.a.length);
    buffer[x++] = msg->version;
    buffer[x++] = HI_UINT16(msg->id);
    buffer[x++] = LO_UINT16(msg->id);
    buffer[x++] = HI_UINT16(msg->attrib.b);
    buffer[x++] = LO_UINT16(msg->attrib.b);
    memcpy(buffer + x, msg->phone_num, PHONE_NUM_SIZE);
    x += PHONE_NUM_SIZE;
    buffer[x++] = HI_UINT16(msg->seq);
    buffer[x++] = LO_UINT16(msg->seq);
    buffer[x++] = msg->reserve;
    if (msg->attrib.a.is_multi_msg) {
        buffer[x++] = HI_UINT16(msg->multi_msg_num);
        buffer[x++] = LO_UINT16(msg->multi_msg_num);
        buffer[x++] = HI_UINT16(msg->multi_msg_seq);
        buffer[x++] = LO_UINT16(msg->multi_msg_seq);
    }
    memcpy(buffer + x, data, msg->attrib.a.length);
    x += msg->attrib.a.length;
    uint8_t checkByte = 0;
    for (int i = 0; i < x; i++) {
        checkByte ^= buffer[i];
    }
    buffer[x++] = checkByte;
    uint32_t y = 0;
    buffer2[y++] = PKT_HEAD_CHAR;
    for (int i = 0; i < x; i++) {
        if (buffer[i] == 0x7E) {
            buffer2[y++] = 0x7D;
            buffer2[y++] = 0x02;
        } else if (buffer[i] == 0x7D) {
            buffer2[y++] = 0x7D;
            buffer2[y++] = 0x01;
        } else {
            buffer2[y++] = buffer[i];
        }
    }
    buffer2[y++] = PKT_HEAD_CHAR;
    AddTxQueue(&message_tx_table_head[access], access, id, msg->seq, buffer2, y, msg->resend, msg->resend_interval);
}
static message_tx_table_t *FindTxQueue(message_tx_table_t **head, uint16_t id, uint16_t seq)
{
    if (head == NULL) {
        return NULL;
    }
    for (message_tx_table_t *ptr = *head; ptr != NULL; ptr = ptr->next) {
        if (ptr->id == id && ptr->seq == seq) {
            return ptr;
        }
    }
    return NULL;
}
static message_tx_table_t *FindTxQueue(message_tx_table_t **head, uint32_t tm)
{
    if (head == NULL) {
        return NULL;
    }
    for (message_tx_table_t *ptr = *head; ptr != NULL; ptr = ptr->next) {
        if (ptr->time_out <= tm) {
            return ptr;
        }
    }
    return NULL;
}
static message_tx_table_t *FindTxQueue(message_tx_table_t **head, message_tx_table_t *item)
{
    if (head == NULL) {
        return NULL;
    }
    for (message_tx_table_t *ptr = *head; ptr != NULL; ptr = ptr->next) {
        if (ptr == item) {
            return ptr;
        }
    }
    return NULL;
}
static void AddTxQueue(message_tx_table_t **head, uint8_t access, uint16_t id, uint16_t seq, const uint8_t *data, uint32_t length, uint8_t resend, uint32_t resend_intval)
{
    message_tx_table_t *new_item;
    if (length == 0 || head == NULL) {
        return;
    }
    DEBUG("AddTxQueue id = 0x%X, seq = %d, len = %d", id, seq, length);
    pthread_mutex_lock(&tx_queue_mutex);
    // If the item is exist, skip it
    if (FindTxQueue(head, id, seq) != NULL) {
        DEBUG("已经有了,我们不干了");
        goto ATQ_END;
    }
    if ((new_item = (message_tx_table_t *)malloc(sizeof(message_tx_table_t) + length))== NULL) {         // Not enough memory!
        LOGE("Not enough memory!");
        goto ATQ_END;
    }
    new_item->next = NULL;
    new_item->access = access;
    new_item->id = id;
    new_item->seq = seq;
    new_item->length = length;
    new_item->curr_cnt = 0;
    new_item->max_try_cnt = 1 + resend;         // Zero means unlimited
    new_item->resend_interval = resend_intval;
    new_item->time_out = resend_intval + AppTimer_GetTickCount();
    memcpy(new_item->data, data, length);
    if (*head == NULL) {
        new_item->prev = NULL;
        *head = new_item;
        DEBUG("############### Create head node ##################");
    } else {
        message_tx_table_t *ptr = *head;
        while (ptr->next != NULL) {
            ptr = ptr->next;
        }
        ptr->next = new_item;
        new_item->prev = ptr;
    }
    SendQueue(new_item);
    if (new_item->max_try_cnt > 0 && new_item->curr_cnt >= new_item->max_try_cnt) {
        if (new_item == *head) {
            if (new_item->next == NULL) {
                *head = NULL;
                DEBUG("****************** Delete all ******************");
            } else {
                *head = new_item->next;
                new_item->next->prev = NULL;
            }
        } else {
            if (new_item->next != NULL) {
                new_item->next->prev = new_item->prev;
            }
            new_item->prev->next = new_item->next;
        }
        free(new_item);
    }
    ATQ_END:
    pthread_mutex_unlock(&tx_queue_mutex);
    uint32_t tim = GetResentTimeoutTxQueue(&message_tx_table_head[DATA_ACCESS_PLATFORM]);
    DEBUG("NEXT ====  %ld", tim);
    AppTimer_delete(TriggerResendTxQueue);
    if (tim != uint32_t (-1)) {
        AppTimer_add(TriggerResendTxQueue, tim);
    }
}
static void RemoveTxQueue(message_tx_table_t **head, uint16_t id, uint16_t seq)
{
    if (head == NULL)
        return;
    message_tx_table_t *ptr = *head;
    pthread_mutex_lock(&tx_queue_mutex);
    while ( ptr != NULL ) {
        if (ptr->id == id && ptr->seq == seq) {
            // delete
            message_tx_table_t *temp = ptr;
            if (ptr == *head) {
                if (ptr->next == NULL) {
                    DEBUG("****************** RemoveTxQueue Delete all ******************");
                    ptr = *head = NULL;
                } else {
                    *head = ptr->next;
                    ptr->next->prev = NULL;
                    ptr = *head;
                }
            } else {
                if (ptr->next != NULL) {
                    ptr->next->prev = ptr->prev;
                }
                ptr->prev->next = ptr->next;
                ptr = ptr->next;
            }
            free(temp);
        } else {
            ptr = ptr->next;
        }
    }
    pthread_mutex_unlock(&tx_queue_mutex);
}
static void RemoveTxQueue(message_tx_table_t **head, uint16_t id)
{
    if (head == NULL)
        return;
    message_tx_table_t *ptr = *head;
    pthread_mutex_lock(&tx_queue_mutex);
    while ( ptr != NULL ) {
        if (ptr->id == id) {
            // delete
            message_tx_table_t *temp = ptr;
            DEBUG("****************** 我们找到了, 弄死它 ******************");
            if (ptr == *head) {
                if (ptr->next == NULL) {
                    DEBUG("****************** RemoveTxQueue Delete all ******************");
                    ptr = *head = NULL;
                } else {
                    *head = ptr->next;
                    ptr->next->prev = NULL;
                    ptr = *head;
                }
            } else {
                if (ptr->next != NULL) {
                    ptr->next->prev = ptr->prev;
                }
                ptr->prev->next = ptr->next;
                ptr = ptr->next;
            }
            free(temp);
        } else {
            ptr = ptr->next;
        }
    }
    pthread_mutex_unlock(&tx_queue_mutex);
}
static void RemoveAllTxQueue(message_tx_table_t **head)
{
    if (head == NULL) {
        return;
    }
    pthread_mutex_lock(&tx_queue_mutex);
    for (message_tx_table_t *ptr = *head, *next; ptr != NULL; ptr = next) {
        next = ptr->next;
        free(ptr);
    }
    *head = NULL;
    pthread_mutex_unlock(&tx_queue_mutex);
}
static int SendQueue(message_tx_table_t *item)
{
    DEBUG("SendQueue id = 0x%04X, seq = %d, length = %d", item->id, item->seq, item->length);
    if (item != NULL) {
        if (item->access == DATA_ACCESS_MCU) {
            if (WriteSerialPort(GetSerialPort(UART_1), item->data, item->length) != item->length) {
                item->time_out = 100 + AppTimer_GetTickCount();
                LOGE("发往串口出错了");
//                return -1;
            }
        } else if(item->access == DATA_ACCESS_PLATFORM) {
            if (WritePlatform(item->data, item->length) != item->length) {
                item->time_out = D_SEC(3) + AppTimer_GetTickCount();
                LOGE("发往网络出错了 seq = %d", item->seq);
//                return -2;
            }
        } else {
//            return 0;
        }
#ifdef ENABLE_DEBUG_PROTOCOL
        /*{
            static char buff[16384];
            buff[0] = 0;
            int i = 0;
            for (i = 0; i < item->length; i++) {
                if ((i % 32) == 0) {
                    sprintf(buff + strlen(buff), "\n");
                }
                sprintf(buff + strlen(buff), "%02X ", item->data[i]);
                if (strlen(buff) > 800) {
                    DEBUG("%s -> %s...", item->access == DATA_ACCESS_MCU ? "UART" : "TCP" , buff);
                    buff[0] = 0;
                }
            }
            if (strlen(buff) > 0) {
                DEBUG("%s -> %s", item->access == DATA_ACCESS_MCU ? "UART" : "TCP", buff);
            }
        }*/
#endif
        // 系统层确认发送成功
        item->curr_cnt += 1;
        item->time_out = GetResendTimeout(item->access, item->curr_cnt - 1, item->resend_interval) + AppTimer_GetTickCount();
    }
    return 0;
}
static void ResendItemTimeout(message_tx_table_t **head, uint32_t tm)
{
    if (head == NULL)
        return;
    message_tx_table_t *ptr = *head;
    while ( ptr != NULL ) {
        if (ptr->time_out <= tm) {
            // send
            LOGE("重发 id 0x%04X seq %d", ptr->id, ptr->seq);
            int ret = SendQueue(ptr);
            if (ret < 0 && ret != -3) {
                break;
            }
            if (ptr->max_try_cnt > 0 && ptr->curr_cnt >= ptr->max_try_cnt) {
                // delete
                DEBUG("Delete item %d", ptr->curr_cnt);
                message_tx_table_t *temp = ptr;
                pthread_mutex_lock(&tx_queue_mutex);
                if (ptr == *head) {
                    if (ptr->next == NULL) {
                        DEBUG("****************** Delete all 2 ******************");
                        ptr = *head = NULL;
                    } else {
                        *head = ptr->next;
                        ptr->next->prev = NULL;
                        ptr = *head;
                    }
                } else {
                    if (ptr->next != NULL) {
                        ptr->next->prev = ptr->prev;
                    }
                    ptr->prev->next = ptr->next;
                    ptr = ptr->next;
                }
                pthread_mutex_unlock(&tx_queue_mutex);
                free(temp);
            } else {
                ptr = ptr->next;
            }
        } else {
            ptr = ptr->next;
        }
    }
}
static uint32_t GetResentTimeoutTxQueue(message_tx_table_t **head)
{
    uint32_t resentTime = uint32_t (-1);
    uint32_t now = AppTimer_GetTickCount();
    if (head == NULL)
        return resentTime;
    pthread_mutex_lock(&tx_queue_mutex);
    message_tx_table_t *ptr = *head;
    while ( ptr != NULL ) {
        if (ptr->time_out <= now) {
            resentTime = 0;
            break;
        } else if (ptr->time_out - now < resentTime) {
            resentTime = ptr->time_out - now;
        }
        ptr = ptr->next;
    }
    pthread_mutex_unlock(&tx_queue_mutex);
    return resentTime;
}
static sem_t sem_tx_mgr;
static void *TxQueueMgrThread(void *p) {
    while (true) {
        sem_wait(&sem_tx_mgr);
        uint32_t tim = AppTimer_GetTickCount();
        DEBUG("TxQueueMgrThread %ld", tim);
        // Check send queue, and resend
        ResendItemTimeout(&message_tx_table_head[DATA_ACCESS_PLATFORM], tim);
        tim = GetResentTimeoutTxQueue(&message_tx_table_head[DATA_ACCESS_PLATFORM]);
        DEBUG("NEXT ====  %ld", tim);
        AppTimer_delete(TriggerResendTxQueue);
        if (tim != uint32_t (-1)) {
            AppTimer_add(TriggerResendTxQueue, tim);
        }
    }
    pthread_exit(NULL);
}
static void TriggerResendTxQueue(union sigval sig) {
    AppTimer_delete(TriggerResendTxQueue);
    sem_post(&sem_tx_mgr);
}
void PlatformTxInit(const uint8_t *phone)
{
    //2019101500000001
    memcpy(PhoneNumber, phone, PHONE_NUM_SIZE);
/*    PhoneNumber[0] = 0x20;
    PhoneNumber[1] = 0x19;
    PhoneNumber[2] = 0x10;
    PhoneNumber[3] = 0x15;
    PhoneNumber[4] = 0x00;
    PhoneNumber[5] = 0x00;
    PhoneNumber[6] = 0x00;
    PhoneNumber[7] = 0x02;*/
    pthread_mutex_init(&seq_mutex, NULL);
    pthread_mutex_init(&tx_queue_mutex, NULL);
    sem_init(&sem_tx_mgr, 0, 0);
    pthread_t pid;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
    pthread_create(&pid, &attr, TxQueueMgrThread, NULL);
}
void CommonRespend(uint8_t access, uint16_t seq, uint16_t id, uint8_t value)
{
    uint8_t data[5];
    data[0] = HI_UINT16(seq);
    data[1] = LO_UINT16(seq);
    data[2] = HI_UINT16(id);
    data[3] = LO_UINT16(id);
    data[4] = value;
    message_t msg;
    MakeMessage(&msg, MESSAGE_VERSION_B2016, ID_CP_COMMON_RSP, PROTOCOL_ENCRYPT_NONE,
                PhoneNumber, PKT_RESEVER, 0, DEFAULT_SHORT_RESEND_INTERVAL);
    SendMessage(access, &msg, data, sizeof(data));
}
void SendHeartBeat(uint8_t access)
{
    message_t msg;
    MakeMessage(&msg, MESSAGE_VERSION_B2016, ID_CP_HEARTBEAT, PROTOCOL_ENCRYPT_NONE,
                PhoneNumber, PKT_RESEVER, 0, DEFAULT_SHORT_RESEND_INTERVAL);
    SendMessage(access, &msg, NULL, 0);
}
void SendDeviceRegister(uint16_t province, uint16_t city, const uint8_t *device_model,
        int device_model_length, const uint8_t *device_sn, const char *imei)
{
    uint8_t data[64] = {0};
    int x = 0;
    data[x++] = HI_UINT16(province);
    data[x++] = LO_UINT16(province);
    data[x++] = HI_UINT16(city);
    data[x++] = LO_UINT16(city);
    memcpy(data + x, device_model, device_model_length);
    x += 20;
    memcpy(data + x, device_sn, 16);
    x += 16;
    memcpy(data + x, imei, strlen(imei));
    x += 15;
    message_t msg;
    MakeMessage(&msg, MESSAGE_VERSION_B2016, ID_CP_DEVICE_REG, PROTOCOL_ENCRYPT_NONE,
                PhoneNumber, PKT_RESEVER, 0, DEFAULT_SHORT_RESEND_INTERVAL);
    SendMessage(DATA_ACCESS_PLATFORM, &msg, data, x);
}
void SendDeviceLogin(const uint8_t *data, int length)
{
    message_t msg;
    MakeMessage(&msg, MESSAGE_VERSION_B2016, ID_CP_LOGIN_REQ, PROTOCOL_ENCRYPT_NONE,
                PhoneNumber, PKT_RESEVER, 0, DEFAULT_SHORT_RESEND_INTERVAL);
    SendMessage(DATA_ACCESS_PLATFORM, &msg, data, length);
}
void SendRTKReport(uint8_t gps_status, uint32_t latitude, uint32_t longitude, uint16_t altitude,
        const uint8_t *bcd_time, const uint8_t *rtk, int rtk_length)
{
    uint8_t data[MAX_CONTENT_SIZE];
    int x = 0;
    data[x++] = gps_status;
    data[x++] = BREAK_UINT32(latitude, 3);
    data[x++] = BREAK_UINT32(latitude, 2);
    data[x++] = BREAK_UINT32(latitude, 1);
    data[x++] = BREAK_UINT32(latitude, 0);
    data[x++] = BREAK_UINT32(longitude, 3);
    data[x++] = BREAK_UINT32(longitude, 2);
    data[x++] = BREAK_UINT32(longitude, 1);
    data[x++] = BREAK_UINT32(longitude, 0);
    data[x++] = HI_UINT16(altitude);
    data[x++] = LO_UINT16(altitude);
    memcpy(data + x, bcd_time, 6);
    x += 6;
    memcpy(data + x, rtk, rtk_length);
    x += rtk_length;
    message_t msg;
    MakeMessage(&msg, MESSAGE_VERSION_B2016, ID_CP_RTK_UPLOAD, PROTOCOL_ENCRYPT_NONE,
                PhoneNumber, PKT_RESEVER, 0, DEFAULT_SHORT_RESEND_INTERVAL);
    SendMessage(DATA_ACCESS_PLATFORM, &msg, data, x);
}
void SendRTKStart(uint32_t latitude, uint32_t longitude, uint16_t altitude,
                    const uint8_t *bcd_time, uint16_t rtk_pkt_interval)
{
    uint8_t data[18];
    int x = 0;
    data[x++] = BREAK_UINT32(latitude, 3);
    data[x++] = BREAK_UINT32(latitude, 2);
    data[x++] = BREAK_UINT32(latitude, 1);
    data[x++] = BREAK_UINT32(latitude, 0);
    data[x++] = BREAK_UINT32(longitude, 3);
    data[x++] = BREAK_UINT32(longitude, 2);
    data[x++] = BREAK_UINT32(longitude, 1);
    data[x++] = BREAK_UINT32(longitude, 0);
    data[x++] = HI_UINT16(altitude);
    data[x++] = LO_UINT16(altitude);
    memcpy(data + x, bcd_time, 6);
    x += 6;
    data[x++] = HI_UINT16(rtk_pkt_interval);
    data[x++] = LO_UINT16(rtk_pkt_interval);
    message_t msg;
    MakeMessage(&msg, MESSAGE_VERSION_B2016, ID_CP_RTK_START_REQ, PROTOCOL_ENCRYPT_NONE,
                PhoneNumber, PKT_RESEVER, 0, DEFAULT_SHORT_RESEND_INTERVAL);
    SendMessage(DATA_ACCESS_PLATFORM, &msg, data, x);
}
void SendRTKStop(void)
{
    message_t msg;
    MakeMessage(&msg, MESSAGE_VERSION_B2016, ID_CP_RTK_END_REQ, PROTOCOL_ENCRYPT_NONE,
                PhoneNumber, PKT_RESEVER, 0, DEFAULT_SHORT_RESEND_INTERVAL);
    SendMessage(DATA_ACCESS_PLATFORM, &msg, NULL, 0);
}
lib/src/main/cpp/rtk_platform/parse_net.h
New file
@@ -0,0 +1,55 @@
//
// Created by YY on 2019/10/8.
//
#ifndef RTKBASESTATION_PARSE_NET_H
#define RTKBASESTATION_PARSE_NET_H
enum
{
    DATA_ACCESS_MCU = 0,
    DATA_ACCESS_PLATFORM,
    DATA_ACCESS_PLATFORM_2,
    DATA_ACCESS_PLATFORM_LOG,
    DATA_ACCESS_END
};
#define MESSAGE_VERSION_JT808          0
#define MESSAGE_VERSION_A2013          1
#define MESSAGE_VERSION_B2016          128
#define PROTOCOL_ENCRYPT_NONE           0
#define PROTOCOL_ENCRYPT_RSA2048        1
#define COMMON_RESP_SUCCESS           0
#define COMMON_RESP_FAIL           1
#define COMMON_RESP_ERROR           2
#define COMMON_RESP_NOT_SUPPORT     3
#define EX_MESSAGE_TYPE_INTERNAL       0x9A
#define EX_MESSAGE_TYPE_EXTERNAL       0x13
#define EX_MESSAGE_DOWNLOAD         0x8900
#define EX_MESSAGE_UPLOAD           0x0900
#define EX_MESSAGE_ATTRIB_RESEND        1           //是否是重传包
#define EX_MESSAGE_ATTRIB_INDICATE     2            //该包是否需要远端应答
#define EX_MESSAGE_ATTRIB_SHA1          4
#define EX_MESSAGE_ATTRIB_SHA256        8
void PlatformTxInit(const uint8_t *phone);
void Parse(uint8_t access, const uint8_t *buff, uint16_t length);
void CommonRespend(uint8_t access, uint16_t seq, uint16_t id, uint8_t value);
void SendHeartBeat(uint8_t access);
void SendDeviceRegister(uint16_t province, uint16_t city, const uint8_t *device_model,
                    int device_model_length, const uint8_t *device_sn, const char *imei);
void SendDeviceLogin(const uint8_t *data, int length);
void SendRTKReport(uint8_t gps_status, uint32_t latitude, uint32_t longitude, uint16_t altitude,
               const uint8_t *bcd_time, const uint8_t *rtk, int rtk_length);
void SendRTKStart(uint32_t latitude, uint32_t longitude, uint16_t altitude,
                    const uint8_t *bcd_time, uint16_t rtk_pkt_interval);
void SendRTKStop(void);
#endif //RTKBASESTATION_PARSE_NET_H
lib/src/main/cpp/rtk_platform/platform.cpp
New file
@@ -0,0 +1,361 @@
//
// Created by YY on 2019/12/27.
//
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <pthread.h>
#include <semaphore.h>
#include "platform.h"
#include "../jni_log.h"
#include "../common/net.h"
#include "../native-lib.h"
#include "../common/apptimer.h"
#include "parse_net.h"
#include "../defs.h"
#include "../rtk_module/rtk.h"
#define PARSE_BUFF_SIZE         4096
const char *VK_REG = "platform_register";
struct platformSocket {
    char domain_name[32];
    int port;
};
static struct deviceInfo_ {
    uint16_t province;
    uint16_t city;
    uint8_t device_model[21];
    uint8_t device_sn[17];
    char imei[16];
}deviceInfo;
struct platformSocketInfo {
    char domain_name[32];
    int port;
};
static struct platformStatus_ {
    uint8_t platformKey[64];
    int platformKeyLength;
    uint32_t connected : 1;
    uint32_t registed : 1;
    uint32_t login : 1;
    uint32_t downloadRtk : 1;
} platformStatus;
struct platformSocket exceptSocket, currSocket;
static uint32_t eventMask;
static sem_t sem_status_changed;
static bool requestPlatformSendRtk = false;
static int platform_tcp_fd = 0;
static pthread_mutex_t platform_tx_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t events_mutex = PTHREAD_MUTEX_INITIALIZER;
static void ConnectPlatform(const char *domain_name, int port);
static void ConnectPlatformLater(union sigval sig);
static void *PlatformDataListenThread(void *p);
static void *StatusListenThread(void *p);
static void RegisterPlatform(void);
static void RegisterPlatformTimeout(union sigval sig);
static void LoginPlatform(void);
static void LoginPlatformTimeout(union sigval sig);
static void TriggerHeartbeat(union sigval sig);
void InitPlatform(const uint8_t *phone, const char *domain_name, int port)
{
    pthread_mutex_init(&platform_tx_mutex, NULL);
    pthread_mutex_init(&events_mutex, NULL);
    eventMask = 0;
    platform_tcp_fd = -1;
    memset(&currSocket, 0, sizeof(currSocket));
    strcpy(exceptSocket.domain_name, domain_name);
    exceptSocket.port = port;
    sem_init(&sem_status_changed, 0, 0);
    strcpy(deviceInfo.imei, GetImei());
    deviceInfo.province = 53;
    deviceInfo.city = 100;
    strcpy((char *)deviceInfo.device_model, "RTKBaseStation");
    strcpy((char *)deviceInfo.device_sn, "2019101500000001");
    PlatformTxInit(phone);
    pthread_t pid;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
    pthread_create(&pid, &attr, StatusListenThread, NULL);
    strcpy(deviceInfo.imei, GetImei());
    ConnectPlatform(domain_name, port);
}
void PlatformStatusChanged(uint32_t event)
{
    pthread_mutex_lock(&events_mutex);
    eventMask |= event;
    pthread_mutex_unlock(&events_mutex);
    sem_post(&sem_status_changed);
}
static void ConnectPlatform(const char *domain_name, int port)
{
    DEBUG("ConnectPlatform %s: %d", domain_name, port);
    // TODO
    struct platformSocketInfo *ptr = (struct platformSocketInfo *)malloc(sizeof(struct platformSocketInfo));
    strcpy(ptr->domain_name, domain_name);
    ptr->port = port;
    pthread_t platform_pid;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
    pthread_create(&platform_pid, &attr, PlatformDataListenThread, ptr);
    char out[64];
    sprintf(out, "连接平台 %s:%d...", domain_name, port);
    DisplayText(out);
}
static void ConnectPlatformLater(union sigval sig) {
    AppTimer_delete(ConnectPlatformLater);
    ConnectPlatform(exceptSocket.domain_name, exceptSocket.port);
}
static void *StatusListenThread(void *p) {
    while (true) {
        sem_wait(&sem_status_changed);
        uint32_t events;
        pthread_mutex_lock(&events_mutex);
        events = eventMask;
        eventMask = 0;
        pthread_mutex_unlock(&events_mutex);
        if (events & PLATFORM_CONNECT_EVT) {
            char out[64];
            sprintf(out, "平台连接成功 %s:%d", currSocket.domain_name, currSocket.port);
            DisplayText(out);
            platformStatus.connected = 1;
            if (!platformStatus.registed || platformStatus.platformKeyLength == 0) {
                RegisterPlatform();
            } else if (!platformStatus.login) {
                LoginPlatform();
            }
        }
        if (events & PLATFORM_DISCONNECT_EVT) {
            char out[64];
            sprintf(out, "平台断开 %s:%d", currSocket.domain_name, currSocket.port);
            DisplayText(out);
            AppTimer_delete(ConnectPlatformLater);
            AppTimer_add(ConnectPlatformLater, D_SEC(2));
            platformStatus.login = 0;
            platformStatus.connected = 0;
            AppTimer_delete(TriggerHeartbeat);
            AppTimer_delete(RegisterPlatformTimeout);
            AppTimer_delete(LoginPlatformTimeout);
        }
        if (events & PLATFORM_REGISTER_EVT) {
            DEBUG("PLATFORM_REGISTER_EVT");
            platformStatus.platformKeyLength = GetPlatformKey(platformStatus.platformKey);
            platformStatus.registed = GetSharedValue(VK_REG);
            platformStatus.login = 0;
            DEBUG("platformStatus.platformKeyLength = %d", platformStatus.platformKeyLength);
            if (platformStatus.registed && platformStatus.platformKeyLength > 0) {
                LoginPlatform();
            }
        }
        if (events & PLATFORM_LOGIN_EVT) {
            DEBUG("PLATFORM_LOGIN_EVT");
            platformStatus.login = 1;
            requestPlatformSendRtk = true;
            AppTimer_delete(TriggerHeartbeat);
            AppTimer_add(TriggerHeartbeat, D_SEC(30));
        }
    }
}
static void *PlatformDataListenThread(void *p) {
    struct platformSocket *ptr = (struct platformSocket *)p;
    uint8_t RxBuf[PARSE_BUFF_SIZE];
    int fds_ret;
    struct timeval tv;
    fd_set rdfds;
    fd_set exfds;
    int fd = -1;
    int RxBufLen = 0;
    fd = ConnectTCP(ptr->domain_name, ptr->port);
    pthread_mutex_lock(&platform_tx_mutex);
    platform_tcp_fd = fd;
    currSocket = *ptr;
    pthread_mutex_unlock(&platform_tx_mutex);
    if (fd > 0) {
        PlatformStatusChanged(PLATFORM_CONNECT_EVT);
    }
    while (fd > 0) {
        tv.tv_sec = 5;
        tv.tv_usec = 0;
        FD_ZERO(&rdfds); //clean
        FD_SET(fd, &rdfds); //set
        fds_ret = select(fd + 1, &rdfds, NULL, NULL, &tv);
        if (fds_ret < 0) {
            break;
        } else if(fds_ret == 0) {
            //Occur failure(such as line disconnect)
        } else if(FD_ISSET(fd, &rdfds)) {
            RxBufLen = ReadTCP(fd, RxBuf, sizeof(RxBuf));
            if (RxBufLen < 0) {
                break;
            } else if (RxBufLen > 0) {
                Parse(DATA_ACCESS_PLATFORM, RxBuf, RxBufLen);
            }
        }
    }
    pthread_mutex_lock(&platform_tx_mutex);
    if (platform_tcp_fd > 0) {
        DisconnectTCP(platform_tcp_fd);
        platform_tcp_fd = -1;
    }
    pthread_mutex_unlock(&platform_tx_mutex);
    free(ptr);
    PlatformStatusChanged(PLATFORM_DISCONNECT_EVT);
    pthread_exit(NULL);
}
int WritePlatform(const uint8_t * buf, uint32_t len)
{
    int ret = -1;
    pthread_mutex_lock(&platform_tx_mutex);
    if (platform_tcp_fd > 0) {
        ret = WriteTCP(platform_tcp_fd, buf, len);
    }
    pthread_mutex_unlock(&platform_tx_mutex);
    return ret;
}
static void RegisterPlatformTimeout(union sigval sig)
{
    DEBUG("RegisterPlatformTimeout");
    AppTimer_delete(RegisterPlatformTimeout);
    RegisterPlatform();
}
static void RegisterPlatform(void)
{
    AppTimer_delete(RegisterPlatformTimeout);
    AppTimer_add(RegisterPlatformTimeout, D_SEC(15));
    SendDeviceRegister(deviceInfo.province, deviceInfo.city, deviceInfo.device_model,
                       strlen((char *)deviceInfo.device_model), deviceInfo.device_sn, deviceInfo.imei);
}
static void TriggerHeartbeat(union sigval sig) {
    AppTimer_delete(TriggerHeartbeat);
    if (platformStatus.login && platformStatus.connected) {
        SendHeartBeat(DATA_ACCESS_PLATFORM);
        AppTimer_add(TriggerHeartbeat, D_SEC(30));
    }
}
static void LoginPlatformTimeout(union sigval sig)
{
    AppTimer_delete(LoginPlatformTimeout);
    LoginPlatform();
}
static void LoginPlatform(void)
{
    uint32_t tim = time(NULL);
    uint8_t data[12];
    uint8_t *ciphertext;
    data[0] = BREAK_UINT32(tim, 3);
    data[1] = BREAK_UINT32(tim, 2);
    data[2] = BREAK_UINT32(tim, 1);
    data[3] = BREAK_UINT32(tim, 0);
    AppTimer_delete(LoginPlatformTimeout);
    AppTimer_add(LoginPlatformTimeout, D_SEC(15));
    DESEncrypt(platformStatus.platformKey, platformStatus.platformKeyLength, data, 4, &ciphertext);
    if (ciphertext != NULL) {
        memcpy(data + 4, ciphertext, 8);
        SendDeviceLogin(data, sizeof(data));
    }
}
void DeviceRegisterCallback(uint8_t res, const uint8_t *data, int length)
{
    AppTimer_delete(RegisterPlatformTimeout);
    if (res != 0) {
    } else {
        TextSpeak("终端注册成功");
        DisplayText("终端注册成功");
        SetPlatformKey(data, length);
        SetSharedValue(VK_REG, 1);
        PlatformStatusChanged(PLATFORM_REGISTER_EVT);
    }
}
void DeviceLoginCallback(uint8_t res)
{
    AppTimer_delete(LoginPlatformTimeout);
    if (res != 0) {
    } else {
        TextSpeak("终端登录成功");
        DisplayText("终端登录成功");
        PlatformStatusChanged(PLATFORM_LOGIN_EVT);
    }
}
void RequestRtkDownload(uint32_t latitude, uint32_t longitude, uint16_t altitude,
                        const uint8_t *bcd_time, uint16_t rtk_pkt_interval)
{
    if (requestPlatformSendRtk) {
        requestPlatformSendRtk = false;
        SendRTKStart(latitude, longitude, altitude, bcd_time, rtk_pkt_interval);
    }
}
void StopRtkDownload(void)
{
    SendRTKStop();
}
lib/src/main/cpp/rtk_platform/platform.h
New file
@@ -0,0 +1,25 @@
//
// Created by YY on 2019/12/27.
//
#ifndef RTKDRIVERTEST_PLATFORM_H
#define RTKDRIVERTEST_PLATFORM_H
#define PLATFORM_CONNECT_EVT                         0x0001
#define PLATFORM_DISCONNECT_EVT                      0x0002
#define PLATFORM_REGISTER_EVT                        0x0004
#define PLATFORM_LOGIN_EVT                           0x0008
#define RTK_UPDATE_EVT                               0x0010
#define GPS_UPDATE_EVT                              0x0020
void InitPlatform(const uint8_t *phone, const char *domain_name, int port);
void PlatformStatusChanged(uint32_t event);
int WritePlatform(const uint8_t * buf, uint32_t len);
void DeviceRegisterCallback(uint8_t res, const uint8_t *data, int length);
void DeviceLoginCallback(uint8_t res);
void RequestRtkDownload(uint32_t latitude, uint32_t longitude, uint16_t altitude,
                        const uint8_t *bcd_time, uint16_t rtk_pkt_interval);
void StopRtkDownload(void);
#endif //RTKDRIVERTEST_PLATFORM_H
lib/src/main/cpp/test_items/driving_curve.cpp
New file
@@ -0,0 +1,140 @@
//
// Created by YY on 2019/11/4.
//
#include "driving_curve.h"
#include "../driver_test.h"
#include "../common/apptimer.h"
#include <vector>
using namespace std;
enum {
    DRIVING_ON_CURVE
};
const uint32_t STOP_CAR_TIME = D_SEC(2);
static bool carStopEvent = false;
static bool DCTesting = false;
static int currTarget;
static uint32_t stopTimepoint = 0;
static bool CrashRedLine(const Polygon *left, const Polygon *right, const car_model_cache_t *car);
static bool ExitTestArea(const Polygon *left, const Polygon *right, const car_model_cache_t *car);
void StartDrivingCurve(void)
{
    DCTesting = true;
    carStopEvent = false;
    currTarget = DRIVING_ON_CURVE;
}
void StopDrivingCurve(void)
{
    DCTesting = false;
}
int TestDrivingCurve(vector<int>&err, const Polygon *map, const Polygon *map2, const car_model_cache_t *car, double speed, int run_status)
{
    int status = 0;
    if (!DCTesting)
        return -2;
    if (currTarget == DRIVING_ON_CURVE) {
        if (CrashRedLine(map, map2, car)) {
            // 车轮压线
            err.push_back(27);
            status = -1;
        }
        if (ExitTestArea(map, map2, car)) {
            // 测试完成
            status = 1;
        }
        if (run_status != 0) {
            if (carStopEvent && AppTimer_GetTickCount() - stopTimepoint > D_SEC(2)) {
                // 中途停车
                err.push_back(28);
            }
            carStopEvent = false;
        } else {
            carStopEvent = true;
            stopTimepoint = AppTimer_GetTickCount();
        }
    }
    if (status != 0) {
        StopDrivingCurve();
    }
    return status;
}
// 车轮是否压边线
static bool CrashRedLine(const Polygon *left, const Polygon *right, const car_model_cache_t *car)
{
    bool ret = false;
    car_model_cache_t *prev_car = GetCarModelCache(1);
    if (prev_car == NULL)
        return false;
    // 按4个轮子的外侧计算
    Line front_left_tire_track, front_right_tire_track, rear_left_tire_track, rear_right_tire_track;
    MakeLine(&front_left_tire_track,  &car->points[car->desc->front_left_tire[TIRE_OUTSIDE]],  &prev_car->points[car->desc->front_left_tire[TIRE_OUTSIDE]]);
    MakeLine(&front_right_tire_track,  &car->points[car->desc->front_right_tire[TIRE_OUTSIDE]],  &prev_car->points[car->desc->front_right_tire[TIRE_OUTSIDE]]);
    MakeLine(&rear_left_tire_track,  &car->points[car->desc->rear_left_tire[TIRE_OUTSIDE]],  &prev_car->points[car->desc->rear_left_tire[TIRE_OUTSIDE]]);
    MakeLine(&rear_right_tire_track,  &car->points[car->desc->rear_right_tire[TIRE_OUTSIDE]],  &prev_car->points[car->desc->rear_right_tire[TIRE_OUTSIDE]]);
    Line line;
    line.X1 = left->point[0].X;
    line.Y1 = left->point[0].Y;
    for (int i = 1; i < left->num; ++i) {
        line.X2 = left->point[i].X;
        line.Y2 = left->point[i].Y;
        if (IntersectionOf(line, front_left_tire_track) == GM_Intersection ||
                IntersectionOf(line, front_right_tire_track) == GM_Intersection ||
                IntersectionOf(line, rear_left_tire_track) == GM_Intersection ||
                IntersectionOf(line, rear_right_tire_track) == GM_Intersection) {
            ret = true;
            break;
        }
        line.X1 = line.X2;
        line.Y1 = line.Y2;
    }
    line.X1 = right->point[0].X;
    line.Y1 = right->point[0].Y;
    for (int i = 1; !ret && i < right->num; ++i) {
        line.X2 = right->point[i].X;
        line.Y2 = right->point[i].Y;
        if (IntersectionOf(line, front_left_tire_track) == GM_Intersection ||
            IntersectionOf(line, front_right_tire_track) == GM_Intersection ||
            IntersectionOf(line, rear_left_tire_track) == GM_Intersection ||
            IntersectionOf(line, rear_right_tire_track) == GM_Intersection) {
            ret = true;
            break;
        }
        line.X1 = line.X2;
        line.Y1 = line.Y2;
    }
    return ret;
}
// 整个车辆都要驶离该测试区域
static bool ExitTestArea(const Polygon *left, const Polygon *right, const car_model_cache_t *car)
{
    for (int i = 0; i < car->point_num; ++i) {
        if (IntersectionOfLine(left->point[0], right->point[0], car->points[i]) != 1)
            return false;
    }
    return true;
}
lib/src/main/cpp/test_items/driving_curve.h
New file
@@ -0,0 +1,17 @@
//
// Created by YY on 2019/11/4.
//
#ifndef RTKDRIVERTEST_DRIVING_CURVE_H
#define RTKDRIVERTEST_DRIVING_CURVE_H
#include "../driver_test.h"
#include <vector>
using namespace std;
void StartDrivingCurve(void);
void StopDrivingCurve(void);
int TestDrivingCurve(vector<int>&err, const Polygon *map, const Polygon *map2, const car_model_cache_t *car, double speed, int run_status);
#endif //RTKDRIVERTEST_DRIVING_CURVE_H
lib/src/main/cpp/test_items/error_list.cpp
New file
@@ -0,0 +1,207 @@
#include "error_list.h"
//
// Created by YY on 2019/10/31.
//
const error_list_t errorList[] = {
        {
                .id = 0,
                .text_desc = "",
                .item = 0,
                .dec_score = 0
        },
        {
                .id = 1,
                .text_desc = "不按规定使用安全带",
                .item = 1,
                .dec_score = 100
        },
        {
                .id = 2,
                .text_desc = "不按考试员指令行驶",
                .item = 1,
                .dec_score = 100
        },
        {
                .id = 3,
                .text_desc = "启动发动机时挡位未置于空档(驻车挡)",
                .item = 1,
                .dec_score = 100
        },
        {
                .id = 4,
                .text_desc = "发动机启动后,不及时松开启动开关",
                .item = 1,
                .dec_score = 10
        },
        {
                .id = 5,
                .text_desc = "因操作不当造成发动机熄火一次",
                .item = 1,
                .dec_score = 10
        },
        {
                .id = 6,
                .text_desc = "不按规定线路、顺序行驶",
                .item = 2,
                .dec_score = 100
        },
        {
                .id = 7,
                .text_desc = "车身出线",
                .item = 2,
                .dec_score = 100
        },
        {
                .id = 8,
                .text_desc = "倒库不入",
                .item = 2,
                .dec_score = 100
        },
        {
                .id = 9,
                .text_desc = "在倒车前,未将两个前轮触地点均驶过控制线",
                .item = 2,
                .dec_score = 100
        },
        {
                .id = 10,
                .text_desc = "项目完成时间超过210秒",
                .item = 2,
                .dec_score = 100
        },
        {
                .id = 11,
                .text_desc = "中途停车时间超过2秒",
                .item = 2,
                .dec_score = 5
        },
        {
                .id = 12,
                .text_desc = "车辆停止后,前保险杠未定于桩杆线上,且前后超出50cm",
                .item = 3,
                .dec_score = 100
        },
        {
                .id = 13,
                .text_desc = "行驶中车轮压线",
                .item = 3,
                .dec_score = 100
        },
        {
                .id = 14,
                .text_desc = "车辆停止后,车身距离路边线超出50cm",
                .item = 3,
                .dec_score = 100
        },
        {
                .id = 15,
                .text_desc = "起步时间超过30s",
                .item = 3,
                .dec_score = 100
        },
        {
                .id = 16,
                .text_desc = "起步后溜大于30cm",
                .item = 3,
                .dec_score = 100
        },
        {
                .id = 17,
                .text_desc = "车辆停止后,前保险杠未定于桩杆线上,且前后不超出50cm",
                .item = 3,
                .dec_score = 10
        },
        {
                .id = 18,
                .text_desc = "车辆停止后,车身距离路边缘线超出30cm,未超出50cm",
                .item = 3,
                .dec_score = 10
        },
        {
                .id = 19,
                .text_desc = "停车后,未拉紧驻车制动器",
                .item = 3,
                .dec_score = 10
        },
        {
                .id = 20,
                .text_desc = "起步时车辆后溜距离10cm~30cm",
                .item = 3,
                .dec_score = 10
        },
        {
                .id = 21,
                .text_desc = "车辆入库停止后,车身出线",
                .item = 4,
                .dec_score = 100
        },
        {
                .id = 22,
                .text_desc = "项目完成时间超过90s",
                .item = 4,
                .dec_score = 100
        },
        {
                .id = 23,
                .text_desc = "行驶中车轮触轧车道边线",
                .item = 4,
                .dec_score = 10
        },
        {
                .id = 24,
                .text_desc = "行驶中车身触碰车位边线",
                .item = 4,
                .dec_score = 10
        },
        {
                .id = 25,
                .text_desc = "出库时不使用或错误使用转向灯",
                .item = 4,
                .dec_score = 10
        },
        {
                .id = 26,
                .text_desc = "中途停车时间超过2秒",
                .item = 4,
                .dec_score = 5
        },
        {
                .id = 27,
                .text_desc = "车轮轧道路边缘线",
                .item = 5,
                .dec_score = 100
        },
        {
                .id = 28,
                .text_desc = "中途停车时间超过2秒",
                .item = 5,
                .dec_score = 100
        },
        {
                .id = 29,
                .text_desc = "车轮轧道路边缘线",
                .item = 6,
                .dec_score = 100
        },
        {
                .id = 30,
                .text_desc = "转弯时不使用或错误使用转向灯,转弯后不关闭转向灯",
                .item = 6,
                .dec_score = 10
        },
        {
                .id = 31,
                .text_desc = "中途停车时间超过2秒",
                .item = 6,
                .dec_score = 5
        }
};
error_list_t GetErrorList(int index)
{
        if (index >= sizeof(errorList) / sizeof(errorList[0]))
                return errorList[0];
        return errorList[index];
}
lib/src/main/cpp/test_items/error_list.h
New file
@@ -0,0 +1,17 @@
//
// Created by YY on 2019/10/29.
//
#ifndef RTKDRIVERTEST_ERROR_LIST_H
#define RTKDRIVERTEST_ERROR_LIST_H
typedef struct {
    int id;
    int item;
    char *text_desc;
    int dec_score;
}error_list_t;
error_list_t GetErrorList(int index);
#endif //RTKDRIVERTEST_ERROR_LIST_H
lib/src/main/cpp/test_items/park_bottom.cpp
New file
@@ -0,0 +1,380 @@
//
// Created by YY on 2019/10/23.
//
#include "park_bottom.h"
#include "../common/apptimer.h"
#include "../Geometry.h"
#include "../native-lib.h"
#include "../jni_log.h"
#include "../driver_test.h"
#include <vector>
using namespace std;
enum {
    NONE,
    FIRST_TOUCH_CTRL_LINE,
    FIRST_PARK,
    SECOND_TOUCH_CTRL_LINE,
    SECOND_PARK,
    THIRD_TOUCH_CTRL_LINE
};
const int PARK_TIMEOUT = 210;
static bool PBTesting = false;
static bool trigLeaveTestAreaDetect = false;
static bool leaveTestArea = false;
static bool stopCar2S = false;
static int currTarget;
static bool leftTireCrossLeftLine, leftTireCrossRightLine, rightTireCrossLeftLine, rightTireCrossRightLine;
static char first_ctrl_line_id;
static bool carStopEvent;               // 中途停车标记
static bool carParkSuccess;             // 是否停在库位
static bool parkTimeout;
static void StopCarTimeout(union sigval sig);
static void LeaveTestAreaLongtime(union sigval sig);
static void CrossCtrlLine(const Polygon *map, const car_model_cache_t *car);
static bool EnterParking(const Polygon *map, const car_model_cache_t *car);
static void ParkBottomTimeout(union sigval sig);
static bool CrashRedLine(const Polygon *map, const car_model_cache_t *car);
void StartParkBottom(void)
{
    stopCar2S = false;
    trigLeaveTestAreaDetect = false;
    leaveTestArea = false;
    PBTesting = true;
    parkTimeout = false;
    first_ctrl_line_id = 0;
    currTarget = FIRST_TOUCH_CTRL_LINE;
    leftTireCrossLeftLine = leftTireCrossRightLine = rightTireCrossLeftLine = rightTireCrossRightLine = false;
    TextOsd(0, "ParkBottom");
}
void StopParkBottom(void)
{
    PBTesting = false;
    AppTimer_delete(StopCarTimeout);
    AppTimer_delete(ParkBottomTimeout);
    AppTimer_delete(LeaveTestAreaLongtime);
    currTarget = NONE;
    TextOsd(0, "ParkBottom End");
}
int TestParkBottom(vector<int>&err, const Polygon *map, const car_model_cache_t *car, double speed, int run_status)
{
    int status = 0;
    if (!PBTesting)
        return -2;
    DEBUG("TestParkBottom speed %f dir %d", speed, run_status);
    if (currTarget > FIRST_TOUCH_CTRL_LINE) {
        // 是否超时
        if (parkTimeout) {
            // 不合格:动作超时
            err.push_back(10);
            status = -1;
        }
        // 是否压线
        if (CrashRedLine(map, car)) {
            // 不合格:车身出线
            err.push_back(7);
            status = -1;
        }
        if (trigLeaveTestAreaDetect) {
            if (IntersectionOf(car->points[car->desc->front_left_tire[TIRE_OUTSIDE]], map) == GM_Containment &&
                IntersectionOf(car->points[car->desc->front_right_tire[TIRE_OUTSIDE]], map) == GM_Containment) {
                trigLeaveTestAreaDetect = false;
                AppTimer_delete(LeaveTestAreaLongtime);
            }
        }
    }
    if (currTarget == FIRST_TOUCH_CTRL_LINE ||
        currTarget == SECOND_TOUCH_CTRL_LINE ||
        currTarget == THIRD_TOUCH_CTRL_LINE) {
        if (run_status > 0) {
            if (stopCar2S && currTarget != FIRST_TOUCH_CTRL_LINE) {
                // 扣5分:中途停车超过2秒
                err.push_back(11);
            }
            if (!((leftTireCrossLeftLine && rightTireCrossLeftLine) ||
                    (leftTireCrossRightLine && rightTireCrossRightLine))) {
                DEBUG("CrossCtrlLine");
                CrossCtrlLine(map, car);
            } else if (currTarget == FIRST_TOUCH_CTRL_LINE || currTarget == SECOND_TOUCH_CTRL_LINE) {
                // 跨过控制线后,车辆持续向前行驶,处理这些乱搞情况
                // 整个车都离开测试区后,如果持续15秒,还没回到测试区,就忽略该测试或者淘汰
                if (leaveTestArea) {
                    if (currTarget == FIRST_TOUCH_CTRL_LINE) {
                        status = -2;
                        TextOsd(0, "来道场玩的");
                        DEBUG("来道场玩的");
                    } else {
                        // 不合格:未按规定线路行驶(直接跑出测试区了)
                        err.push_back(6);
                        status = -1;
                        TextOsd(0, "直接跑出测试区了");
                        DEBUG("直接跑出测试区了");
                    }
                } else if (!trigLeaveTestAreaDetect) {
                    Polygon car_body;
                    car_body.num = car->desc->body_num;
                    car_body.point = (PointF *) malloc(sizeof(PointF) * car_body.num);
                    for (int i = 0; i < car_body.num; ++i) {
                        car_body.point[i] = car->points[car->desc->body[i]];
                    }
                    if (IntersectionOf(map, &car_body) == GM_None) {
                        trigLeaveTestAreaDetect = true;
                        AppTimer_delete(LeaveTestAreaLongtime);
                        AppTimer_add(LeaveTestAreaLongtime, D_SEC(15));
                        DEBUG("开始离场计时");
                    }
                    free(car_body.point);
                }
            }
            if (currTarget == THIRD_TOUCH_CTRL_LINE) {
                char the_ctrl_line_crossed = 0;
                if (leftTireCrossLeftLine && rightTireCrossLeftLine) {
                    the_ctrl_line_crossed = 'L';
                } else if (leftTireCrossRightLine && rightTireCrossRightLine) {
                    the_ctrl_line_crossed = 'R';
                }
                if (the_ctrl_line_crossed != 0 && the_ctrl_line_crossed == first_ctrl_line_id) {
                    // 项目完成
                    status = 1;
                } else if (the_ctrl_line_crossed != 0) {
                    // 不合格:未按规定线路行驶(未回到起始点)
                    err.push_back(6);
                    status = -1;
                }
            }
            if (carStopEvent)
                AppTimer_delete(StopCarTimeout);
            carStopEvent = false;
            stopCar2S = false;
        } else if (run_status < 0) {
            // 左右倒库大纲并未要求谁先完成,故以先越过的控制线为准,下次得越过另外一条
            char the_ctrl_line_crossed = 0;
            if (leftTireCrossLeftLine && rightTireCrossLeftLine) {
                the_ctrl_line_crossed = 'L';
            } else if (leftTireCrossRightLine && rightTireCrossRightLine) {
                the_ctrl_line_crossed = 'R';
            }
            if (first_ctrl_line_id > 0 && first_ctrl_line_id == the_ctrl_line_crossed) {
                // 不合格:未按规定线路行驶(试图做2次同方向的倒库)
                err.push_back(6);
                status = -1;
            } else if (the_ctrl_line_crossed > 0 && first_ctrl_line_id == 0) {
                first_ctrl_line_id = the_ctrl_line_crossed;
                // 项目正式开始,210秒内完成
                AppTimer_delete(ParkBottomTimeout);
                AppTimer_add(ParkBottomTimeout, D_SEC(PARK_TIMEOUT));
                currTarget = FIRST_PARK;
                carParkSuccess = false;
                parkTimeout = false;
                leftTireCrossLeftLine = leftTireCrossRightLine = rightTireCrossLeftLine = rightTireCrossRightLine = false;
                TextOsd(0, "第一次倒库");
            } else if (the_ctrl_line_crossed > 0) {
                currTarget = SECOND_PARK;
                carParkSuccess = false;
                leftTireCrossLeftLine = leftTireCrossRightLine = rightTireCrossLeftLine = rightTireCrossRightLine = false;
                TextOsd(0, "第二次倒库");
            } else if (currTarget != THIRD_TOUCH_CTRL_LINE) {
                // 不合格:倒车前,2前轮没驶过控制线
                err.push_back(9);
                status = -1;
            }
            if (carStopEvent)
                AppTimer_delete(StopCarTimeout);
            carStopEvent = false;
            stopCar2S = false;
        } else {
            if (!carStopEvent) {
                AppTimer_delete(StopCarTimeout);
                AppTimer_add(StopCarTimeout, D_SEC(2));
            }
            carStopEvent = true;
        }
    } else if (currTarget == FIRST_PARK || currTarget == SECOND_PARK) {
        if (run_status < 0) {
            if (stopCar2S) {
                // 扣5分:中途停车
                err.push_back(11);
            }
            carStopEvent = false;
            stopCar2S = false;
        } else if (run_status == 0) {
            // 立即检查是否停车到位,也许是中途停车,先不管,待发生前进事件后,再断定是否停车到位
            if (!carStopEvent) {
                carStopEvent = true;
                carParkSuccess = EnterParking(map, car);
                AppTimer_delete(StopCarTimeout);
                AppTimer_add(StopCarTimeout, D_SEC(2));
            }
        } else {
            if (carStopEvent) {
                if (!carParkSuccess) {
                    // 不合格:倒库不入
                    err.push_back(8);
                    status = -1;
                } else if (currTarget == FIRST_PARK) {
                    currTarget = SECOND_TOUCH_CTRL_LINE;
                    TextOsd(0, "过另一根控制线");
                } else {
                    currTarget = THIRD_TOUCH_CTRL_LINE;
                    TextOsd(0, "再过第一根控制线");
                }
            }
            carStopEvent = false;
            stopCar2S = false;
        }
    }
    if (status != 0) {
        StopParkBottom();
    }
    return status;
}
static void StopCarTimeout(union sigval sig) {
    AppTimer_delete(StopCarTimeout);
    stopCar2S = true;
}
static void LeaveTestAreaLongtime(union sigval sig) {
    AppTimer_delete(LeaveTestAreaLongtime);
    leaveTestArea = true;
    trigLeaveTestAreaDetect = false;
}
static void ParkBottomTimeout(union sigval sig) {
    AppTimer_delete(ParkBottomTimeout);
    parkTimeout = true;
}
// 检测2前轮是否正向越过左右控制线
static void CrossCtrlLine(const Polygon *map, const car_model_cache_t *car)
{
    Line leftCtrlLine, rightCtrlLine;
    Line track1;
    PointF p1, p2;
    car_model_cache_t *prev_car = GetCarModelCache(1);
    if (prev_car == NULL)
        return;
    MakeLine(&leftCtrlLine, &map->point[0], &map->point[1]);
    MakeLine(&rightCtrlLine, &map->point[6], &map->point[7]);
    // 左前轮,取轮宽的中点
    p1.X = (car->points[car->desc->front_left_tire[TIRE_OUTSIDE]].X + car->points[car->desc->front_left_tire[TIRE_INSIDE]].X) / 2;
    p1.Y = (car->points[car->desc->front_left_tire[TIRE_OUTSIDE]].Y + car->points[car->desc->front_left_tire[TIRE_INSIDE]].Y) / 2;
    p2.X = (prev_car->points[prev_car->desc->front_left_tire[TIRE_OUTSIDE]].X + prev_car->points[prev_car->desc->front_left_tire[TIRE_INSIDE]].X) / 2;
    p2.Y = (prev_car->points[prev_car->desc->front_left_tire[TIRE_OUTSIDE]].Y + prev_car->points[prev_car->desc->front_left_tire[TIRE_INSIDE]].Y) / 2;
    MakeLine(&track1, &p1, &p2);
    if (IntersectionOf(track1, leftCtrlLine) == GM_Intersection &&
        IntersectionOf(p1, map) == GM_None) {
        leftTireCrossLeftLine = true;
    }
    if (IntersectionOf(track1, rightCtrlLine) == GM_Intersection &&
        IntersectionOf(p1, map) == GM_None) {
        leftTireCrossRightLine = true;
    }
    // 右前轮
    p1.X = (car->points[car->desc->front_right_tire[TIRE_OUTSIDE]].X + car->points[car->desc->front_right_tire[TIRE_INSIDE]].X) / 2;
    p1.Y = (car->points[car->desc->front_right_tire[TIRE_OUTSIDE]].Y + car->points[car->desc->front_right_tire[TIRE_INSIDE]].Y) / 2;
    p2.X = (prev_car->points[prev_car->desc->front_right_tire[TIRE_OUTSIDE]].X + prev_car->points[prev_car->desc->front_right_tire[TIRE_INSIDE]].X) / 2;
    p2.Y = (prev_car->points[prev_car->desc->front_right_tire[TIRE_OUTSIDE]].Y + prev_car->points[prev_car->desc->front_right_tire[TIRE_INSIDE]].Y) / 2;
    MakeLine(&track1, &p1, &p2);
    if (IntersectionOf(track1, leftCtrlLine) == GM_Intersection &&
        IntersectionOf(p1, map) == GM_None) {
        rightTireCrossLeftLine = true;
    }
    if (IntersectionOf(track1, rightCtrlLine) == GM_Intersection &&
        IntersectionOf(p1, map) == GM_None) {
        rightTireCrossRightLine = true;
    }
}
static bool EnterParking(const Polygon *map, const car_model_cache_t *car) {
    bool succ = false;
    Polygon parking;
    Polygon car_body;
    car_body.num = car->desc->body_num;
    car_body.point = (PointF *) malloc(sizeof(PointF) * car_body.num);
    for (int i = 0; i < car_body.num; ++i) {
        car_body.point[i] = car->points[car->desc->body[i]];
    }
    MakePolygon(&parking, {map->point[2], map->point[3], map->point[4], map->point[5]});
    if (IntersectionOf(&car_body, &parking) == GM_Containment) {
        succ = true;
    }
    CleanPolygon(&parking);
    free(car_body.point);
    return succ;
}
static bool CrashRedLine(const Polygon *map, const car_model_cache_t *car)
{
    bool ret = false;
    Line red_line;
    const int red_lines[][2] = {{0, 7}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}};
    Polygon car_body;
    car_body.num = car->desc->body_num;
    car_body.point = (PointF *) malloc(sizeof(PointF) * car_body.num);
    for (int i = 0; i < car_body.num; ++i) {
        car_body.point[i] = car->points[car->desc->body[i]];
    }
    for (int i = 0; i < sizeof(red_lines) / sizeof(red_lines[0]); ++i) {
        MakeLine(&red_line, &map->point[red_lines[i][0]], &map->point[red_lines[i][1]]);
        if (IntersectionOf(red_line, &car_body) != GM_None) {
            ret = true;
            break;
        }
    }
    free(car_body.point);
    return ret;
}
lib/src/main/cpp/test_items/park_bottom.h
New file
@@ -0,0 +1,18 @@
//
// Created by YY on 2019/10/23.
//
#ifndef RTKDRIVERTEST_PARK_BOTTOM_H
#define RTKDRIVERTEST_PARK_BOTTOM_H
#include "../Geometry.h"
#include "../driver_test.h"
#include <vector>
using namespace std;
void StartParkBottom(void);
void StopParkBottom(void);
int TestParkBottom(vector<int>&err, const Polygon *map, const car_model_cache_t *car, double speed, int run_status);
#endif //RTKDRIVERTEST_PARK_BOTTOM_H
lib/src/main/cpp/test_items/park_edge.cpp
New file
@@ -0,0 +1,274 @@
//
// Created by YY on 2019/10/23.
//
#include "park_edge.h"
#include "../Geometry.h"
#include "../driver_test.h"
#include "../common/apptimer.h"
#include "../native-lib.h"
#include <vector>
using namespace std;
enum {
    ARRIVED_START,
    PARK_CAR,
    START_CAR
};
const int PARK_TIMEOUT = 90;
const uint32_t STOP_CAR_TIME = D_SEC(2);
static int prev_run_status;
static int runStatusBeforeStop;
static uint32_t stopTimepoint = 0;
static bool parkTimeout;
static bool occurCrashRedLine1, occurCrashRedLine2;
static bool PETesting = false;
static int currTarget;
static bool carStopEvent;               // 中途停车标记
static bool carParkSuccess;             // 是否停在库位
static int leaveParkCnt;                // 车辆离开库位刚发生的时,检查是否开启转向灯
static void ParkEdgeTimeout(union sigval sig);
static bool CrashRedLine1(const Polygon *map, const car_model_cache_t *car);
static bool CrashRedLine2(const Polygon *map, const car_model_cache_t *car);
static bool EnterParking(const Polygon *map, const car_model_cache_t *car);
static bool ExitParkArea(const Polygon *map, const car_model_cache_t *car);
static bool ExitTestArea(const Polygon *map, const car_model_cache_t *car);
bool EnterParkEdgeArea(const Polygon *car, const Polygon *tire, const Polygon *map)
{
    if (IntersectionOf(tire->point[0], map) == GM_Containment &&
        IntersectionOf(tire->point[1], map) == GM_Containment &&
        IntersectionOf(tire->point[4], map) == GM_Containment &&
        IntersectionOf(tire->point[5], map) == GM_Containment) {
        return true;
    }
    return false;
}
void StartParkEdge(void)
{
    prev_run_status = 0;
    parkTimeout = false;
    occurCrashRedLine1 = occurCrashRedLine2 = false;        // 这个科目规定特殊点,发生一次扣10分,而不直接淘汰
    PETesting = true;
    currTarget = ARRIVED_START;
}
void StopParkEdge(void)
{
    AppTimer_delete(ParkEdgeTimeout);
    PETesting = false;
}
int TestParkEdge(vector<int>&err, const Polygon *map, const car_model_cache_t *car, double speed, int run_status)
{
    int status = 0;
    if (!PETesting)
        return 0;
    if (currTarget >= PARK_CAR) {
        if (CrashRedLine1(map, car)) {
            if (!occurCrashRedLine1) {
                occurCrashRedLine1 = true;
                // 车轮压边线,每次扣10分
                err.push_back(23);
            }
        } else {
            occurCrashRedLine1 = false;
        }
        if (CrashRedLine2(map, car)) {
            if (!occurCrashRedLine2) {
                occurCrashRedLine2 = true;
                // 车身压库位线,每次扣10分
                err.push_back(24);
            }
        } else {
            occurCrashRedLine2 = false;
        }
        if (parkTimeout) {
            // 超时90秒,不合格
            err.push_back(22);
            status = -1;
        }
        if (prev_run_status != run_status) {
            if (run_status == 0) {
                // 车停了
                runStatusBeforeStop = prev_run_status;
                stopTimepoint = AppTimer_GetTickCount();
            } else {
                // 车动了,且和停车前的运行状态一致
                if (runStatusBeforeStop == run_status && AppTimer_GetTickCount() - stopTimepoint > STOP_CAR_TIME) {
                    // 中途停车,扣5分
                    err.push_back(26);
                }
            }
        }
    }
    if (currTarget == ARRIVED_START) {
        if (ExitTestArea(map, car)) {
            // 直接驶离测试区域
            status = -2;
        } else
        if (run_status < 0) {
            AppTimer_add(ParkEdgeTimeout, D_SEC(PARK_TIMEOUT));
            currTarget = PARK_CAR;
            TextOsd(0, "开始倒库");
        }
    } else if (currTarget == PARK_CAR) {
        if (run_status < 0) {
            carStopEvent = false;
        } else if (run_status == 0) {
            // 立即检查是否停车到位,也许是中途停车,先不管,待发生前进事件后,再断定是否停车到位
            if (!carStopEvent) {
                carStopEvent = true;
                carParkSuccess = EnterParking(map, car);
            }
        } else {
            if (carStopEvent) {
                if (!carParkSuccess) {
                    // 不合格:车身出线
                    err.push_back(21);
                    status = -1;
                }
            }
            carStopEvent = false;
            leaveParkCnt = 0;
            currTarget = START_CAR;
            TextOsd(0, "开始出库");
        }
    } else if (currTarget == START_CAR) {
        if (run_status > 0) {
            leaveParkCnt++;
            if (leaveParkCnt == 1) {
                // 未开启转向灯,扣10分
                err.push_back(25);
            }
            if (ExitParkArea(map, car)) {
                // 项目完成
                status = 1;
                TextOsd(0, "项目完成");
            }
        }
    }
    if (status != 0) {
        StopParkEdge();
    }
    prev_run_status = run_status;
    return status;
}
static void ParkEdgeTimeout(union sigval sig) {
    AppTimer_delete(ParkEdgeTimeout);
    parkTimeout = true;
}
// 车轮是否压道路边线
static bool CrashRedLine1(const Polygon *map, const car_model_cache_t *car)
{
    bool ret = false;
    Line red_line;
    const int red_lines[][2] = {{0, 7}, {1, 2}, {5, 6}};
    Line frontAxle, rearAxle;
    MakeLine(&frontAxle, &car->points[car->desc->front_left_tire[TIRE_OUTSIDE]], &car->points[car->desc->front_right_tire[TIRE_OUTSIDE]]);
    MakeLine(&rearAxle, &car->points[car->desc->rear_left_tire[TIRE_OUTSIDE]], &car->points[car->desc->rear_right_tire[TIRE_OUTSIDE]]);
    for (int i = 0; i < sizeof(red_lines) / sizeof(red_lines[0]); ++i) {
        MakeLine(&red_line, &map->point[red_lines[i][0]], &map->point[red_lines[i][1]]);
        if (IntersectionOf(red_line, frontAxle) == GM_Intersection ||
            IntersectionOf(red_line, rearAxle) == GM_Intersection) {
            ret = true;
            break;
        }
    }
    return ret;
}
// 车身是否压库位线
static bool CrashRedLine2(const Polygon *map, const car_model_cache_t *car)
{
    bool ret = false;
    Line red_line;
    const int red_lines[][2] = {{2, 3}, {3, 4}, {4, 5}};
    Polygon car_body;
    car_body.num = car->desc->body_num;
    car_body.point = (PointF *) malloc(sizeof(PointF) * car_body.num);
    for (int i = 0; i < car_body.num; ++i) {
        car_body.point[i] = car->points[car->desc->body[i]];
    }
    for (int i = 0; i < sizeof(red_lines) / sizeof(red_lines[0]); ++i) {
        MakeLine(&red_line, &map->point[red_lines[i][0]], &map->point[red_lines[i][1]]);
        if (IntersectionOf(red_line, &car_body) != GM_None) {
            ret = true;
            break;
        }
    }
    free(car_body.point);
    return ret;
}
static bool EnterParking(const Polygon *map, const car_model_cache_t *car) {
    bool succ = false;
    Polygon parking;
    Polygon car_body;
    car_body.num = car->desc->body_num;
    car_body.point = (PointF *) malloc(sizeof(PointF) * car_body.num);
    for (int i = 0; i < car_body.num; ++i) {
        car_body.point[i] = car->points[car->desc->body[i]];
    }
    MakePolygon(&parking, {map->point[2], map->point[3], map->point[4], map->point[5]});
    if (IntersectionOf(&car_body, &parking) == GM_Containment) {
        succ = true;
    }
    CleanPolygon(&parking);
    free(car_body.point);
    return succ;
}
// 整个车辆都要驶过前库位线
static bool ExitParkArea(const Polygon *map, const car_model_cache_t *car)
{
    for (int i = 0; i < car->point_num; ++i) {
        if (IntersectionOfLine(map->point[4], map->point[5], car->points[i]) != -1)
            return false;
    }
    return true;
}
// 整个车辆都要驶过最前端控制线
static bool ExitTestArea(const Polygon *map, const car_model_cache_t *car)
{
    for (int i = 0; i < car->point_num; ++i) {
        if (IntersectionOfLine(map->point[6], map->point[7], car->points[i]) != -1)
            return false;
    }
    return true;
}
lib/src/main/cpp/test_items/park_edge.h
New file
@@ -0,0 +1,19 @@
//
// Created by YY on 2019/10/23.
//
#ifndef RTKDRIVERTEST_PARK_EDGE_H
#define RTKDRIVERTEST_PARK_EDGE_H
#include "../Geometry.h"
#include "../driver_test.h"
#include <vector>
using namespace std;
bool EnterParkEdgeArea(const Polygon *car, const Polygon *tire, const Polygon *map);
void StartParkEdge(void);
void StopParkEdge(void);
int TestParkEdge(vector<int>&err, const Polygon *map, const car_model_cache_t *car, double speed, int run_status);
#endif //RTKDRIVERTEST_PARK_EDGE_H
lib/src/main/cpp/test_items/stop_and_start.cpp
New file
@@ -0,0 +1,227 @@
//
// Created by YY on 2019/10/31.
//
#include <cstdlib>
#include <vector>
#include <cmath>
#include "stop_and_start.h"
#include "../driver_test.h"
#include "../jni_log.h"
#include "../common/apptimer.h"
using namespace std;
enum
{
    STOP_CAR,
    START_CAR
};
const double STOP_DISTANCE_THRESHOLD_RED = 0.5;
const double EDGE_DISTANCE_THRESHOLD_RED = 0.5;
const double EDGE_DISTANCE_THRESHOLD_YELLOW = 0.3;
const double SLIDE_DISTANCE_THRESHOLD_RED = 0.3;
const double SLIDE_DISTANCE_THRESHOLD_YELLOW = 0.1;
const int CAR_START_TIMEOUT = 30;
const double EPSILON = 1e-3;
static bool SASTesting = false;
static double slideDistance;
static bool startCarTimeout;
static int currTarget;
static PointF stopPoint;
static int startCarConfirm;             // 起步时,持续前进一小段才算
static void StartCarTimeout(union sigval sig);
static bool CrashRedLine(const Polygon *map, const car_model_cache_t *car);
static double DistanceOfHead2Stopline(const Polygon *map, const car_model_cache_t *car);
static double DistanceOfTire2Edge(const Polygon *map, const car_model_cache_t *car);
static bool ExitTestArea(const Polygon *map, const car_model_cache_t *car);
void StartSAS(void)
{
    SASTesting = true;
    slideDistance = 0.0;
    startCarTimeout = false;
    currTarget = STOP_CAR;
}
void StopSAS(void)
{
    SASTesting = false;
    AppTimer_delete(StartCarTimeout);
}
int TestSAS(vector<int>&err, const Polygon *map, const car_model_cache_t *car, double speed, int run_status)
{
    int status = 0;
    if (!SASTesting)
        return -2;
    if (currTarget >= STOP_CAR) {
        if (CrashRedLine(map, car)) {
            // 车轮压线
            err.push_back(13);
            status = -1;
        }
    }
    if (currTarget == STOP_CAR) {
        if (run_status == 0) {
            double dis1 = DistanceOfHead2Stopline(map, car);
            double dis2 = DistanceOfTire2Edge(map, car);
            if (dis1 > STOP_DISTANCE_THRESHOLD_RED) {
                // 距离停止线前后超出50厘米
                err.push_back(12);
                status = -1;
            } else if (fabs(dis1) > EPSILON) {
                // 前保险没有位于停止带内,但没有超出50厘米
                err.push_back(17);
            }
            if (dis2 > EDGE_DISTANCE_THRESHOLD_RED) {
                // 距离边线超出50厘米
                err.push_back(14);
                status = -1;
            } else if (dis2 > EDGE_DISTANCE_THRESHOLD_YELLOW) {
                // 距离边线超出30厘米
                err.push_back(18);
            }
            // 检查是否拉住手刹
            AppTimer_delete(StartCarTimeout);
            AppTimer_add(StartCarTimeout, D_SEC(CAR_START_TIMEOUT));
            slideDistance = 0.0;
            stopPoint = car->points[0];
            startCarConfirm = 0;
            currTarget = START_CAR;
        } else if (run_status > 0) {
            if (ExitTestArea(map, car)) {
                // 车辆直接驶离测试区,直接淘汰
                err.push_back(12);
                status = -1;
            }
        }
    } else if (currTarget == START_CAR) {
        if (startCarTimeout) {
            startCarTimeout = false;
            //起步时间超过30秒
            err.push_back(15);
            status = -1;
        }
        if (run_status > 0) {
            startCarConfirm++;
            if (startCarConfirm == 2) {
                AppTimer_delete(StartCarTimeout);           // 起步完成
            }
            if (slideDistance > SLIDE_DISTANCE_THRESHOLD_YELLOW) {
                // 后滑超过10厘米,但没超过30厘米
                err.push_back(20);
            }
        } else if (run_status < 0) {
            // 后滑了
            slideDistance = DistanceOf(stopPoint, car->points[0]);
            if (slideDistance > SLIDE_DISTANCE_THRESHOLD_RED) {
                // 后滑超过30厘米
                err.push_back(16);
                status = -1;
            }
        }
        if (ExitTestArea(map, car)) {
            // 测试完成了
            status = 1;
        }
    }
    if (status != 0) {
        StopSAS();
    }
    return status;
}
static void StartCarTimeout(union sigval sig) {
    AppTimer_delete(StartCarTimeout);
    startCarTimeout = true;
}
// 车轮是否压边线
static bool CrashRedLine(const Polygon *map, const car_model_cache_t *car)
{
    bool ret = false;
    Line red_line;
    const int red_lines[][2] = {{0, 8}};
    Line frontAxle, rearAxle;
    MakeLine(&frontAxle, &car->points[car->desc->front_left_tire[TIRE_OUTSIDE]], &car->points[car->desc->front_right_tire[TIRE_OUTSIDE]]);
    MakeLine(&rearAxle, &car->points[car->desc->rear_left_tire[TIRE_OUTSIDE]], &car->points[car->desc->rear_right_tire[TIRE_OUTSIDE]]);
    for (int i = 0; i < sizeof(red_lines) / sizeof(red_lines[0]); ++i) {
        MakeLine(&red_line, &map->point[red_lines[i][0]], &map->point[red_lines[i][1]]);
        if (IntersectionOf(red_line, frontAxle) == GM_Intersection ||
                IntersectionOf(red_line, rearAxle) == GM_Intersection) {
            ret = true;
            break;
        }
    }
    return ret;
}
static double DistanceOfHead2Stopline(const Polygon *map, const car_model_cache_t *car)
{
    double dis = 0.0;
    int rel1 = IntersectionOfLine(map->point[4], map->point[3], car->points[0]);
    int rel2 = IntersectionOfLine(map->point[5], map->point[6], car->points[0]);
    if (rel1 == 1) {
        Line line1;
        MakeLine(&line1, &map->point[4], &map->point[3]);
        dis = DistanceOf(car->points[0], line1);
    } else if (rel2 == -1) {
        Line line2;
        MakeLine(&line2, &map->point[5], &map->point[6]);
        dis = DistanceOf(car->points[0], line2);
    }
    return dis;
}
static double DistanceOfTire2Edge(const Polygon *map, const car_model_cache_t *car)
{
    Line edge;
    MakeLine(&edge,  &map->point[0], &map->point[8]);
    double l1 = DistanceOf(car->points[car->desc->front_right_tire[TIRE_OUTSIDE]], edge);
    double l2 = DistanceOf(car->points[car->desc->rear_right_tire[TIRE_OUTSIDE]], edge);
    return (l1+l2)/2.0;
}
// 整个车辆都要驶离该测试区域
static bool ExitTestArea(const Polygon *map, const car_model_cache_t *car)
{
    for (int i = 0; i < car->point_num; ++i) {
        if (IntersectionOfLine(map->point[8], map->point[7], car->points[i]) != -1)
            return false;
    }
    return true;
}
lib/src/main/cpp/test_items/stop_and_start.h
New file
@@ -0,0 +1,16 @@
//
// Created by YY on 2019/10/31.
//
#ifndef RTKDRIVERTEST_STOP_AND_START_H
#define RTKDRIVERTEST_STOP_AND_START_H
#include "../driver_test.h"
using namespace std;
void StartSAS(void);
void StopSAS(void);
int TestSAS(vector<int>&err, const Polygon *map, const car_model_cache_t *car, double speed, int run_status);
#endif //RTKDRIVERTEST_STOP_AND_START_H
lib/src/main/cpp/test_items/turn_a90.cpp
New file
@@ -0,0 +1,172 @@
//
// Created by YY on 2019/11/4.
//
#include "turn_a90.h"
#include "../Geometry.h"
#include "../driver_test.h"
#include "../common/apptimer.h"
#include "../jni_log.h"
#include <vector>
using namespace std;
enum {
    TURN_ANGLE_90,
    TURN_ANGLE_90_CMP
};
const uint32_t STOP_CAR_TIME = D_SEC(2);
static bool TA90Testing;
static bool carStopEvent;
static bool checked;
static int currTarget;
static int azimuth;
static uint32_t stopTimepoint = 0;
static bool CrashRedLine(const Polygon *map, const car_model_cache_t *car);
static bool ExitTestArea(const Polygon *map, const car_model_cache_t *car);
static bool Turned(const Polygon *map, const car_model_cache_t *car);
void StartTurnA90(double heading)
{
    azimuth = (int) heading;
    checked = false;
    TA90Testing = true;
    carStopEvent = false;
    currTarget = TURN_ANGLE_90;
}
void StopTurnA90(void)
{
    TA90Testing = false;
}
int TestTurnA90(vector<int>&err, const Polygon *map, const car_model_cache_t *car, double speed, int run_status, double heading)
{
    int status = 0;
    if (!TA90Testing)
        return -2;
    DEBUG("TestTurnA90 %d", run_status);
    if (CrashRedLine(map, car)) {
        // 压线了
        err.push_back(29);
        status = -1;
        DEBUG("错误 压线");
    }
    if (run_status != 0) {
        if (carStopEvent)
            DEBUG("TURN_ANGLE_90 停车时间 %ld", AppTimer_GetTickCount() - stopTimepoint);
        if (carStopEvent && AppTimer_GetTickCount() - stopTimepoint > STOP_CAR_TIME) {
            // 中途停车
            err.push_back(31);
            DEBUG("错误 停车1");
        }
        carStopEvent = false;
    } else if (!carStopEvent){
        carStopEvent = true;
        stopTimepoint = AppTimer_GetTickCount();
    }
    if (ExitTestArea(map, car)) {
        // 测试结束
        status = 1;
    }
    if (currTarget == TURN_ANGLE_90) {
        // 转向灯开启
        int az = (int) heading;
        if (abs(az - azimuth) > 180) {
            az = 360 - abs(az-azimuth);
        } else {
            az = abs(az - azimuth);
        }
        if (az >= 10 && !checked) {
            checked = true;
            // 转向灯未开启
            err.push_back(30);
            DEBUG("错误 灯没看");
        }
        if (Turned(map, car)) {
            currTarget = TURN_ANGLE_90_CMP;
            checked = false;
        }
        DEBUG("TURN_ANGLE_90 %d %d",run_status, az);
    } else {
        // 关闭转向灯
        Line line;
        MakeLine(&line, &map->point[1], &map->point[2]);
        // 大于2.5米后检查车灯
        if (!checked && DistanceOf(car->points[0], line) >= 2.5) {
            checked = true;
            // 转向灯未关闭
            err.push_back(30);
            DEBUG("错误 灯没管");
        }
        DEBUG("TURN_ANGLE_90_CMP");
    }
    if (status != 0) {
        StopTurnA90();
    }
    return status;
}
// 车轮是否压边线
static bool CrashRedLine(const Polygon *map, const car_model_cache_t *car)
{
    bool ret = false;
    Line red_line;
    const int red_lines[][2] = {{0, 5}, {5, 4}, {1, 2}, {2, 3}};
    Line frontAxle, rearAxle;
    // 仅看车轮外侧
    MakeLine(&frontAxle, &car->points[car->desc->front_left_tire[TIRE_OUTSIDE]], &car->points[car->desc->front_right_tire[TIRE_OUTSIDE]]);
    MakeLine(&rearAxle, &car->points[car->desc->rear_left_tire[TIRE_OUTSIDE]], &car->points[car->desc->rear_right_tire[TIRE_OUTSIDE]]);
    for (int i = 0; i < sizeof(red_lines) / sizeof(red_lines[0]); ++i) {
        MakeLine(&red_line, &map->point[red_lines[i][0]], &map->point[red_lines[i][1]]);
        if (IntersectionOf(red_line, frontAxle) == GM_Intersection ||
            IntersectionOf(red_line, rearAxle) == GM_Intersection) {
            ret = true;
            break;
        }
    }
    return ret;
}
// 整个车辆都要驶离该测试区域
static bool ExitTestArea(const Polygon *map, const car_model_cache_t *car)
{
    for (int i = 0; i < car->point_num; ++i) {
        if (IntersectionOfLine(map->point[3], map->point[4], car->points[i]) != 1)
            return false;
    }
    return true;
}
static bool Turned(const Polygon *map, const car_model_cache_t *car)
{
    for (int i = 0; i < car->point_num; ++i) {
        if (IntersectionOfLine(map->point[1], map->point[2], car->points[i]) != 1)
            return false;
    }
    return true;
}
lib/src/main/cpp/test_items/turn_a90.h
New file
@@ -0,0 +1,17 @@
//
// Created by YY on 2019/11/4.
//
#ifndef RTKDRIVERTEST_TURN_A90_H
#define RTKDRIVERTEST_TURN_A90_H
#include "../driver_test.h"
#include <vector>
using namespace std;
void StartTurnA90(double heading);
void StopTurnA90(void);
int TestTurnA90(vector<int>&err, const Polygon *map, const car_model_cache_t *car, double speed, int run_status, double heading);
#endif //RTKDRIVERTEST_TURN_A90_H
lib/src/main/cpp/utils/crc16.cpp
New file
@@ -0,0 +1,66 @@
//
// Created by YY on 2019/9/10.
//
#include <cstdio>
#include "crc16.h"
const static unsigned short crc_table [256] = {
        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5,
        0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b,
        0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
        0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
        0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c,
        0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
        0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
        0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
        0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
        0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
        0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5,
        0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
        0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969,
        0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
        0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
        0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
        0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03,
        0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
        0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6,
        0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
        0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
        0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb,
        0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1,
        0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
        0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,
        0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
        0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
        0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
        0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447,
        0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
        0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2,
        0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
        0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
        0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827,
        0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
        0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
        0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0,
        0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d,
        0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
        0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
        0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba,
        0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
        0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
unsigned short CRCCCITT(unsigned char *data, size_t length, unsigned short seed, unsigned short final)
{
    size_t count;
    unsigned int crc = seed;
    unsigned int temp;
    for (count = 0; count < length; ++count) {
        temp = (*data++ ^ (crc >> 8)) & 0xff;
        crc = crc_table[temp] ^ (crc << 8);
    }
    return (unsigned short)(crc ^ final);
}
lib/src/main/cpp/utils/crc16.h
New file
@@ -0,0 +1,12 @@
//
// Created by YY on 2019/9/10.
//
#ifndef ANYUNPROJECT_CRC16_H
#define ANYUNPROJECT_CRC16_H
#include <cstdio>
unsigned short CRCCCITT(unsigned char *data, size_t length, unsigned short seed, unsigned short final);
#endif //ANYUNPROJECT_CRC16_H
lib/src/main/cpp/utils/num.cpp
New file
@@ -0,0 +1,76 @@
//
// Created by YY on 2019/12/23.
//
#include "num.h"
long str2int(const uint8_t *s, uint16_t length)
{
    uint16_t i;
    long n = 0;
    bool sign = false;
    for(i = 0; i < length; i++)
    {
        if(s[i] >= '0' && s[i] <= '9') {
            n = n*10 + s[i] - '0';
        } else if (i == 0 && s[i]=='-') {
            sign = true;
        }
    }
    if (sign) {
        n = 0-n;
    }
    return n;
}
bool str2float(double *f, const uint8_t *s, uint16_t length)
{
    bool sign = false;
    bool getDecimal = false;
    uint16_t i;
    uint64_t m = 0, n = 1;
    double x;
    *f = 0.0;
    for(i = 0; i < length; i++)
    {
        if(s[i] == '-' || s[i] == '+') {
            if(i == 0) {
                if (s[i] == '-') {
                    sign = true;
                }
            }
            else {
                return false;
            }
        }
        else if(s[i] == '.') {
            if(getDecimal == true) {
                return false;
            }
            getDecimal = true;
        }
        else if(s[i] >= '0' && s[i] <= '9') {
            m = m*10 + s[i] - '0';
            if(getDecimal == true) {
                n *= 10;
            }
        }
        else {
            return false;
        }
    }
    x = (double)m / (double)n;
    if(sign) {
        x = 0.0 - x;
    }
    *f = x;
    return true;
}
lib/src/main/cpp/utils/num.h
New file
@@ -0,0 +1,13 @@
//
// Created by YY on 2019/12/23.
//
#ifndef RTKDRIVERTEST_NUM_H
#define RTKDRIVERTEST_NUM_H
#include <cstdint>
long str2int(const uint8_t *s, uint16_t length);
bool str2float(double *f, const uint8_t *s, uint16_t length);
#endif //RTKDRIVERTEST_NUM_H
lib/src/main/java/com/anyun/exam/lib/RemoteService.java
@@ -2,10 +2,15 @@
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Base64;
import android.util.Log;
import com.anyun.exam.lib.util.DESUtil;
import com.anyun.exam.lib.util.NetUtils;
import androidx.annotation.Nullable;
@@ -18,8 +23,8 @@
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class RemoteService extends Service {
    private static final String TAG = "RemoteService";
    private final static String LOAD_KEY = "RTKBaseStation";
    /**服务是否销毁标志*/
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
    private RemoteCallbackList<IListenerInterface> mListenerList = new RemoteCallbackList();
@@ -54,6 +59,7 @@
    public void onCreate() {
        super.onCreate();
        Log.i(TAG,"onCreate()");
        startNative();
        new Thread(new Worker()).start();
    }
@@ -95,4 +101,71 @@
            mListenerList.finishBroadcast();
        }
    }
    public String javaGetImei() {
        return NetUtils.getDeviceId(getApplicationContext());
    }
    public String javaDESEncrypt(String plaintext, String key) {
        try {
            byte []des = DESUtil.encrypt(plaintext.getBytes("utf-8"), key);
            return Base64.encodeToString(des, Base64.DEFAULT);
        } catch (Exception e) {
        }
        return null;
    }
    public byte[] javaDESEncrypt(byte []plaintext, byte []key) {
        try {
            return DESUtil.encrypt(plaintext, key);
        } catch (Exception e) {
        }
        return null;
    }
    public void SetPlatformKey(byte []key) {
        String params = Base64.encodeToString(key, Base64.DEFAULT);
        SharedPreferences sharedPreferences = getSharedPreferences(LOAD_KEY, MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("platform_key", params);
        editor.commit();
    }
    public byte[] GetPlatformKey() {
        SharedPreferences sharedPreferences = getSharedPreferences(LOAD_KEY, MODE_PRIVATE);
        String params = sharedPreferences.getString("platform_key", "");
        if (params.equals("")) {
            return null;
        }
        return Base64.decode(params, Base64.DEFAULT);
    }
    public void DeletePlatformKey() {
        SharedPreferences sharedPreferences = getSharedPreferences(LOAD_KEY, MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("platform_key", "");
        editor.commit();
    }
    public void SetSharedValue(String key, int value) {
        SharedPreferences sharedPreferences = getSharedPreferences(LOAD_KEY, MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putInt(key, value);
        editor.commit();
    }
    public int GetSharedValue(String key) {
        SharedPreferences sharedPreferences = getSharedPreferences(LOAD_KEY, MODE_PRIVATE);
        return sharedPreferences.getInt(key, 0);
    }
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
    public native void startNative();
}
lib/src/main/java/com/anyun/exam/lib/util/ByteUtil.java
New file
@@ -0,0 +1,16 @@
package com.anyun.exam.lib.util;
public class ByteUtil {
    public static String byte2hex(byte [] buffer){
        StringBuilder h = new StringBuilder();
        for(int i = 0; i < buffer.length; i++){
            String temp = Integer.toHexString(buffer[i] & 0xFF).toUpperCase();
            if(temp.length() == 1){
                temp = "0" + temp;
            }
            h.append(temp);
            h.append(" ");
        }
        return h.toString();
    }
}
lib/src/main/java/com/anyun/exam/lib/util/DESUtil.java
New file
@@ -0,0 +1,124 @@
package com.anyun.exam.lib.util;
import android.util.Base64;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class DESUtil {
    private static String password = "safeluck";
    /**
     *
     * @Method: encrypt
     * @Description: 加密数据
     * @param data
     * @return
     * @throws Exception
     * @date 2016年7月26日
     */
    public static String encrypt(String data, String pwd) {  //对string进行BASE64Encoder转换
        byte[] bt = encryptByKey(data.getBytes(), pwd);
        return Base64.encodeToString(bt, Base64.DEFAULT);
    }
    public static byte[] encrypt(byte[] message, String key) throws Exception {
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
        IvParameterSpec iv = new IvParameterSpec(key.getBytes("UTF-8"));
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        return cipher.doFinal(message);
    }
    public static byte[] encrypt(byte[] message, byte[] key) throws Exception {
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        DESKeySpec desKeySpec = new DESKeySpec(key);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
        IvParameterSpec iv = new IvParameterSpec(key);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        return cipher.doFinal(message);
    }
    public static byte[] decrypt2(byte[] message, String key) throws Exception {
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
        IvParameterSpec iv = new IvParameterSpec(key.getBytes("UTF-8"));
        cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
        return cipher.doFinal(message);
    }
    /**
     *
     * @Method: encrypt
     * @Description: 解密数据
     * @param data
     * @return
     * @throws Exception
     * @date 2016年7月26日
     */
    public static String decryptor(String data, String pwd) throws Exception {  //对string进行BASE64Encoder转换
        byte[] bt = decrypt(Base64.decode(data, Base64.DEFAULT), pwd);
        String strs = new String(bt);
        return strs;
    }
    /**
     * 加密
     * @param datasource byte[]
     * @param password String
     * @return byte[]
     */
    private static byte[] encryptByKey(byte[] datasource, String key) {
        try{
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(key.getBytes());
            //创建一个密匙工厂,然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            //Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            //用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey);
            //现在,获取数据并加密
            //正式执行加密操作
            return cipher.doFinal(datasource);
        }catch(Throwable e){
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 解密
     * @param src byte[]
     * @param password String
     * @return byte[]
     * @throws Exception
     */
    private static byte[] decrypt(byte[] src, String key) throws Exception {
        // DES算法要求有一个可信任的随机数源
        SecureRandom random = new SecureRandom();
        // 创建一个DESKeySpec对象
        DESKeySpec desKey = new DESKeySpec(key.getBytes());
        // 创建一个密匙工厂
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        // 将DESKeySpec对象转换成SecretKey对象
        SecretKey securekey = keyFactory.generateSecret(desKey);
        // Cipher对象实际完成解密操作
        Cipher cipher = Cipher.getInstance("DES");
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, securekey, random);
        // 真正开始解密操作
        return cipher.doFinal(src);
    }
}
lib/src/main/java/com/anyun/exam/lib/util/NetUtils.java
New file
@@ -0,0 +1,221 @@
package com.anyun.exam.lib.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.telephony.TelephonyManager;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
 * Created by YY on 2017/10/27.
 */
public class NetUtils {
    /**
     * 判断网络情况
     *
     * @param context  上下文
     * @return false 表示没有网络 true 表示有网络
     */
    public static boolean isNetworkAvalible(Context context) {
        // 获得网络状态管理器
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivityManager == null) {
            return false;
        } else {
            // 建立网络数组
            NetworkInfo[] net_info = connectivityManager.getAllNetworkInfo();
            if (net_info != null) {
                for (int i = 0; i < net_info.length; i++) {
                    // 判断获得的网络状态是否是处于连接状态
                    if (net_info[i].getState() == State.CONNECTED) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    /**
     * 判断网络是否连接
     **/
    public static boolean netState(Context context) {
        ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        // 获取代表联网状态的NetWorkInfo对象
        NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
        // 获取当前的网络连接是否可用
        boolean available = false;
        try {
            available = networkInfo.isAvailable();
        } catch (Exception e) {
//            e.printStackTrace();
            return false;
        }
        if (available) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * 在连接到网络基础之上,判断设备是否是SIM网络连接
     *
     * @param context
     * @return
     */
    public static boolean IsMobileNetConnect(Context context) {
        try {
            ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            State state = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
            if (State.CONNECTED == state)
                return true;
            else
                return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    //返回值 -1:没有网络  1:WIFI网络2:wap网络3:net网络
    public static int GetNetype(Context context) {
        int netType = -1;
        ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if(networkInfo==null) {
            return netType;
        }
        int nType = networkInfo.getType();
        if(nType== ConnectivityManager.TYPE_MOBILE) {
            if(networkInfo.getExtraInfo().toLowerCase().equals("cmnet")) {
                netType = 3;
            }
            else {
                netType = 2;
            }
        }
        else if(nType== ConnectivityManager.TYPE_WIFI) {
            netType = 1;
        }
        return netType;
    }
    public static boolean hasSimCard(Context context) {
        TelephonyManager telMgr = (TelephonyManager)
                context.getSystemService(Context.TELEPHONY_SERVICE);
        boolean result = true;
        switch (telMgr.getSimState()) {
            case TelephonyManager.SIM_STATE_ABSENT:
                result = false; // 没有SIM卡
                break;
            case TelephonyManager.SIM_STATE_UNKNOWN:
                result = false;
                break;
        }
        return result;
    }
    public static TelephonyManager getTelephonyManager(Context context) {
        // 获取telephony系统服务,用于取得SIM卡和网络相关信息
        TelephonyManager mTelephonyManager = (TelephonyManager) context
                    .getSystemService(Context.TELEPHONY_SERVICE);
        return mTelephonyManager;
    }
    public static String getIccid(Context context) {
        TelephonyManager mTelephonyManager = (TelephonyManager) context
                .getSystemService(Context.TELEPHONY_SERVICE);
        try {
            return mTelephonyManager.getSimSerialNumber();
        } catch (SecurityException e) {
        }
        return null;
    }
    /**
     * 唯一的设备ID: GSM手机的 IMEI 和 CDMA手机的 MEID. Return null if device ID is not
     * 取得手机IMEI
     * available.
     */
    public static String getDeviceId(Context context) {
        String mDeviceId = getTelephonyManager(context).getImei();// String
        return mDeviceId;
    }
    /**
     * 取得IMEI SV
     * 设备的软件版本号: 返回移动终端的软件版本,例如:GSM手机的IMEI/SV码。 例如:the IMEI/SV(software version)
     * for GSM phones. Return null if the software version is not available.
     */
    public static String getDeviceSoftwareVersion(Context context) {
        String mDeviceSoftwareVersion = getTelephonyManager(context).getDeviceSoftwareVersion();// String
        return mDeviceSoftwareVersion;
    }
    /**
     * 取得手机IMSI
     * 返回用户唯一标识,比如GSM网络的IMSI编号 唯一的用户ID: 例如:IMSI(国际移动用户识别码) for a GSM phone.
     * 需要权限:READ_PHONE_STATE
     */
    public static String getSubscriberId(Context context) {
        String mSubscriberId = getTelephonyManager(context).getSubscriberId();// String
        return mSubscriberId;
    }
    public static String getIPAddress(Context context) {
        NetworkInfo info = ((ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
        if (info != null && info.isConnected()) {
            if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//当前使用2G/3G/4G网络
                try {
                    //Enumeration<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();
                    for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                        NetworkInterface intf = en.nextElement();
                        for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                            InetAddress inetAddress = enumIpAddr.nextElement();
                            if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
                                return inetAddress.getHostAddress();
                            }
                        }
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                }
            } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//当前使用无线网络
                WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
                WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                String ipAddress = intIP2StringIP(wifiInfo.getIpAddress());//得到IPV4地址
                return ipAddress;
            }
        } else {
            //当前无网络连接,请在设置中打开网络
        }
        return null;
    }
    /**
     * 将得到的int类型的IP转换为String类型
     *
     * @param ip
     * @return
     */
    public static String intIP2StringIP(int ip) {
        return (ip & 0xFF) + "." +
                ((ip >> 8) & 0xFF) + "." +
                ((ip >> 16) & 0xFF) + "." +
                (ip >> 24 & 0xFF);
    }
}
lib/src/main/java/com/anyun/exam/lib/util/Speaker.java
New file
@@ -0,0 +1,44 @@
package com.anyun.exam.lib.util;
import android.content.Context;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import java.util.Locale;
public class Speaker {
    private Context context;
    private TextToSpeech tts;
    final public String TAG = Speaker.class.getCanonicalName();
    public Speaker(final Context context) {
        // TODO Auto-generated constructor stub
        this.context = context;
        tts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
                Log.d(TAG, "onInit TextToSpeech.SUCCESS");
                // TODO Auto-generated method stub
                if (status == TextToSpeech.SUCCESS)
                {
                    Log.d(TAG, "TextToSpeech.SUCCESS");
                    int result = tts.setLanguage(Locale.CHINA);
                    if (result == TextToSpeech.LANG_MISSING_DATA
                            || result == TextToSpeech.LANG_NOT_SUPPORTED)
                    {
                        Log.d(TAG, "TextToSpeech.LANG_NOT_SUPPORTED");
                    }
                }
            }
        });
    }
    public void speak(String text) {
        Log.d(TAG, "SPEAK");
//            tts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
        tts.speak(text, TextToSpeech.QUEUE_ADD, null, "speech");
    }
}