Files
templates/c/set-protocol/ports/android/gas_slots_jni.c
T
Andrey 876b58517c feat(set-protocol): Раздели GAS и добавь пакетный сбор регистров
Сохрани адресацию отдельных регистров GAS 0x3 и выдели слотам тип 0x4 с маской Word0. Добавь единое C-ядро GAS_WATCH2, тонкие Python/JNI-порты и проверки общих эталонных байтов.

Совместимость: тип 0x4 больше не обслуживает Modbus Coil. Потребителям PCAN_MSG_MODBUS_COIL и ProtoCanMsgToModbusCoil нужно перейти на GAS_SLOT и отдельный API слотов; регистровый GAS остаётся на 0x3.
2026-10-23 14:14:48 +03:00

43 lines
7.5 KiB
C

#include <jni.h> /* Единственная ответственность JNI — преобразование массивов и скалярных типов JVM. */
#include "pcan_gas_slot.h" /* Формат ID, слотов и payload реализуется общим C-ядром. */
JNIEXPORT jlong JNICALL Java_ru_setcorp_setprotocol_GasSlots_nativeRequestId(JNIEnv *env,jobject self,jint mode,jint local,jint route) /* Kotlin и host JVM вызывают одинаковый native-метод. */
{ /* Начинаем преобразование адресных координат. */
(void)env;(void)self;if(mode<0 || local<0 || route<0)return 0; /* Не сужаем отрицательные Java-числа в адреса устройств. */
return (jlong)pcan_gas_slot_request_id((unsigned)mode,(unsigned)local,(unsigned)route); /* Все формулы и пределы проверяются C. */
} /* Конец построения Extended ID. */
JNIEXPORT jbyteArray JNICALL Java_ru_setcorp_setprotocol_GasSlots_nativeEncode(JNIEnv *env,jobject self,jintArray input) /* Кодируем Word0-маску и три значения без второго endian-алгоритма на JVM. */
{ /* NULL означает payload запроса чтения, массив — значения слота. */
jint source[4];uint16_t words[4];uint_least8_t bytes[8];int i,n; jbyteArray out;(void)self; /* Все буферы имеют фиксированную вместимость одного кадра. */
if(input==NULL)return (*env)->NewByteArray(env,0); /* Для запроса чтения payload пуст и не требует фиктивных регистров. */
if((*env)->GetArrayLength(env,input)!=4)return NULL; /* Фиксированные позиции маски и трёх значений требуют четырёх wire-слов даже при частичной записи. */
(*env)->GetIntArrayRegion(env,input,0,4,source);if((*env)->ExceptionCheck(env))return NULL; /* Не продолжаем JNI-вызовы после ошибки копирования массива. */
for(i=0;i<4;i++){if(source[i]<0 || source[i]>65535)return NULL;words[i]=(uint16_t)source[i];} /* Сохраняем границы беззнакового 16-битного регистра. */
n=pcan_gas_slot_encode(words,4U,bytes);if(n<0)return NULL; /* Общий кодек проверяет маску Word0 и формирует восемь wire-октетов. */
out=(*env)->NewByteArray(env,n);if(out!=NULL)(*env)->SetByteArrayRegion(env,out,0,n,(const jbyte *)bytes);return out; /* Возвращаем готовый payload транспорта JVM/Android. */
} /* Конец тонкого адаптера записи. */
JNIEXPORT jintArray JNICALL Java_ru_setcorp_setprotocol_GasSlots_nativeDecode(JNIEnv *env,jobject self,jlong id,jint flags,jbyteArray input) /* Декодирование возвращает mode, локальный слот и четыре слова. */
{ /* Заголовок и содержимое проверяются до создания модели JVM. */
uint_least8_t bytes[8];uint16_t words[4];unsigned mode,slot;int n,i;jsize length;jint values[6];jintArray out;(void)self; /* Ограниченные массивы не требуют heap внутри ядра. */
if(id<0 || id>0xFFFFFFFFLL || flags<0 || input==NULL)return NULL; /* Не допускаем сужения 64-битного Long до другого действующего ID. */
length=(*env)->GetArrayLength(env,input);if(length>8)return NULL; /* Extended Classic CAN не принимает payload CAN FD. */
if(length>0)(*env)->GetByteArrayRegion(env,input,0,length,(jbyte *)bytes);if((*env)->ExceptionCheck(env))return NULL; /* Передаём исходные октеты C без ручной интерпретации. */
n=pcan_gas_slot_decode((uint32_t)id,(unsigned)flags,bytes,(unsigned)length,&mode,&slot,words);if(n<0)return NULL; /* Проверяем адрес узла, страницу слота, DLC и направление общей реализацией. */
values[0]=(jint)mode;values[1]=(jint)slot;for(i=0;i<n;i++)values[2+i]=(jint)words[i]; /* Результат запроса имеет два элемента, результат значений — шесть. */
out=(*env)->NewIntArray(env,n+2);if(out!=NULL)(*env)->SetIntArrayRegion(env,out,0,n+2,values);return out; /* JVM получает только проверенные скалярные значения. */
} /* Конец JNI-декодирования без резервной реализации протокола. */
JNIEXPORT jlong JNICALL Java_ru_setcorp_setprotocol_GasSlots_nativeAddressId(JNIEnv *env, jobject self, jint device_type, jint device_id, jint address, jint route) /* Создаём слотный ID любого узла, не только PERIPH. */
{ /* JNI проверяет представимость, а ширину полей и номер типа проверяет C. */
(void)env; (void)self; if (device_type < 0 || device_id < 0 || address < 0 || route < 0) return 0; /* Отрицательные Java Int не превращаются в действительные unsigned-адреса. */
return (jlong)pcan_gas_slot_id((unsigned)device_type, (unsigned)device_id, (unsigned)address, (unsigned)route); /* Один общий построитель формирует MsgType=0x4 и полный адрес 0…65535. */
} /* Конец адресного адаптера. */
JNIEXPORT jintArray JNICALL Java_ru_setcorp_setprotocol_GasSlots_nativeDecodeWords(JNIEnv *env, jobject self, jlong id, jint flags, jbyteArray input) /* Возвращаем проверенные wire-слова любого слотного узла. */
{ /* В этой границе отсутствуют собственные правила маски или порядка октетов. */
uint_least8_t bytes[8]; uint16_t words[4]; jint values[4]; jsize length; jintArray out; int count, i; (void)self; /* Все буферы ограничены одним classic CAN кадром. */
if (id < 0 || id > 0xFFFFFFFFLL || flags < 0 || input == NULL) return NULL; /* Проверяем представимость Long и наличие входа до JNI-копирования. */
length = (*env)->GetArrayLength(env, input); if (length > 8) return NULL; /* Не усекаем данные CAN FD до classic CAN. */
if (length > 0) (*env)->GetByteArrayRegion(env, input, 0, length, (jbyte *)bytes); if ((*env)->ExceptionCheck(env)) return NULL; /* Сохраняем точные wire-октеты и прекращаем работу при исключении JVM. */
count = pcan_gas_slot_decode_words((uint32_t)id, (unsigned)flags, bytes, (unsigned)length, words); if (count < 0) return NULL; /* Тип 0x4, маску, DLC и направление проверяет общее C-ядро. */
for (i = 0; i < count; ++i) values[i] = (jint)words[i]; /* Расширяем проверенные u16 слова без изменения знака или смысла маски. */
out = (*env)->NewIntArray(env, count); if (out != NULL) (*env)->SetIntArrayRegion(env, out, 0, count, values); return out; /* Публикуем готовый результат, пустой только для корректного запроса чтения. */
} /* Конец общего JNI-декодера. */