Files
templates/c/set-protocol/ports/android/setprotocol_jni.c

454 lines
17 KiB
C

#include <jni.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "setprotocol_abi.h"
#include "set_trends.h"
#include "set_spectrum.h"
#include "balsam_can.h"
JNIEXPORT jintArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeBalsamDecode(
JNIEnv *env, jobject self, jlong can_id, jbyteArray input)
{
(void)self;
balsam_can_frame_t frame;
jsize size;
jbyte data[BALSAM_CAN_DLC];
jint values[7];
if (input == NULL) return NULL;
size = (*env)->GetArrayLength(env, input);
if (size != (jsize)BALSAM_CAN_DLC) return NULL;
(*env)->GetByteArrayRegion(env, input, 0, size, data);
if (balsam_can_decode((uint32_t)can_id, (const uint8_t *)data,
(size_t)size, &frame) != 1) return NULL;
values[0] = frame.device;
values[1] = frame.direction;
values[2] = frame.present_mask;
values[3] = frame.start_address;
values[4] = frame.values[0];
values[5] = frame.values[1];
values[6] = frame.values[2];
jintArray result = (*env)->NewIntArray(env, 7);
if (result != NULL) (*env)->SetIntArrayRegion(env, result, 0, 7, values);
return result;
}
JNIEXPORT jstring JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeBalsamDeviceName(
JNIEnv *env, jobject self, jint device)
{
(void)self;
return (*env)->NewStringUTF(env, balsam_can_device_name((uint8_t)device));
}
JNIEXPORT jstring JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeBalsamRegisterName(
JNIEnv *env, jobject self, jint device, jint address)
{
(void)self;
char name[128];
balsam_can_register_name((uint8_t)device, (uint16_t)address,
name, sizeof name);
return (*env)->NewStringUTF(env, name);
}
JNIEXPORT jdoubleArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeSpectrum(
JNIEnv *env, jobject self, jdoubleArray times, jdoubleArray values, jint max_size,
jint window, jint filter, jdouble low_hz, jdouble high_hz, jboolean remove_mean)
{
(void)self;
if (times == NULL || values == NULL || max_size < 16 || max_size > (jint)SET_SPECTRUM_MAX ||
(max_size & (max_size - 1)) != 0) return NULL;
jsize count = (*env)->GetArrayLength(env, times);
if ((*env)->GetArrayLength(env, values) != count) return NULL;
/* Only the analyzed tail crosses JNI; allocations cannot grow with file length. */
jsize used = count < max_size ? count : max_size;
size_t bins = (size_t)max_size / 2 + 1;
double *buffer = (double *)calloc((size_t)used * 2 + bins + 4, sizeof(double));
if (buffer == NULL) return NULL;
double *t = buffer, *v = t + used, *out = v + used;
(*env)->GetDoubleArrayRegion(env, times, count - used, used, t);
(*env)->GetDoubleArrayRegion(env, values, count - used, used, v);
if ((*env)->ExceptionCheck(env)) { free(buffer); return NULL; }
int status = set_spectrum_analyze(t, v, (size_t)used, (size_t)max_size,
window, filter, low_hz, high_hz, remove_mean, out + 4, bins, out + 1);
out[0] = (double)status;
jsize length = status == SET_SPECTRUM_OK ? (jsize)out[1] / 2 + 5 : 4;
jdoubleArray result = (*env)->NewDoubleArray(env, length);
if (result != NULL) (*env)->SetDoubleArrayRegion(env, result, 0, length, out);
free(buffer);
return result;
}
JNIEXPORT jint JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTrendCanValue(
JNIEnv *env, jobject self, jint source, jlong address, jint device_type,
jint device, jint byte_offset, jboolean extended, jboolean is_signed,
jlong can_id, jint flags, jbyteArray input)
{
(void)self;
jsize size = (*env)->GetArrayLength(env, input);
if (size < 2 || size > 8) return SET_TREND_NO_VALUE;
jbyte data[8];
(*env)->GetByteArrayRegion(env, input, 0, size, data);
return set_trend_can_value((uint8_t)source, (uint32_t)address,
(uint8_t)device_type, (uint8_t)device, (uint8_t)byte_offset,
(uint8_t)extended, (uint8_t)is_signed, (uint32_t)can_id,
(uint8_t)flags, (const uint8_t *)data, (size_t)size);
}
JNIEXPORT jbyteArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTrendWatchRequest(
JNIEnv *env, jobject self, jint period, jintArray input)
{
(void)self;
jsize count = (*env)->GetArrayLength(env, input);
if (count > (jsize)SET_TREND_WATCH_MAX || period < 0 || period > 65535) return NULL;
jint values[SET_TREND_WATCH_MAX];
uint16_t addresses[SET_TREND_WATCH_MAX];
uint8_t output[4U + 2U * SET_TREND_WATCH_MAX];
(*env)->GetIntArrayRegion(env, input, 0, count, values);
for (jsize i = 0; i < count; ++i) {
if (values[i] < 0 || values[i] > 65535) return NULL;
addresses[i] = (uint16_t)values[i];
}
size_t size = set_trend_watch_request((uint16_t)period, addresses, (size_t)count, output, sizeof(output));
if (size == 0U) return NULL;
jbyteArray result = (*env)->NewByteArray(env, (jsize)size);
if (result != NULL) (*env)->SetByteArrayRegion(env, result, 0, (jsize)size, (const jbyte *)output);
return result;
}
JNIEXPORT jintArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTrendWatchValues(
JNIEnv *env, jobject self, jbyteArray input)
{
(void)self;
jsize size = (*env)->GetArrayLength(env, input);
if (size < 6 || size > (jsize)(6U + 2U * SET_TREND_WATCH_MAX)) return NULL;
jbyte payload[6U + 2U * SET_TREND_WATCH_MAX];
uint16_t words[SET_TREND_WATCH_MAX];
jint values[SET_TREND_WATCH_MAX];
(*env)->GetByteArrayRegion(env, input, 0, size, payload);
int count = set_trend_watch_values((const uint8_t *)payload, (size_t)size, words, SET_TREND_WATCH_MAX);
if (count < 0) return NULL;
for (int i = 0; i < count; ++i) values[i] = words[i];
jintArray result = (*env)->NewIntArray(env, count);
if (result != NULL) (*env)->SetIntArrayRegion(env, result, 0, count, values);
return result;
}
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;
}