Сохрани адресацию отдельных регистров GAS 0x3 и выдели слотам тип 0x4 с маской Word0. Добавь единое C-ядро GAS_WATCH2, тонкие Python/JNI-порты и проверки общих эталонных байтов. Совместимость: тип 0x4 больше не обслуживает Modbus Coil. Потребителям PCAN_MSG_MODBUS_COIL и ProtoCanMsgToModbusCoil нужно перейти на GAS_SLOT и отдельный API слотов; регистровый GAS остаётся на 0x3.
932 lines
48 KiB
C
932 lines
48 KiB
C
/*
|
||
* Граница JNI между Kotlin и общим C-ядром. Массивы и примитивные значения Java преобразуются
|
||
* в аргументы C; полученные через JNI указатели имеют ограниченное время жизни и должны
|
||
* освобождаться по правилам JNI.
|
||
*/
|
||
|
||
#include <jni.h>
|
||
#include <stdint.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include "setprotocol_abi.h"
|
||
#include "set_trends.h"
|
||
#include "set_gas_watch.h" /* Подключает единственный кодек GAS_WATCH2 и общие лимиты буферов, чтобы JNI не воспроизводил wire-формат. */
|
||
#include "set_spectrum.h"
|
||
#include "balsam_can.h"
|
||
#include "periph28335.h"
|
||
#include "tms2812.h"
|
||
|
||
static jbyteArray tms_result(JNIEnv *env, const uint8_t *data, size_t size)
|
||
{
|
||
if (size == 0U || size > INT32_MAX) return NULL;
|
||
jbyteArray result = (*env)->NewByteArray(env, (jsize)size);
|
||
if (result != NULL) (*env)->SetByteArrayRegion(env, result, 0, (jsize)size, (const jbyte *)data);
|
||
return result;
|
||
}
|
||
|
||
JNIEXPORT jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812BuildFixed(
|
||
JNIEnv *env, jobject self, jint controller, jint command, jbyteArray input)
|
||
{
|
||
(void)self;
|
||
if (input == NULL || controller < 0 || controller > 255 || command < 0 || command > 255) return NULL;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
if (size > INT32_MAX - 4) return NULL;
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if (data == NULL) return NULL;
|
||
uint8_t *output = (uint8_t *)malloc((size_t)size + 4U);
|
||
if (output == NULL) { (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT); return NULL; }
|
||
size_t written = tms2812_build_fixed_request((uint8_t)controller, (uint8_t)command,
|
||
(const uint8_t *)data, (size_t)size, output, (size_t)size + 4U);
|
||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
jbyteArray result = tms_result(env, output, written);
|
||
free(output);
|
||
return result;
|
||
}
|
||
|
||
JNIEXPORT jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812BuildMemory(
|
||
JNIEnv *env, jobject self, jint controller, jint command, jlong address, jint value)
|
||
{
|
||
(void)self;
|
||
uint8_t output[12];
|
||
if (controller < 0 || controller > 255 || command < 0 || command > 255 ||
|
||
address < 0 || (uint64_t)address > UINT32_MAX || value < 0 || value > 65535) return NULL;
|
||
size_t written = tms2812_build_memory_request((uint8_t)controller, (uint8_t)command,
|
||
(uint32_t)address, (uint16_t)value, output, sizeof output);
|
||
return tms_result(env, output, written);
|
||
}
|
||
|
||
JNIEXPORT jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812BuildTest(
|
||
JNIEnv *env, jobject self, jint controller, jint duration, jint pause, jbyteArray input)
|
||
{
|
||
(void)self;
|
||
uint8_t output[20];
|
||
if (input == NULL || controller < 0 || controller > 255 || duration < 0 ||
|
||
duration > 65535 || pause < 0 || pause > 65535 || (*env)->GetArrayLength(env, input) != 12) return NULL;
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if (data == NULL) return NULL;
|
||
size_t written = tms2812_build_test_request((uint8_t)controller, (uint16_t)duration,
|
||
(uint16_t)pause, (const uint8_t *)data, 12U, output, sizeof output);
|
||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
return tms_result(env, output, written);
|
||
}
|
||
|
||
JNIEXPORT jboolean JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812ValidateResponse(
|
||
JNIEnv *env, jobject self, jbyteArray input, jint controller, jint command,
|
||
jint full_size, jboolean allow_short_tail)
|
||
{
|
||
(void)self;
|
||
if (input == NULL || controller < 0 || controller > 255 || command < 0 || command > 255 || full_size < 6) return JNI_FALSE;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if (data == NULL) return JNI_FALSE;
|
||
int status = tms2812_validate_response((const uint8_t *)data, (size_t)size,
|
||
(uint8_t)controller, (uint8_t)command, (size_t)full_size, allow_short_tail != JNI_FALSE);
|
||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
return status == TMS2812_OK ? JNI_TRUE : JNI_FALSE;
|
||
}
|
||
|
||
JNIEXPORT jint JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812Crc16(
|
||
JNIEnv *env, jobject self, jbyteArray input)
|
||
{
|
||
(void)self;
|
||
if (input == NULL) return 0;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if ((data == NULL) && (size != 0)) return 0;
|
||
uint16_t crc = tms2812_crc16((const uint8_t *)data, (size_t)size);
|
||
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
return (jint)crc;
|
||
}
|
||
|
||
JNIEXPORT jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812BuildUpload(
|
||
JNIEnv *env, jobject self, jint controller, jlong word_address,
|
||
jlong byte_count)
|
||
{
|
||
(void)self;
|
||
uint8_t output[TMS2812_UPLOAD_REQUEST_SIZE];
|
||
if (controller < 0 || controller > 255 || word_address < 0 ||
|
||
(uint64_t)word_address > UINT32_MAX || byte_count < 1 ||
|
||
(uint64_t)byte_count > UINT32_MAX) return NULL;
|
||
size_t written = tms2812_build_upload_request(
|
||
(uint8_t)controller, (uint32_t)word_address, (uint32_t)byte_count,
|
||
output, sizeof output);
|
||
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 jint JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812ExpectedUploadSize(
|
||
JNIEnv *env, jobject self, jlong byte_count)
|
||
{
|
||
(void)env; (void)self;
|
||
if (byte_count < 1 || (uint64_t)byte_count > UINT32_MAX) return 0;
|
||
size_t size = tms2812_expected_upload_response_size((uint32_t)byte_count);
|
||
return size <= INT32_MAX ? (jint)size : 0;
|
||
}
|
||
|
||
JNIEXPORT jboolean JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812ValidateUpload(
|
||
JNIEnv *env, jobject self, jbyteArray input, jint controller,
|
||
jint byte_count)
|
||
{
|
||
(void)self;
|
||
if (input == NULL || controller < 0 || controller > 255 || byte_count < 1)
|
||
return JNI_FALSE;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if (data == NULL) return JNI_FALSE;
|
||
int status = tms2812_validate_upload_response(
|
||
(const uint8_t *)data, (size_t)size, (uint8_t)controller,
|
||
(uint32_t)byte_count);
|
||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
return status == TMS2812_OK ? JNI_TRUE : JNI_FALSE;
|
||
}
|
||
|
||
JNIEXPORT jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTms2812DecodeUpload(
|
||
JNIEnv *env, jobject self, jbyteArray input, jint controller,
|
||
jint byte_count)
|
||
{
|
||
(void)self;
|
||
if (input == NULL || controller < 0 || controller > 255 || byte_count < 1)
|
||
return NULL;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if (data == NULL) return NULL;
|
||
uint8_t *output = (uint8_t *)malloc((size_t)byte_count);
|
||
if (output == NULL) {
|
||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
return NULL;
|
||
}
|
||
int status = tms2812_decode_upload_response(
|
||
(const uint8_t *)data, (size_t)size, (uint8_t)controller,
|
||
(uint32_t)byte_count, output, (size_t)byte_count);
|
||
(*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
if (status != TMS2812_OK) { free(output); return NULL; }
|
||
jbyteArray result = (*env)->NewByteArray(env, byte_count);
|
||
if (result != NULL) (*env)->SetByteArrayRegion(
|
||
env, result, 0, byte_count, (const jbyte *)output);
|
||
free(output);
|
||
return result;
|
||
}
|
||
|
||
JNIEXPORT jint JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335Crc16(
|
||
JNIEnv *env, jobject self, jbyteArray input)
|
||
{
|
||
(void)self;
|
||
if (input == NULL) return 0;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if ((data == NULL) && (size != 0)) return 0;
|
||
uint16_t crc = periph28335_crc16_modbus((const uint8_t *)data, (size_t)size);
|
||
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
return (jint)crc;
|
||
}
|
||
|
||
JNIEXPORT jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335AppendCrc(
|
||
JNIEnv *env, jobject self, jbyteArray input)
|
||
{
|
||
(void)self;
|
||
if (input == NULL) return NULL;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if ((data == NULL) && (size != 0)) return NULL;
|
||
uint8_t *output = (uint8_t *)malloc((size_t)size + 2U);
|
||
if (output == NULL) {
|
||
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
return NULL;
|
||
}
|
||
size_t written = periph28335_append_crc(
|
||
(const uint8_t *)data, (size_t)size, output, (size_t)size + 2U);
|
||
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
if (written == 0U) { free(output); return NULL; }
|
||
jbyteArray result = (*env)->NewByteArray(env, (jsize)written);
|
||
if (result != NULL) (*env)->SetByteArrayRegion(
|
||
env, result, 0, (jsize)written, (const jbyte *)output);
|
||
free(output);
|
||
return result;
|
||
}
|
||
|
||
JNIEXPORT jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335BuildRead(
|
||
JNIEnv *env, jobject self, jint controller, jint start, jint count)
|
||
{
|
||
(void)self;
|
||
uint8_t output[PERIPH28335_REQUEST_SIZE];
|
||
if (controller < 0 || controller > 255 || start < 0 || start > 65535 ||
|
||
count < 0 || count > 65535) return NULL;
|
||
size_t written = periph28335_build_read_registers(
|
||
(uint8_t)controller, (uint16_t)start, (uint16_t)count,
|
||
output, sizeof output);
|
||
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 jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335BuildWrite(
|
||
JNIEnv *env, jobject self, jint controller, jint address, jint value)
|
||
{
|
||
(void)self;
|
||
uint8_t output[PERIPH28335_REQUEST_SIZE];
|
||
if (controller < 0 || controller > 255 || address < 0 || address > 65535 ||
|
||
value < 0 || value > 65535) return NULL;
|
||
size_t written = periph28335_build_write_register(
|
||
(uint8_t)controller, (uint16_t)address, (uint16_t)value,
|
||
output, sizeof output);
|
||
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 jbyteArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335BuildCommand(
|
||
JNIEnv *env, jobject self, jint controller, jint command_index)
|
||
{
|
||
(void)self;
|
||
uint8_t output[PERIPH28335_REQUEST_SIZE];
|
||
if (controller < 0 || controller > 255 || command_index < 0 || command_index > 255) return NULL;
|
||
size_t written = periph28335_build_command(
|
||
(uint8_t)controller, (uint8_t)command_index, output, sizeof output);
|
||
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 jint JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335ExpectedReadSize(
|
||
JNIEnv *env, jobject self, jint count)
|
||
{
|
||
(void)env; (void)self;
|
||
if (count < 0 || count > 65535) return 0;
|
||
return (jint)periph28335_expected_read_response_size((uint16_t)count);
|
||
}
|
||
|
||
JNIEXPORT jintArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335DecodeRead(
|
||
JNIEnv *env, jobject self, jbyteArray input, jint controller, jint count)
|
||
{
|
||
(void)self;
|
||
if (input == NULL || controller < 0 || controller > 255 ||
|
||
count < 1 || count > (jint)PERIPH28335_REGISTER_COUNT) return NULL;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
|
||
if ((data == NULL) && (size != 0)) return NULL;
|
||
uint16_t words[PERIPH28335_REGISTER_COUNT];
|
||
int status = periph28335_decode_read_response(
|
||
(const uint8_t *)data, (size_t)size, (uint8_t)controller,
|
||
(uint16_t)count, words, PERIPH28335_REGISTER_COUNT);
|
||
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
|
||
if (status != PERIPH28335_OK) return NULL;
|
||
jint values[PERIPH28335_REGISTER_COUNT];
|
||
for (jint index = 0; index < count; ++index) values[index] = words[index];
|
||
jintArray result = (*env)->NewIntArray(env, count);
|
||
if (result != NULL) (*env)->SetIntArrayRegion(env, result, 0, count, values);
|
||
return result;
|
||
}
|
||
|
||
JNIEXPORT jint JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335ValidateWrite(
|
||
JNIEnv *env, jobject self, jbyteArray response, jbyteArray request)
|
||
{
|
||
(void)self;
|
||
if (response == NULL || request == NULL) return PERIPH28335_ERROR_ARGUMENT;
|
||
jsize response_size = (*env)->GetArrayLength(env, response);
|
||
jsize request_size = (*env)->GetArrayLength(env, request);
|
||
jbyte *response_data = (*env)->GetByteArrayElements(env, response, NULL);
|
||
jbyte *request_data = (*env)->GetByteArrayElements(env, request, NULL);
|
||
if (response_data == NULL || request_data == NULL) {
|
||
if (response_data != NULL) (*env)->ReleaseByteArrayElements(env, response, response_data, JNI_ABORT);
|
||
if (request_data != NULL) (*env)->ReleaseByteArrayElements(env, request, request_data, JNI_ABORT);
|
||
return PERIPH28335_ERROR_ARGUMENT;
|
||
}
|
||
int status = periph28335_validate_write_response(
|
||
(const uint8_t *)response_data, (size_t)response_size,
|
||
(const uint8_t *)request_data, (size_t)request_size);
|
||
(*env)->ReleaseByteArrayElements(env, response, response_data, JNI_ABORT);
|
||
(*env)->ReleaseByteArrayElements(env, request, request_data, JNI_ABORT);
|
||
return status;
|
||
}
|
||
|
||
JNIEXPORT jint JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335ProjectCount(
|
||
JNIEnv *env, jobject self)
|
||
{
|
||
(void)env; (void)self;
|
||
return (jint)periph28335_project_count();
|
||
}
|
||
|
||
JNIEXPORT jstring JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335ProjectName(
|
||
JNIEnv *env, jobject self, jint project_index)
|
||
{
|
||
(void)self;
|
||
const char *name = project_index >= 0
|
||
? periph28335_project_name((size_t)project_index) : NULL;
|
||
return name != NULL ? (*env)->NewStringUTF(env, name) : NULL;
|
||
}
|
||
|
||
JNIEXPORT jstring JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335CommandName(
|
||
JNIEnv *env, jobject self, jint project_index, jint command_index)
|
||
{
|
||
(void)self;
|
||
const char *name = project_index >= 0 && command_index >= 0
|
||
? periph28335_command_name((size_t)project_index, (size_t)command_index)
|
||
: NULL;
|
||
return name != NULL ? (*env)->NewStringUTF(env, name) : NULL;
|
||
}
|
||
|
||
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 jdoubleArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeSpectrumPeak(
|
||
JNIEnv *env, jobject self, jdoubleArray input, jdouble bin_hz,
|
||
jdouble relative_threshold, jdouble absolute_floor)
|
||
{
|
||
(void)self;
|
||
jsize count = input == NULL ? 0 : (*env)->GetArrayLength(env, input);
|
||
if (count < 3 || count > (jsize)(SET_SPECTRUM_MAX / 2U + 1U)) return NULL;
|
||
double *buffer = (double *)malloc(sizeof(double) * (size_t)(count * 2 - 1));
|
||
if (buffer == NULL) return NULL;
|
||
double *amplitudes = buffer, *scratch = buffer + count, peak[2];
|
||
(*env)->GetDoubleArrayRegion(env, input, 0, count, amplitudes);
|
||
int status = (*env)->ExceptionCheck(env) ? -1 : set_spectrum_dominant_peak(
|
||
amplitudes, (size_t)count, bin_hz, relative_threshold, absolute_floor,
|
||
scratch, (size_t)count - 1U, peak, 2U);
|
||
jdoubleArray result = NULL;
|
||
if (status >= 0) {
|
||
result = (*env)->NewDoubleArray(env, status == 1 ? 2 : 0);
|
||
if (result != NULL && status == 1) (*env)->SetDoubleArrayRegion(env, result, 0, 2, peak);
|
||
}
|
||
free(buffer);
|
||
return result;
|
||
}
|
||
|
||
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 jboolean JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTrendWatchAck(
|
||
JNIEnv *env, jobject self, jbyteArray input, jint period, jint count)
|
||
{
|
||
(void)self;
|
||
if (input == NULL || period < 0 || period > 65535 || count < 0) return JNI_FALSE;
|
||
jsize size = (*env)->GetArrayLength(env, input);
|
||
if (size != 4) return JNI_FALSE;
|
||
jbyte payload[4];
|
||
(*env)->GetByteArrayRegion(env, input, 0, size, payload);
|
||
return set_trend_watch_ack((const uint8_t *)payload, (size_t)size,
|
||
(uint16_t)period, (size_t)count) ? JNI_TRUE : JNI_FALSE;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
JNIEXPORT jlongArray JNICALL
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeTrendWatchDecode(
|
||
JNIEnv *env, jobject self, jbyteArray input)
|
||
{
|
||
(void)self;
|
||
jsize size = input == NULL ? 0 : (*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];
|
||
uint32_t timestamp = 0U;
|
||
jlong values[1U + SET_TREND_WATCH_MAX];
|
||
(*env)->GetByteArrayRegion(env, input, 0, size, payload);
|
||
int count = set_trend_watch_decode((const uint8_t *)payload, (size_t)size,
|
||
×tamp, words, SET_TREND_WATCH_MAX);
|
||
if (count < 0) return NULL;
|
||
values[0] = (jlong)timestamp;
|
||
for (int i = 0; i < count; ++i) values[i + 1] = (jlong)words[i];
|
||
jlongArray result = (*env)->NewLongArray(env, count + 1);
|
||
if (result != NULL) (*env)->SetLongArrayRegion(env, result, 0, count + 1, values);
|
||
return result;
|
||
}
|
||
|
||
static int gas_watch_copy_addresses(JNIEnv *env, jintArray input, uint16_t *addresses, jsize *count) /* Преобразует JVM IntArray в u16-массив без усечения; протокольные правила проверяет только C-кодек. */
|
||
{ /* Начинает общий для запроса и ACK ограниченный переход между представлениями JNI и C. */
|
||
if (input == NULL) return 0; /* Отклоняет отсутствующий массив до обращения к GetArrayLength, исключая исключение JNI из-за null. */
|
||
*count = (*env)->GetArrayLength(env, input); /* Получает фактическое число адресов из JVM-массива, не доверяя внешнему счётчику. */
|
||
if (*count > (jsize)SET_GAS_WATCH_MAX) return 0; /* Ограничивает копирование общим лимитом 64 слов до заполнения стековых массивов. */
|
||
jint source[SET_GAS_WATCH_MAX]; /* Резервирует исходные знаковые JVM-числа по единственному лимиту из C-заголовка. */
|
||
(*env)->GetIntArrayRegion(env, input, 0, *count, source); /* Копирует JVM-адреса без удержания указателей на управляемую память. */
|
||
if ((*env)->ExceptionCheck(env)) return 0; /* Не использует частично скопированный массив, если JVM сообщила исключение доступа к массиву. */
|
||
for (jsize index = 0; index < *count; ++index) { /* Проверяет каждое число до приведения к беззнаковому 16-битному адресу. */
|
||
if (source[index] < 0 || source[index] > UINT16_MAX) return 0; /* Не позволяет отрицательному или слишком большому Int превратиться в другой действительный GAS-адрес. */
|
||
addresses[index] = (uint16_t)source[index]; /* Сохраняет проверенный адрес без потери битов; порядок остаётся равным порядку конфигурации. */
|
||
} /* Завершает безопасное преобразование адресов без сортировки, удаления дублей или иной протокольной логики. */
|
||
return 1; /* Сообщает вызывающей JNI-функции, что массив готов для проверки общим C-кодеком. */
|
||
} /* Завершает вспомогательное преобразование числовых типов JNI. */
|
||
|
||
JNIEXPORT jbyteArray JNICALL /* Экспортирует JNI-метод, возвращающий готовые байты конфигурации GAS_WATCH2 либо null при отказе. */
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeGasWatchRequest( /* Связывает метод с объявлением NativeSetProtocol.nativeGasWatchRequest без регистрации второго кодека. */
|
||
JNIEnv *env, jobject self, jlong config_id, jint period, jintArray input) /* Принимает u32 id в положительном Long, период в миллисекундах и JVM-массив адресов u16. */
|
||
{ /* Начинает тонкий адаптер формирования запроса через общее C-ядро. */
|
||
(void)self; /* Объект Kotlin не содержит данных, необходимых переносимому C-кодеку. */
|
||
if (config_id <= 0 || (uint64_t)config_id > UINT32_MAX || period < 0 || period > UINT16_MAX) return NULL; /* Проверяет границы до приведения Long и Int, исключая потерю битов идентификатора или периода. */
|
||
jsize count = 0; /* Получит число адресов только после проверки фактического JVM-массива. */
|
||
uint16_t addresses[SET_GAS_WATCH_MAX]; /* Выделяет адреса на стеке по общему C-лимиту подписки без динамических выделений. */
|
||
uint8_t output[SET_GAS_WATCH_CONFIG_MAX]; /* Выделяет максимально допустимый payload по C-константе вместо повторного расчёта wire-размеров в адаптере. */
|
||
if (!gas_watch_copy_addresses(env, input, addresses, &count)) return NULL; /* Не передаёт C-кодеку адреса, которые нельзя точно представить как u16. */
|
||
size_t size = set_gas_watch_request((uint32_t)config_id, (uint16_t)period, addresses, (size_t)count, output, sizeof output); /* Единственный C builder проверяет геометрию и создаёт канонический payload конфигурации. */
|
||
return tms_result(env, output, size); /* Копирует результат в ByteArray существующим общим JNI-помощником; нулевая длина C-отказа превращается в null. */
|
||
} /* Завершает запрос без хранения или изменения состояния подписки на JNI-стороне. */
|
||
|
||
JNIEXPORT jboolean JNICALL /* Экспортирует JNI-проверку полного подтверждения конфигурации GAS_WATCH2. */
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeGasWatchAck( /* Связывает метод с NativeSetProtocol.nativeGasWatchAck для проверки точного эхо-ответа. */
|
||
JNIEnv *env, jobject self, jbyteArray input, jlong config_id, jint period, jintArray expected) /* Принимает payload ACK и исходные ожидаемые параметры без извлечения полей в JNI. */
|
||
{ /* Начинает адаптер проверки ACK, делегирующий правила общему C-кодеку. */
|
||
(void)self; /* Kotlin-объект служит только точкой вызова и не участвует в сравнении wire-полей. */
|
||
if (input == NULL || config_id <= 0 || (uint64_t)config_id > UINT32_MAX || period < 0 || period > UINT16_MAX) return JNI_FALSE; /* Отбрасывает null и числа, которые были бы усечены при переходе к C-типам. */
|
||
jsize count = 0; /* Получит проверенное количество исходных адресов для полного сравнения ACK в C. */
|
||
uint16_t addresses[SET_GAS_WATCH_MAX]; /* Хранит ожидаемые адреса в том же исходном порядке, которым определяются позиции значений снимка. */
|
||
if (!gas_watch_copy_addresses(env, expected, addresses, &count)) return JNI_FALSE; /* Не сравнивает ACK с недопустимым JVM-массивом или усечёнными адресами. */
|
||
jsize size = (*env)->GetArrayLength(env, input); /* Получает длину ACK для безопасного копирования ограниченного буфера. */
|
||
if (size > (jsize)SET_GAS_WATCH_CONFIG_MAX) return JNI_FALSE; /* Отбрасывает превышение общего максимума до копирования на стек. */
|
||
jbyte payload[SET_GAS_WATCH_CONFIG_MAX]; /* Резервирует payload по общей C-константе; проверка точной длины остаётся в set_gas_watch_ack. */
|
||
(*env)->GetByteArrayRegion(env, input, 0, size, payload); /* Копирует байты ACK без ручного разбора их полей или порядка байтов. */
|
||
if ((*env)->ExceptionCheck(env)) return JNI_FALSE; /* Не вызывает C-кодек, если JVM не смогла полностью скопировать входной массив. */
|
||
return set_gas_watch_ack((const uint8_t *)payload, (size_t)size, (uint32_t)config_id, (uint16_t)period, addresses, (size_t)count) ? JNI_TRUE : JNI_FALSE; /* Проверяет C-кодеком весь echo, включая версию, id, период, количество, reserved и порядок адресов. */
|
||
} /* Завершает проверку ACK без изменения активной или ожидающей конфигурации. */
|
||
|
||
JNIEXPORT jlongArray JNICALL /* Экспортирует JNI-декодер снимка с положительными u32 счётчиками и отдельным маркером невалидного слова. */
|
||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeGasWatchDecode( /* Связывает метод с NativeSetProtocol.nativeGasWatchDecode и единственным C-декодером GAS_WATCH2. */
|
||
JNIEnv *env, jobject self, jbyteArray input, jlong config_id, jint expected_count) /* Принимает payload и параметры подтверждённой конфигурации, а не предполагаемые параметры из самого пакета. */
|
||
{ /* Начинает ограниченное преобразование готового C-результата в JVM-массив. */
|
||
(void)self; /* Все параметры ожидаемой подписки переданы явно; объект Kotlin не участвует в C-декодировании. */
|
||
if (input == NULL || config_id <= 0 || (uint64_t)config_id > UINT32_MAX || expected_count < 0 || expected_count > (jint)SET_GAS_WATCH_MAX) return NULL; /* Проверяет null, u32 id и ограниченный count до приведения к C-типам и обращения к стековым буферам. */
|
||
jsize size = (*env)->GetArrayLength(env, input); /* Получает длину входного JVM-массива без чтения каких-либо wire-полей. */
|
||
if (size > (jsize)SET_GAS_WATCH_SAMPLE_MAX) return NULL; /* Гарантирует, что вход помещается в стековый буфер общего максимального размера снимка. */
|
||
jbyte payload[SET_GAS_WATCH_SAMPLE_MAX]; /* Хранит исходный payload до завершения C-проверки версии, длины, id, count и bitmap. */
|
||
uint16_t words[SET_GAS_WATCH_MAX]; /* Принимает от C сырые слова GAS без изменения знакового представления пользовательского параметра. */
|
||
uint8_t valid[SET_GAS_WATCH_MAX]; /* Принимает от C уже проверенные признаки валидности по одному элементу на канал вместо повторного разбора bitmap. */
|
||
uint32_t sequence = 0U, timestamp_ms = 0U; /* Принимает номер снимка и приборное время в миллисекундах как u32 без промежуточного знакового Int. */
|
||
(*env)->GetByteArrayRegion(env, input, 0, size, payload); /* Копирует вход без удержания JVM-памяти и без воспроизведения протокольных смещений в адаптере. */
|
||
if ((*env)->ExceptionCheck(env)) return NULL; /* Не использует частичный вход при исключении копирования JVM-массива. */
|
||
int count = set_gas_watch_decode((const uint8_t *)payload, (size_t)size, (uint32_t)config_id, (size_t)expected_count, &sequence, ×tamp_ms, words, valid, SET_GAS_WATCH_MAX); /* C проверяет принадлежность снимка активной конфигурации и каноничность всего payload, возвращая число слов или −1. */
|
||
if (count < 0) return NULL; /* Не публикует частично заполненные выходные данные после любой ошибки C-декодера. */
|
||
jlong values[2U + SET_GAS_WATCH_MAX]; /* Два первых JVM-элемента принадлежат sequence и времени; остальные ограничены общим максимумом адресов. */
|
||
values[0] = (jlong)sequence; /* Расширяет u32 номер снимка напрямую в положительный Long, включая значения с установленным bit31. */
|
||
values[1] = (jlong)timestamp_ms; /* Расширяет u32 приборное время напрямую в положительный Long без потери старшего бита. */
|
||
for (int index = 0; index < count; ++index) values[index + 2] = valid[index] ? (jlong)words[index] : -1; /* Передаёт валидное u16 слово как 0..65535, а невалидный канал как −1, которое Kotlin преобразует в null. */
|
||
jlongArray result = (*env)->NewLongArray(env, count + 2); /* Создаёт ровно два элемента метаданных и по одному элементу на каждый проверенный канал снимка. */
|
||
if (result != NULL) (*env)->SetLongArrayRegion(env, result, 0, count + 2, values); /* Копирует завершённый результат в управляемую память только после успешной C-проверки. */
|
||
return result; /* Возвращает null при отказе JVM выделить массив и корректный LongArray во всех успешных случаях. */
|
||
} /* Завершает JNI-декодер без второго протокольного алгоритма или собственной таблицы адресов. */
|
||
|
||
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;
|
||
}
|