Browse Source

新增日志功能
1. 支持设置输出日志等级
2. 支持JNI层日志调用Java层日志,统一日志入口,方便后续对接本地文件或者日志上报到服务端预留入口

GrayCarbon 1 month ago
parent
commit
1bce89b07f

+ 89 - 18
transport/src/main/cpp/comm_can.cpp

@@ -14,16 +14,78 @@
 #include <iomanip>
 
 // 定义JNI层通信TAG
-#define TAG "native_iscs_can"
+const char *TAG = "ISCS_CAN_NATIVE";
 
-#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,  TAG, __VA_ARGS__)
-#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
 
-static void throwIOException(JNIEnv *env, const char *msg) {
-    jclass ex = env->FindClass("java/io/IOException");
-    if (ex) env->ThrowNew(ex, msg);
+/**
+ * 打印常规级别的日志
+ *
+ * @param env
+ * @param msg
+ */
+void log_i(JNIEnv *env, const char *fmt, ...) {
+    // 获取类
+    jclass clazz = env->FindClass("com/iscs/comm/utils/ISCSLog");
+    if (clazz == nullptr) return;
+
+    // 获取静态方法
+    jmethodID methodId = env->GetStaticMethodID(clazz, "i", "(Ljava/lang/String;Ljava/lang/String;)V");
+    if (methodId == nullptr) return;
+
+    // 转换需要传递的数据
+    // 创建缓存区域
+    char buffer[1024];
+    // 获取参数列表
+    va_list args;
+    va_start(args, fmt);
+    vsnprintf(buffer, sizeof(buffer), fmt, args);
+    va_end(args);
+
+    jstring msg = env->NewStringUTF(buffer);
+
+    // 调用静态方法传递参数
+    env->CallStaticVoidMethod(clazz, methodId, env->NewStringUTF(TAG), msg);
+
+    // 资源释放
+    env->DeleteLocalRef(clazz);
+    env->DeleteLocalRef(msg);
+}
+
+/**
+ * 打印错误级别的日志
+ *
+ * @param env
+ * @param msg
+ */
+void log_e(JNIEnv *env, const char *fmt, ...) {
+    // 获取类
+    jclass clazz = env->FindClass("com/iscs/comm/utils/ISCSLog");
+    if (clazz == nullptr) return;
+
+    // 获取静态方法
+    jmethodID methodId = env->GetStaticMethodID(clazz, "e", "(Ljava/lang/String;Ljava/lang/String;)V");
+    if (methodId == nullptr) return;
+
+    // 转换需要传递的数据
+    // 创建缓存区域
+    char buffer[1024];
+    // 获取参数列表
+    va_list args;
+    va_start(args, fmt);
+    vsnprintf(buffer, sizeof(buffer), fmt, args);
+    va_end(args);
+
+    jstring msg = env->NewStringUTF(buffer);
+
+    // 调用静态方法传递参数
+    env->CallStaticVoidMethod(clazz, methodId, env->NewStringUTF(TAG), msg);
+
+    // 资源释放
+    env->DeleteLocalRef(clazz);
+    env->DeleteLocalRef(msg);
 }
 
+
 /**
  * 将字节数组转化为16进制字符串
  *
@@ -55,6 +117,15 @@ std::string bytesToHexString(JNIEnv *env, jbyteArray array, bool uppercase = tru
     return oss.str();
 }
 
+/**
+ * 字符串拼接操作
+ *
+ * @param env
+ * @param can_id
+ * @param jdata
+ * @param len
+ * @return
+ */
 jbyteArray append_can_id_to_data(JNIEnv *env, jint can_id, const void *jdata, jint len) {
     if (env == nullptr || jdata == nullptr || len <= 0) {
         return nullptr;
@@ -85,11 +156,11 @@ jbyteArray append_can_id_to_data(JNIEnv *env, jint can_id, const void *jdata, ji
 extern "C" JNIEXPORT jint JNICALL
 Java_com_iscs_comm_jni_NativeCan_canOpen(JNIEnv *env, jobject thiz, jstring can_port) {
     const char *can_name = env->GetStringUTFChars(can_port, nullptr);
-    LOGI("CAN ---> opening %s", can_name);
+    log_i(env, "CAN ---> opening %s", can_name);
 
     int s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
     if (s < 0) {
-        LOGE("CAN ---> socket failed: %s", strerror(errno));
+        log_e(env, "CAN ---> socket failed: %s", strerror(errno));
         env->ReleaseStringUTFChars(can_port, can_name);
         return -1;
     }
@@ -97,7 +168,7 @@ Java_com_iscs_comm_jni_NativeCan_canOpen(JNIEnv *env, jobject thiz, jstring can_
     struct ifreq ifr{};
     strncpy(ifr.ifr_name, can_name, IFNAMSIZ - 1);
     if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
-        LOGE("CAN ---> ioctl(SIOCGIFINDEX) failed: %s", strerror(errno));
+        log_e(env, "CAN ---> ioctl(SIOCGIFINDEX) failed: %s", strerror(errno));
         close(s);
         env->ReleaseStringUTFChars(can_port, can_name);
         return -2;
@@ -108,7 +179,7 @@ Java_com_iscs_comm_jni_NativeCan_canOpen(JNIEnv *env, jobject thiz, jstring can_
     addr.can_ifindex = ifr.ifr_ifindex;
 
     if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        LOGE("CAN ---> bind %s failed: %s", can_name, strerror(errno));
+        log_e(env, "CAN ---> bind %s failed: %s", can_name, strerror(errno));
         close(s);
         env->ReleaseStringUTFChars(can_port, can_name);
         return -3;
@@ -117,7 +188,7 @@ Java_com_iscs_comm_jni_NativeCan_canOpen(JNIEnv *env, jobject thiz, jstring can_
     // 可选: 非阻塞模式
     // fcntl(s, F_SETFL, O_NONBLOCK);
 
-    LOGI("CAN ---> opened  %s port_id %d", can_name, s);
+    log_i(env, "CAN ---> opened  %s port_id %d", can_name, s);
     env->ReleaseStringUTFChars(can_port, can_name);
     return s;
 }
@@ -131,7 +202,7 @@ Java_com_iscs_comm_jni_NativeCan_canOpen(JNIEnv *env, jobject thiz, jstring can_
  */
 extern "C" JNIEXPORT jint JNICALL
 Java_com_iscs_comm_jni_NativeCan_canClose(JNIEnv *env, jobject thiz, jint fd) {
-    LOGI("CAN ---> close %d", fd);
+    log_i(env, "CAN ---> close %d", fd);
     if (fd >= 0) close(fd);
     return 0;
 }
@@ -166,7 +237,7 @@ Java_com_iscs_comm_jni_NativeCan_canWrite(
         if (is_rtr) f.can_id |= CAN_RTR_FLAG;
         f.can_dlc = (uint8_t) ((len > 8) ? 8 : len);
         memcpy(f.data, data, f.can_dlc);
-        LOGI("CAN ---> %s", bytesToHexString(env, append_can_id_to_data(env, f.can_id, f.data, f.can_dlc)).c_str());
+        log_i(env, "CAN ---> %s", bytesToHexString(env, append_can_id_to_data(env, f.can_id, f.data, f.can_dlc)).c_str());
         ret = write(fd, &f, sizeof(f));
     } else {
         struct canfd_frame f{};
@@ -175,11 +246,11 @@ Java_com_iscs_comm_jni_NativeCan_canWrite(
         if (is_rtr) f.can_id |= CAN_RTR_FLAG;
         f.len = (uint8_t) ((len > 64) ? 64 : len);
         memcpy(f.data, data, f.len);
-        LOGI("CAN ---> %s", bytesToHexString(env, append_can_id_to_data(env, f.can_id, f.data, f.len)).c_str());
+        log_i(env, "CAN ---> %s", bytesToHexString(env, append_can_id_to_data(env, f.can_id, f.data, f.len)).c_str());
         ret = write(fd, &f, sizeof(f));
     }
 
-    if (ret < 0) LOGE("CAN ---> write failed: %s", strerror(errno));
+    if (ret < 0) log_e(env, "CAN ---> write failed: %s", strerror(errno));
 
     env->ReleaseByteArrayElements(jdata, data, JNI_ABORT);
     return ret;
@@ -198,18 +269,18 @@ Java_com_iscs_comm_jni_NativeCan_canRead(JNIEnv *env, jobject thiz, jint fd, jin
     struct pollfd pfd{.fd = fd, .events = POLLIN, .revents = 0};
     int pr = poll(&pfd, 1, timeout_millis);  // 确保 timeoutMs > 0
     if (pr <= 0) {
-        if (pr < 0) LOGE("CAN ---> poll error: %s", strerror(errno));  // 打印 poll 错误
+        if (pr < 0) log_e(env, "CAN ---> poll error: %s", strerror(errno));  // 打印 poll 错误
         return env->NewByteArray(0);  // 没有数据或超时
     }
 
     struct can_frame f{};
     int r = read(fd, &f, sizeof(f));  // 读取 CAN 帧
     if (r < 0) {
-        LOGE("CAN ---> read failed: %s", strerror(errno));  // 读取失败的错误日志
+        log_e(env, "CAN ---> read failed: %s", strerror(errno));  // 读取失败的错误日志
         return env->NewByteArray(0);  // 返回空数组表示没有数据
     }
 
     jbyteArray out = append_can_id_to_data(env, f.can_id, f.data, f.can_dlc);
-    LOGI("CAN <--- %s", bytesToHexString(env, out).c_str());
+    log_i(env, "CAN <--- %s", bytesToHexString(env, out).c_str());
     return out;
 }

+ 0 - 2
transport/src/main/java/com/iscs/comm/entity/device/status/DeviceStatusKeySlot.kt

@@ -30,7 +30,6 @@ class DeviceStatusKeySlot : DeviceStatus() {
             // 获取指定位置数据
             val dataL = frame.data[9].toBinaryString()
             val dataH = frame.data[10].toBinaryString()
-            // Log.d("xiaoming","检查钥匙仓位是否有设备 L = $dataL H = $dataH")
             for (ch in 0..1) {
                 val data = if (ch == 0) dataL else dataH
                 val isNotEmpty = data[7] == '1'
@@ -45,7 +44,6 @@ class DeviceStatusKeySlot : DeviceStatus() {
             // 获取指定位置数据
             val dataL = frame.data[9].toBinaryString()
             val dataH = frame.data[10].toBinaryString()
-            // Log.d("xiaoming","检查钥匙仓位工作状态 L = $dataL H = $dataH")
             for (ch in 0..1) {
                 when (ch) {
                     // 左边仓位

+ 0 - 2
transport/src/main/java/com/iscs/comm/entity/device/status/DeviceStatusLockSlot.kt

@@ -30,7 +30,6 @@ class DeviceStatusLockSlot() : DeviceStatus() {
         if (frame.data.size >= 9) {
             // 获取指定位置数据
             val data = frame.data[9].toBinaryString()
-            // Log.d("xiaoming", "$data")
             for (i in 7 downTo 3) {
                 val ch = 7 - i
                 val isNotEmpty = data[i] == '1'
@@ -45,7 +44,6 @@ class DeviceStatusLockSlot() : DeviceStatus() {
             // 获取指定位置数据
             val dataL = frame.data[9].toBinaryString()
             val dataH = frame.data[10].toBinaryString()
-            // Log.d("xiaoming", "$dataL $dataH")
             for (i in 7 downTo 3) {
                 val ch = 7 - i
                 val isSlotLock = dataL[i] == '1'

+ 0 - 1
transport/src/main/java/com/iscs/comm/extension/DeviceFactoryExt.kt

@@ -10,7 +10,6 @@ import com.iscs.comm.entity.device.DeviceLockSlot
  * 通过数据帧构建设备类型
  */
 fun Frame.factoryDevice(): Device {
-    // Log.d("xiaoming", "构建设备 CAN_ID = ${this.cmd.toString(16)} DATA = ${this.data.toHexString()}")
     val nodeId = this.data.copyOfRange(6, 8).byte2ToInt()
     val subNodeId = this.data[8].toInt()
     val type = this.data.copyOfRange(9, 11).byte2ToInt()

+ 5 - 2
transport/src/main/java/com/iscs/comm/manager/CanManager.kt

@@ -1,12 +1,12 @@
 package com.iscs.comm.manager
 
-import android.util.Log
 import com.iscs.comm.entity.Frame
 import com.iscs.comm.enums.CommType
 import com.iscs.comm.extension.byte2ToInt
 import com.iscs.comm.extension.byte4ToInt
 import com.iscs.comm.intf.AbsCommBase
 import com.iscs.comm.jni.NativeCan
+import com.iscs.comm.utils.ISCSLog
 import com.iscs.comm.utils.ShellTools
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Dispatchers
@@ -23,6 +23,9 @@ import java.util.concurrent.ConcurrentHashMap
 class CanManager(private val port: String) : AbsCommBase() {
 
     companion object {
+
+        private const val TAG = "CanManager"
+
         // CAN总线响应码的起始
         private const val CAN_RESPONSE_INDEX = 1408
     }
@@ -67,7 +70,7 @@ class CanManager(private val port: String) : AbsCommBase() {
             isOpened = false
             close()
             // 这里通知外部CAN连接中断了,重连操作交给外部使用
-            Log.d("xiaoming", "CAN disconnected")
+            ISCSLog.e(TAG, "CAN write failed, execute close")
         }
     }
 

+ 66 - 0
transport/src/main/java/com/iscs/comm/utils/ISCSLog.kt

@@ -0,0 +1,66 @@
+package com.iscs.comm.utils
+
+import android.util.Log
+import com.iscs.comm.utils.TimeTools.formatTimestamp
+
+/**
+ * 用于SDK层Log输出
+ */
+object ISCSLog {
+
+    private const val TAG = "ISCSLog"
+
+    // log输出等级,默认不输出
+    private var level: LogLevel = LogLevel.ALL
+
+    /**
+     * 常规级别的日志输出
+     *
+     * @param tag 日志标记
+     * @param msg 输出的内容
+     */
+    @JvmStatic
+    fun i(tag: String = "", msg: String) {
+        if (level != LogLevel.INFO && level != LogLevel.ALL) return
+        val time = formatTimestamp(System.currentTimeMillis())
+        Log.i(TAG, "I $time $tag $msg")
+    }
+
+    /**
+     * 警告级别的日志输出
+     *
+     * @param tag 日志标记
+     * @param msg 输出的内容
+     */
+    @JvmStatic
+    fun w(tag: String, msg: String) {
+        if (level != LogLevel.WARNING && level != LogLevel.ALL) return
+        val time = formatTimestamp(System.currentTimeMillis())
+        Log.w(TAG, "W $time $tag $msg")
+    }
+
+    /**
+     * 错误级别的日志输出
+     *
+     * @param tag 日志标记
+     * @param msg 输出的内容
+     */
+    @JvmStatic
+    fun e(tag: String, msg: String) {
+        if (level != LogLevel.ERROR && level != LogLevel.ALL) return
+        val time = formatTimestamp(System.currentTimeMillis())
+        Log.e(TAG, "E $time $tag $msg")
+    }
+
+}
+
+/**
+ * 日志等级
+ */
+enum class LogLevel {
+    NONE,       // 无日志输出
+    ALL,        // 全日志输出
+    INFO,       // 常规级别日志输出
+    WARNING,    // 告警级别日志输出
+    ERROR,      // 错误级别日志输出
+}

+ 23 - 0
transport/src/main/java/com/iscs/comm/utils/TimeTools.kt

@@ -0,0 +1,23 @@
+package com.iscs.comm.utils
+
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+/**
+ * 时间格式化工具
+ */
+object TimeTools {
+
+    /**
+     * 格式化时间戳
+     *
+     * @param timestamp     需要格式化的时间戳
+     * @param pattern       格式化规则
+     */
+    fun formatTimestamp(timestamp: Long, pattern: String = "yyyy-MM-dd HH:mm:ss:SSS"): String {
+        val formatter = SimpleDateFormat(pattern, Locale.getDefault())
+        return formatter.format(Date(timestamp))
+    }
+
+}