refactor: merge protocol cores as SETProtocol
This commit is contained in:
23
c/set-protocol/ports/android/Android.mk
Normal file
23
c/set-protocol/ports/android/Android.mk
Normal file
@@ -0,0 +1,23 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := setprotocol
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../include
|
||||
LOCAL_SRC_FILES := \
|
||||
../../src/set_protocol.c \
|
||||
../../src/set_can.c \
|
||||
../../src/set_firmware.c \
|
||||
../../src/set_telemetry.c \
|
||||
../../src/gui_catalog.c \
|
||||
../../src/gui_frame.c \
|
||||
../../src/pcan_abi.c \
|
||||
../../src/pcan_crc.c \
|
||||
../../src/pcan_frame.c \
|
||||
../../src/pcan_id.c \
|
||||
../../src/pcan_link.c \
|
||||
../../src/pcan_ring.c \
|
||||
../../src/pcan_gas.c \
|
||||
setprotocol_jni.c
|
||||
LOCAL_CFLAGS := -std=c99 -Wall -Wextra -Wpedantic -fvisibility=hidden
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
10
c/set-protocol/ports/android/README.md
Normal file
10
c/set-protocol/ports/android/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Android port
|
||||
|
||||
`Android.mk` builds `libsetprotocol.so` from the same C99 sources used by MCU
|
||||
and desktop builds. `setprotocol_jni.c` contains only JNI marshalling; protocol rules
|
||||
remain in the core. Add the Kotlin directory as an Android source directory
|
||||
and include this `Android.mk` from the application NDK build.
|
||||
|
||||
The JNI streaming parser returns fixed 15-byte records
|
||||
`SEQ | FLAGS | CAN_ID_LE | DLC | DATA[8]`. Dynamic allocation is confined to
|
||||
the Android adapter; the portable core remains allocation free.
|
||||
@@ -0,0 +1,42 @@
|
||||
package ru.setcorp.setprotocol
|
||||
|
||||
/** Thin Kotlin facade over the shared C99 SETProtocol core. */
|
||||
object NativeSetProtocol {
|
||||
val available: Boolean by lazy {
|
||||
runCatching {
|
||||
System.loadLibrary("setprotocol")
|
||||
nativeAbiVersion() == 1
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
external fun nativeAbiVersion(): Int
|
||||
external fun nativePackId(
|
||||
priority: Int,
|
||||
route: Int,
|
||||
deviceType: Int,
|
||||
deviceId: Int,
|
||||
messageType: Int,
|
||||
body: Int,
|
||||
): Long
|
||||
external fun nativeUnpackId(raw: Long): IntArray?
|
||||
external fun nativeCrc16(input: ByteArray): Int
|
||||
external fun nativeEncodeFrame(
|
||||
sequence: Int,
|
||||
flags: Int,
|
||||
canId: Long,
|
||||
input: ByteArray,
|
||||
): ByteArray?
|
||||
external fun nativeCreateParser(): Long
|
||||
external fun nativeDestroyParser(handle: Long)
|
||||
external fun nativeFeedParser(handle: Long, input: ByteArray): ByteArray?
|
||||
external fun nativeParserStats(handle: Long): IntArray?
|
||||
external fun nativeGuiEncode(
|
||||
messageType: Int,
|
||||
sequence: Int,
|
||||
input: ByteArray,
|
||||
): ByteArray?
|
||||
external fun nativeCreateGuiParser(): Long
|
||||
external fun nativeDestroyGuiParser(handle: Long)
|
||||
external fun nativeFeedGuiParser(handle: Long, input: ByteArray): ByteArray?
|
||||
external fun nativeGuiParserStats(handle: Long): IntArray?
|
||||
}
|
||||
317
c/set-protocol/ports/android/setprotocol_jni.c
Normal file
317
c/set-protocol/ports/android/setprotocol_jni.c
Normal file
@@ -0,0 +1,317 @@
|
||||
#include <jni.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "setprotocol_abi.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t *storage;
|
||||
size_t storage_size;
|
||||
int last_sequence;
|
||||
uint32_t sequence_lost;
|
||||
} android_parser_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *storage;
|
||||
size_t storage_size;
|
||||
} android_gui_parser_t;
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeAbiVersion(JNIEnv *env, jobject self)
|
||||
{
|
||||
(void)env;
|
||||
(void)self;
|
||||
return (jint)pcan_abi_version();
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePackId(
|
||||
JNIEnv *env, jobject self, jint priority, jint route, jint device_type,
|
||||
jint device_id, jint message_type, jint body)
|
||||
{
|
||||
(void)env;
|
||||
(void)self;
|
||||
return (jlong)pcan_abi_id_pack((uint8_t)priority, (uint8_t)route,
|
||||
(uint8_t)device_type, (uint8_t)device_id,
|
||||
(uint8_t)message_type, (uint16_t)body);
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeUnpackId(
|
||||
JNIEnv *env, jobject self, jlong raw)
|
||||
{
|
||||
(void)self;
|
||||
uint8_t priority, route, device_type, device_id, message_type;
|
||||
uint16_t body;
|
||||
pcan_abi_id_unpack((uint32_t)raw, &priority, &route, &device_type,
|
||||
&device_id, &message_type, &body);
|
||||
jint values[6] = { (jint)priority, (jint)route, (jint)device_type,
|
||||
(jint)device_id, (jint)message_type, (jint)body };
|
||||
jintArray result = (*env)->NewIntArray(env, 6);
|
||||
if (result != NULL) {
|
||||
(*env)->SetIntArrayRegion(env, result, 0, 6, values);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeCrc16(
|
||||
JNIEnv *env, jobject self, jbyteArray input)
|
||||
{
|
||||
(void)self;
|
||||
jsize size = (*env)->GetArrayLength(env, input);
|
||||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||||
if (data == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint16_t crc = pcan_abi_crc16((const uint8_t *)data, (size_t)size);
|
||||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||||
return (jint)crc;
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeEncodeFrame(
|
||||
JNIEnv *env, jobject self, jint sequence, jint flags, jlong can_id,
|
||||
jbyteArray input)
|
||||
{
|
||||
(void)self;
|
||||
jsize size = (*env)->GetArrayLength(env, input);
|
||||
if (size > 8) {
|
||||
return NULL;
|
||||
}
|
||||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||||
if ((data == NULL) && (size != 0)) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t output[19];
|
||||
size_t written = pcan_abi_frame_encode(
|
||||
(uint8_t)sequence, (uint8_t)flags, (uint32_t)can_id,
|
||||
(const uint8_t *)data, (uint8_t)size, output, sizeof output);
|
||||
if (data != NULL) {
|
||||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||||
}
|
||||
if (written == 0U) {
|
||||
return NULL;
|
||||
}
|
||||
jbyteArray result = (*env)->NewByteArray(env, (jsize)written);
|
||||
if (result != NULL) {
|
||||
(*env)->SetByteArrayRegion(env, result, 0, (jsize)written,
|
||||
(const jbyte *)output);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeCreateParser(JNIEnv *env, jobject self)
|
||||
{
|
||||
(void)env;
|
||||
(void)self;
|
||||
android_parser_t *parser = (android_parser_t *)calloc(1U, sizeof *parser);
|
||||
if (parser == NULL) {
|
||||
return 0;
|
||||
}
|
||||
parser->storage_size = pcan_abi_parser_size();
|
||||
parser->storage = (uint8_t *)malloc(parser->storage_size);
|
||||
parser->last_sequence = -1;
|
||||
if ((parser->storage == NULL) ||
|
||||
!pcan_abi_parser_init(parser->storage, parser->storage_size)) {
|
||||
free(parser->storage);
|
||||
free(parser);
|
||||
return 0;
|
||||
}
|
||||
return (jlong)(intptr_t)parser;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeDestroyParser(
|
||||
JNIEnv *env, jobject self, jlong handle)
|
||||
{
|
||||
(void)env;
|
||||
(void)self;
|
||||
android_parser_t *parser = (android_parser_t *)(intptr_t)handle;
|
||||
if (parser != NULL) {
|
||||
free(parser->storage);
|
||||
free(parser);
|
||||
}
|
||||
}
|
||||
|
||||
/* Each returned record is 15 bytes: seq, flags, id LE, dlc, data[8]. */
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeFeedParser(
|
||||
JNIEnv *env, jobject self, jlong handle, jbyteArray input)
|
||||
{
|
||||
(void)self;
|
||||
android_parser_t *parser = (android_parser_t *)(intptr_t)handle;
|
||||
if (parser == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
jsize size = (*env)->GetArrayLength(env, input);
|
||||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||||
if ((data == NULL) && (size != 0)) {
|
||||
return NULL;
|
||||
}
|
||||
size_t capacity = ((size_t)size / 11U + 2U) * 15U;
|
||||
uint8_t *records = (uint8_t *)malloc(capacity);
|
||||
if (records == NULL) {
|
||||
if (data != NULL) {
|
||||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
size_t used = 0U;
|
||||
pcan_abi_frame_t frame;
|
||||
for (jsize i = 0; i < size; ++i) {
|
||||
if (pcan_abi_parser_push(parser->storage, (uint8_t)data[i], &frame) > 0) {
|
||||
uint8_t *record = &records[used];
|
||||
record[0] = frame.sequence;
|
||||
record[1] = frame.flags;
|
||||
record[2] = (uint8_t)frame.can_id;
|
||||
record[3] = (uint8_t)(frame.can_id >> 8);
|
||||
record[4] = (uint8_t)(frame.can_id >> 16);
|
||||
record[5] = (uint8_t)(frame.can_id >> 24);
|
||||
record[6] = frame.dlc;
|
||||
memset(&record[7], 0, 8U);
|
||||
memcpy(&record[7], frame.data, frame.dlc);
|
||||
used += 15U;
|
||||
if (parser->last_sequence >= 0) {
|
||||
parser->sequence_lost += (uint8_t)(
|
||||
frame.sequence - (uint8_t)parser->last_sequence - 1U);
|
||||
}
|
||||
parser->last_sequence = frame.sequence;
|
||||
}
|
||||
}
|
||||
if (data != NULL) {
|
||||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||||
}
|
||||
jbyteArray result = (*env)->NewByteArray(env, (jsize)used);
|
||||
if (result != NULL && used != 0U) {
|
||||
(*env)->SetByteArrayRegion(env, result, 0, (jsize)used,
|
||||
(const jbyte *)records);
|
||||
}
|
||||
free(records);
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeParserStats(
|
||||
JNIEnv *env, jobject self, jlong handle)
|
||||
{
|
||||
(void)self;
|
||||
android_parser_t *parser = (android_parser_t *)(intptr_t)handle;
|
||||
if (parser == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
uint32_t frames = 0U, crc = 0U, bad_len = 0U, stray = 0U;
|
||||
pcan_abi_parser_stats(parser->storage, &frames, &crc, &bad_len, &stray);
|
||||
jint values[5] = { (jint)frames, (jint)crc, (jint)bad_len,
|
||||
(jint)stray, (jint)parser->sequence_lost };
|
||||
jintArray result = (*env)->NewIntArray(env, 5);
|
||||
if (result != NULL) {
|
||||
(*env)->SetIntArrayRegion(env, result, 0, 5, values);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeGuiEncode(
|
||||
JNIEnv *env, jobject self, jint message_type, jint sequence,
|
||||
jbyteArray input)
|
||||
{
|
||||
(void)self;
|
||||
jsize size = (*env)->GetArrayLength(env, input);
|
||||
if (size > (jsize)PCAN_ABI_GUI_PAYLOAD_MAX) return NULL;
|
||||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||||
uint8_t output[PCAN_ABI_GUI_PAYLOAD_MAX + 12U];
|
||||
size_t written = pcan_abi_gui_frame_encode(
|
||||
(uint8_t)message_type, (uint16_t)sequence, (const uint8_t *)data,
|
||||
(uint16_t)size, output, sizeof output);
|
||||
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||||
if (written == 0U) return NULL;
|
||||
jbyteArray result = (*env)->NewByteArray(env, (jsize)written);
|
||||
if (result != NULL) {
|
||||
(*env)->SetByteArrayRegion(env, result, 0, (jsize)written,
|
||||
(const jbyte *)output);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeCreateGuiParser(JNIEnv *env, jobject self)
|
||||
{
|
||||
(void)env; (void)self;
|
||||
android_gui_parser_t *parser = (android_gui_parser_t *)calloc(1U, sizeof *parser);
|
||||
if (parser == NULL) return 0;
|
||||
parser->storage_size = pcan_abi_gui_parser_size();
|
||||
parser->storage = (uint8_t *)malloc(parser->storage_size);
|
||||
if ((parser->storage == NULL) ||
|
||||
!pcan_abi_gui_parser_init(parser->storage, parser->storage_size)) {
|
||||
free(parser->storage); free(parser); return 0;
|
||||
}
|
||||
return (jlong)(intptr_t)parser;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeDestroyGuiParser(
|
||||
JNIEnv *env, jobject self, jlong handle)
|
||||
{
|
||||
(void)env; (void)self;
|
||||
android_gui_parser_t *parser = (android_gui_parser_t *)(intptr_t)handle;
|
||||
if (parser != NULL) { free(parser->storage); free(parser); }
|
||||
}
|
||||
|
||||
/* Records: type[1], sequence LE[2], size LE[2], payload[size]. */
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeFeedGuiParser(
|
||||
JNIEnv *env, jobject self, jlong handle, jbyteArray input)
|
||||
{
|
||||
(void)self;
|
||||
android_gui_parser_t *parser = (android_gui_parser_t *)(intptr_t)handle;
|
||||
if (parser == NULL) return NULL;
|
||||
jsize size = (*env)->GetArrayLength(env, input);
|
||||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||||
size_t capacity = (size_t)size + ((size_t)size / 12U + 2U) * 5U + 512U;
|
||||
uint8_t *records = (uint8_t *)malloc(capacity);
|
||||
if (records == NULL) {
|
||||
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||||
return NULL;
|
||||
}
|
||||
size_t used = 0U;
|
||||
pcan_abi_gui_frame_t frame;
|
||||
for (jsize i = 0; i < size; ++i) {
|
||||
if (pcan_abi_gui_parser_push(parser->storage, (uint8_t)data[i], &frame) > 0) {
|
||||
records[used++] = frame.message_type;
|
||||
records[used++] = (uint8_t)frame.sequence;
|
||||
records[used++] = (uint8_t)(frame.sequence >> 8);
|
||||
records[used++] = (uint8_t)frame.size;
|
||||
records[used++] = (uint8_t)(frame.size >> 8);
|
||||
memcpy(&records[used], frame.payload, frame.size);
|
||||
used += frame.size;
|
||||
}
|
||||
}
|
||||
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||||
jbyteArray result = (*env)->NewByteArray(env, (jsize)used);
|
||||
if ((result != NULL) && (used != 0U)) {
|
||||
(*env)->SetByteArrayRegion(env, result, 0, (jsize)used,
|
||||
(const jbyte *)records);
|
||||
}
|
||||
free(records);
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeGuiParserStats(
|
||||
JNIEnv *env, jobject self, jlong handle)
|
||||
{
|
||||
(void)self;
|
||||
android_gui_parser_t *parser = (android_gui_parser_t *)(intptr_t)handle;
|
||||
if (parser == NULL) return NULL;
|
||||
uint32_t frames = 0U, crc = 0U, version = 0U, length = 0U, stray = 0U;
|
||||
pcan_abi_gui_parser_stats(parser->storage, &frames, &crc, &version,
|
||||
&length, &stray);
|
||||
jint values[5] = { (jint)frames, (jint)crc, (jint)version,
|
||||
(jint)length, (jint)stray };
|
||||
jintArray result = (*env)->NewIntArray(env, 5);
|
||||
if (result != NULL) (*env)->SetIntArrayRegion(env, result, 0, 5, values);
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user