Move PM35 protocol into shared C core
This commit is contained in:
@@ -18,6 +18,7 @@ set(SETPROTOCOL_V2_SOURCES
|
||||
# Совместимые ProtoCAN/SETGUI v1 форматы переходного периода.
|
||||
set(SETPROTOCOL_LEGACY_SOURCES
|
||||
src/balsam_can.c
|
||||
src/periph28335.c
|
||||
src/gui_catalog.c
|
||||
src/gui_frame.c
|
||||
src/pcan_abi.c
|
||||
@@ -98,6 +99,9 @@ if(SETP_BUILD_TESTS)
|
||||
add_executable(test_balsam_can tests/test_balsam_can.c)
|
||||
target_link_libraries(test_balsam_can PRIVATE setprotocol_static)
|
||||
add_test(NAME legacy_balsam_can COMMAND test_balsam_can)
|
||||
add_executable(test_periph28335 tests/test_periph28335.c)
|
||||
target_link_libraries(test_periph28335 PRIVATE setprotocol_static)
|
||||
add_test(NAME shared_periph28335 COMMAND test_periph28335)
|
||||
add_executable(test_trends tests/test_trends.c)
|
||||
target_link_libraries(test_trends PRIVATE setprotocol_static)
|
||||
add_test(NAME shared_trends COMMAND test_trends)
|
||||
|
||||
@@ -73,6 +73,34 @@ PCAN_ABI_API const char *pcan_abi_balsam_device_name(uint8_t device);
|
||||
PCAN_ABI_API size_t pcan_abi_balsam_register_name(
|
||||
uint8_t device, uint16_t address, char *output, size_t output_size);
|
||||
|
||||
/* PM35/TMS320F28335 direct RS232/485 register terminal. */
|
||||
PCAN_ABI_API uint16_t pcan_abi_periph28335_crc16(
|
||||
const uint8_t *data, size_t size);
|
||||
PCAN_ABI_API size_t pcan_abi_periph28335_append_crc(
|
||||
const uint8_t *payload, size_t payload_size,
|
||||
uint8_t *output, size_t output_size);
|
||||
PCAN_ABI_API size_t pcan_abi_periph28335_build_read(
|
||||
uint8_t controller, uint16_t start, uint16_t count,
|
||||
uint8_t *output, size_t output_size);
|
||||
PCAN_ABI_API size_t pcan_abi_periph28335_build_write(
|
||||
uint8_t controller, uint16_t address, uint16_t value,
|
||||
uint8_t *output, size_t output_size);
|
||||
PCAN_ABI_API size_t pcan_abi_periph28335_build_command(
|
||||
uint8_t controller, uint8_t command_index,
|
||||
uint8_t *output, size_t output_size);
|
||||
PCAN_ABI_API size_t pcan_abi_periph28335_expected_read_size(uint16_t count);
|
||||
PCAN_ABI_API int pcan_abi_periph28335_decode_read(
|
||||
const uint8_t *data, size_t size, uint8_t controller, uint16_t count,
|
||||
uint16_t *output, size_t output_count);
|
||||
PCAN_ABI_API int pcan_abi_periph28335_validate_write(
|
||||
const uint8_t *response, size_t response_size,
|
||||
const uint8_t *request, size_t request_size);
|
||||
PCAN_ABI_API size_t pcan_abi_periph28335_project_count(void);
|
||||
PCAN_ABI_API const char *pcan_abi_periph28335_project_name(
|
||||
size_t project_index);
|
||||
PCAN_ABI_API const char *pcan_abi_periph28335_command_name(
|
||||
size_t project_index, size_t command_index);
|
||||
|
||||
PCAN_ABI_API size_t pcan_abi_frame_encode(uint8_t sequence, uint8_t flags,
|
||||
uint32_t can_id,
|
||||
const uint8_t *data, uint8_t dlc,
|
||||
|
||||
68
c/set-protocol/include/periph28335.h
Normal file
68
c/set-protocol/include/periph28335.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file periph28335.h
|
||||
* @brief Shared PM35/TMS320F28335 register protocol.
|
||||
*
|
||||
* The protocol is independent from PM67/TMS320F2812 CAN and RS channels.
|
||||
* It implements the direct RS232/485 register terminal used by PM35.
|
||||
*/
|
||||
#ifndef PERIPH28335_H
|
||||
#define PERIPH28335_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PERIPH28335_REGISTER_COUNT 128U
|
||||
#define PERIPH28335_REQUEST_SIZE 8U
|
||||
#define PERIPH28335_COMMAND_COUNT 17U
|
||||
|
||||
typedef enum {
|
||||
PERIPH28335_OK = 0,
|
||||
PERIPH28335_ERROR_ARGUMENT = -1,
|
||||
PERIPH28335_ERROR_RANGE = -2,
|
||||
PERIPH28335_ERROR_LENGTH = -3,
|
||||
PERIPH28335_ERROR_CRC = -4,
|
||||
PERIPH28335_ERROR_HEADER = -5,
|
||||
PERIPH28335_ERROR_CAPACITY = -6
|
||||
} periph28335_status_t;
|
||||
|
||||
uint16_t periph28335_crc16_modbus(const uint8_t *data, size_t size);
|
||||
|
||||
size_t periph28335_append_crc(const uint8_t *payload, size_t payload_size,
|
||||
uint8_t *output, size_t output_size);
|
||||
|
||||
size_t periph28335_build_read_registers(
|
||||
uint8_t controller, uint16_t start, uint16_t count,
|
||||
uint8_t *output, size_t output_size);
|
||||
|
||||
size_t periph28335_build_write_register(
|
||||
uint8_t controller, uint16_t address, uint16_t value,
|
||||
uint8_t *output, size_t output_size);
|
||||
|
||||
size_t periph28335_build_command(
|
||||
uint8_t controller, uint8_t command_index,
|
||||
uint8_t *output, size_t output_size);
|
||||
|
||||
size_t periph28335_expected_read_response_size(uint16_t count);
|
||||
|
||||
int periph28335_decode_read_response(
|
||||
const uint8_t *data, size_t size, uint8_t controller, uint16_t count,
|
||||
uint16_t *output, size_t output_count);
|
||||
|
||||
int periph28335_validate_write_response(
|
||||
const uint8_t *response, size_t response_size,
|
||||
const uint8_t *request, size_t request_size);
|
||||
|
||||
size_t periph28335_project_count(void);
|
||||
const char *periph28335_project_name(size_t project_index);
|
||||
const char *periph28335_command_name(size_t project_index,
|
||||
size_t command_index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PERIPH28335_H */
|
||||
@@ -6,6 +6,7 @@
|
||||
#define SETPROTOCOL_H
|
||||
|
||||
#include "balsam_can.h"
|
||||
#include "periph28335.h"
|
||||
|
||||
/* Основной SET protocol v2. */
|
||||
#include "set_protocol.h"
|
||||
|
||||
@@ -13,6 +13,7 @@ LOCAL_SRC_FILES := \
|
||||
../../src/set_trends.c \
|
||||
../../src/set_spectrum.c \
|
||||
../../src/balsam_can.c \
|
||||
../../src/periph28335.c \
|
||||
../../src/gui_catalog.c \
|
||||
../../src/gui_frame.c \
|
||||
../../src/pcan_abi.c \
|
||||
|
||||
@@ -4,7 +4,10 @@ package ru.setcorp.setprotocol
|
||||
object NativeSetProtocol {
|
||||
val available: Boolean by lazy {
|
||||
runCatching {
|
||||
System.loadLibrary("setprotocol")
|
||||
val hostLibrary = System.getProperty("setprotocol.library")
|
||||
?: System.getProperty("setplot.library")
|
||||
if (hostLibrary != null) System.load(hostLibrary)
|
||||
else System.loadLibrary("setprotocol")
|
||||
nativeAbiVersion() == 1
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
@@ -32,6 +35,17 @@ object NativeSetProtocol {
|
||||
external fun nativeBalsamDecode(canId: Long, input: ByteArray): IntArray?
|
||||
external fun nativeBalsamDeviceName(device: Int): String
|
||||
external fun nativeBalsamRegisterName(device: Int, address: Int): String
|
||||
external fun nativePeriph28335Crc16(input: ByteArray): Int
|
||||
external fun nativePeriph28335AppendCrc(input: ByteArray): ByteArray?
|
||||
external fun nativePeriph28335BuildRead(controller: Int, start: Int, count: Int): ByteArray?
|
||||
external fun nativePeriph28335BuildWrite(controller: Int, address: Int, value: Int): ByteArray?
|
||||
external fun nativePeriph28335BuildCommand(controller: Int, commandIndex: Int): ByteArray?
|
||||
external fun nativePeriph28335ExpectedReadSize(count: Int): Int
|
||||
external fun nativePeriph28335DecodeRead(input: ByteArray, controller: Int, count: Int): IntArray?
|
||||
external fun nativePeriph28335ValidateWrite(response: ByteArray, request: ByteArray): Int
|
||||
external fun nativePeriph28335ProjectCount(): Int
|
||||
external fun nativePeriph28335ProjectName(projectIndex: Int): String?
|
||||
external fun nativePeriph28335CommandName(projectIndex: Int, commandIndex: Int): String?
|
||||
external fun nativeEncodeFrame(
|
||||
sequence: Int,
|
||||
flags: Int,
|
||||
|
||||
@@ -1,37 +1,42 @@
|
||||
package ru.setcorp.setprotocol.periph28335
|
||||
|
||||
/** Shared wire protocol migrated from Set_Terminal_28335/DTrans.pas and UNiiefa.pas. */
|
||||
import ru.setcorp.setprotocol.NativeSetProtocol
|
||||
|
||||
/** Kotlin UI adapter; all PM35 wire logic and the command catalog live in C99. */
|
||||
object Periph28335Protocol {
|
||||
const val REGISTER_COUNT = 128
|
||||
const val DEFAULT_CONTROLLER = 16
|
||||
const val DEFAULT_BAUD_RATE = 115_200
|
||||
|
||||
val projectCommands: Map<String, List<String>> = linkedMapOf(
|
||||
"По умолчанию" to listOf("Test", "Def", "Save", "Load", "Calibr", "Calcul", "Secret", "Light", "Raw", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
|
||||
"23470" to listOf("Test", "Def", "Save", "Load", "Calibr", "Read", "Secret", "-", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
|
||||
"23550" to listOf("Test", "Def", "Save", "Load", "Calibr", "Read", "Secret", "Send", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
|
||||
"23550.2" to listOf("Test", "Def", "Save", "Load", "Calibr", "Calcul", "Secret", "Send", "Raw", "Beep", "", "", "", "", "Log", "Reset", "Nothing at all"),
|
||||
"ICE 22220.1-3" to listOf("Test", "Zero", "Save", "Def", "Calibr", "Read", "ExtLamp", "ExtLite", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
|
||||
"ICE 22220.4-5" to listOf("Test", "Def", "Save", "Load", "Raw", "Read", "ExtLamp", "ExtLite", "No log", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
|
||||
"Бальзам 161" to listOf("Test", "Zero", "Save", "Def", "Calibr", "Clbr 400", "Stop", "Start", "Init", "Secret", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
|
||||
"Бальзам 162" to listOf("Test", "Def", "Save", "Load", "Calibr", "Secret", "Stop", "Start", "Init", "Tune", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
|
||||
"Бальзам 163" to listOf("Test", "Def", "Save", "Load", "Calibr", "Calcul", "Stop", "Start", "Init", "Tune", "Secret", "-", "-", "-", "-", "Reset", "Nothing at all"),
|
||||
)
|
||||
|
||||
fun crc16Modbus(data: ByteArray, initial: Int = 0xFFFF): Int {
|
||||
var crc = initial and 0xFFFF
|
||||
data.forEach { value ->
|
||||
crc = crc xor (value.toInt() and 0xFF)
|
||||
repeat(8) {
|
||||
crc = if (crc and 1 != 0) (crc ushr 1) xor 0xA001 else crc ushr 1
|
||||
val projectCommands: Map<String, List<String>> by lazy {
|
||||
requireNative()
|
||||
buildMap {
|
||||
repeat(NativeSetProtocol.nativePeriph28335ProjectCount()) { project ->
|
||||
val name = requireNotNull(
|
||||
NativeSetProtocol.nativePeriph28335ProjectName(project),
|
||||
) { "Повреждён каталог проектов ПМ35" }
|
||||
put(name, List(17) { command ->
|
||||
requireNotNull(
|
||||
NativeSetProtocol.nativePeriph28335CommandName(project, command),
|
||||
) { "Повреждён каталог команд ПМ35" }
|
||||
})
|
||||
}
|
||||
}
|
||||
return crc and 0xFFFF
|
||||
}
|
||||
|
||||
fun crc16Modbus(data: ByteArray, initial: Int = 0xFFFF): Int {
|
||||
require(initial == 0xFFFF) {
|
||||
"Произвольное начальное значение CRC не входит в протокол ПМ35"
|
||||
}
|
||||
requireNative()
|
||||
return NativeSetProtocol.nativePeriph28335Crc16(data)
|
||||
}
|
||||
|
||||
fun withCrc(payload: ByteArray): ByteArray {
|
||||
val crc = crc16Modbus(payload)
|
||||
return payload + byteArrayOf(crc.toByte(), (crc ushr 8).toByte())
|
||||
requireNative()
|
||||
return requireNotNull(NativeSetProtocol.nativePeriph28335AppendCrc(payload)) {
|
||||
"SETProtocol отклонил данные ПМ35"
|
||||
}
|
||||
}
|
||||
|
||||
fun buildReadRegisters(controller: Int, start: Int, count: Int): ByteArray {
|
||||
@@ -40,50 +45,51 @@ object Periph28335Protocol {
|
||||
require(count in 1..REGISTER_COUNT && start + count <= REGISTER_COUNT) {
|
||||
"Диапазон регистров должен находиться в 0..127"
|
||||
}
|
||||
return withCrc(byteArrayOf(
|
||||
controller.toByte(), 3,
|
||||
(start ushr 8).toByte(), start.toByte(),
|
||||
(count ushr 8).toByte(), count.toByte(),
|
||||
))
|
||||
requireNative()
|
||||
return requireNotNull(
|
||||
NativeSetProtocol.nativePeriph28335BuildRead(controller, start, count),
|
||||
) { "SETProtocol отклонил запрос чтения ПМ35" }
|
||||
}
|
||||
|
||||
fun buildWriteRegister(controller: Int, address: Int, value: Int): ByteArray {
|
||||
requireRange("Адрес контроллера", controller, 0xFF)
|
||||
requireRange("Адрес регистра", address, REGISTER_COUNT - 1)
|
||||
requireRange("Значение", value, 0xFFFF)
|
||||
return withCrc(byteArrayOf(
|
||||
controller.toByte(), 6,
|
||||
(address ushr 8).toByte(), address.toByte(),
|
||||
(value ushr 8).toByte(), value.toByte(),
|
||||
))
|
||||
requireNative()
|
||||
return requireNotNull(
|
||||
NativeSetProtocol.nativePeriph28335BuildWrite(controller, address, value),
|
||||
) { "SETProtocol отклонил запрос записи ПМ35" }
|
||||
}
|
||||
|
||||
fun buildCommand(controller: Int, commandIndex: Int): ByteArray {
|
||||
require(commandIndex in 0..16) { "Номер команды должен быть в диапазоне 0..16" }
|
||||
return buildWriteRegister(controller, 127, if (commandIndex < 16) 1 shl commandIndex else 0)
|
||||
requireNative()
|
||||
return requireNotNull(
|
||||
NativeSetProtocol.nativePeriph28335BuildCommand(controller, commandIndex),
|
||||
) { "SETProtocol отклонил команду ПМ35" }
|
||||
}
|
||||
|
||||
fun expectedReadResponseSize(count: Int): Int {
|
||||
require(count in 1..REGISTER_COUNT)
|
||||
return count * 2 + 5
|
||||
requireNative()
|
||||
return NativeSetProtocol.nativePeriph28335ExpectedReadSize(count)
|
||||
}
|
||||
|
||||
fun decodeReadResponse(data: ByteArray, controller: Int, count: Int): List<Int> {
|
||||
val expected = expectedReadResponseSize(count)
|
||||
require(data.size == expected) { "Ожидалось $expected байт, получено ${data.size}" }
|
||||
validateCrc(data)
|
||||
require(data[0].toInt() and 0xFF == controller) { "Ответ другого контроллера" }
|
||||
require(data[1].toInt() and 0xFF == 3) { "Неверная функция ответа" }
|
||||
require(data[2].toInt() and 0xFF == count * 2) { "Неверная длина данных ответа" }
|
||||
return (0 until count).map { index ->
|
||||
val offset = 3 + index * 2
|
||||
((data[offset].toInt() and 0xFF) shl 8) or (data[offset + 1].toInt() and 0xFF)
|
||||
}
|
||||
requireNative()
|
||||
return requireNotNull(
|
||||
NativeSetProtocol.nativePeriph28335DecodeRead(data, controller, count),
|
||||
) { "Повреждён ответ ПМ35: заголовок, длина или CRC" }.toList()
|
||||
}
|
||||
|
||||
fun validateWriteResponse(data: ByteArray, request: ByteArray): Boolean =
|
||||
data.size == 8 && request.size == 8 && data.contentEquals(request) && runCatching { validateCrc(data) }.isSuccess
|
||||
fun validateWriteResponse(data: ByteArray, request: ByteArray): Boolean {
|
||||
requireNative()
|
||||
return NativeSetProtocol.nativePeriph28335ValidateWrite(data, request) == 0
|
||||
}
|
||||
|
||||
// Presentation-only conversions stay in the GUI port; they do not define wire bytes.
|
||||
fun bitsLsbFirst(value: Int): List<Boolean> {
|
||||
requireRange("Значение", value, 0xFFFF)
|
||||
return (0 until 16).map { bit -> value and (1 shl bit) != 0 }
|
||||
@@ -91,7 +97,9 @@ object Periph28335Protocol {
|
||||
|
||||
fun wordFromBits(bits: List<Boolean>): Int {
|
||||
require(bits.size == 16) { "Должно быть ровно 16 бит" }
|
||||
return bits.foldIndexed(0) { bit, value, checked -> if (checked) value or (1 shl bit) else value }
|
||||
return bits.foldIndexed(0) { bit, value, checked ->
|
||||
if (checked) value or (1 shl bit) else value
|
||||
}
|
||||
}
|
||||
|
||||
fun signedWord(value: Int): Int {
|
||||
@@ -99,11 +107,8 @@ object Periph28335Protocol {
|
||||
return if (value < 0x8000) value else value - 0x10000
|
||||
}
|
||||
|
||||
private fun validateCrc(data: ByteArray) {
|
||||
require(data.size >= 2)
|
||||
val expected = crc16Modbus(data.copyOf(data.size - 2))
|
||||
val actual = (data[data.lastIndex - 1].toInt() and 0xFF) or ((data.last().toInt() and 0xFF) shl 8)
|
||||
require(actual == expected) { "Ошибка CRC ответа" }
|
||||
private fun requireNative() {
|
||||
check(NativeSetProtocol.available) { "Нативное ядро SETProtocol недоступно" }
|
||||
}
|
||||
|
||||
private fun requireRange(name: String, value: Int, maximum: Int) {
|
||||
|
||||
@@ -7,6 +7,182 @@
|
||||
#include "set_trends.h"
|
||||
#include "set_spectrum.h"
|
||||
#include "balsam_can.h"
|
||||
#include "periph28335.h"
|
||||
|
||||
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(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "pcan_crc.h"
|
||||
#include "balsam_can.h"
|
||||
#include "periph28335.h"
|
||||
#include "pcan_frame.h"
|
||||
#include "pcan_id.h"
|
||||
#include "gui_frame.h"
|
||||
@@ -88,6 +89,79 @@ size_t pcan_abi_balsam_register_name(uint8_t device, uint16_t address,
|
||||
return balsam_can_register_name(device, address, output, output_size);
|
||||
}
|
||||
|
||||
uint16_t pcan_abi_periph28335_crc16(const uint8_t *data, size_t size)
|
||||
{
|
||||
return periph28335_crc16_modbus(data, size);
|
||||
}
|
||||
|
||||
size_t pcan_abi_periph28335_append_crc(
|
||||
const uint8_t *payload, size_t payload_size,
|
||||
uint8_t *output, size_t output_size)
|
||||
{
|
||||
return periph28335_append_crc(payload, payload_size, output, output_size);
|
||||
}
|
||||
|
||||
size_t pcan_abi_periph28335_build_read(
|
||||
uint8_t controller, uint16_t start, uint16_t count,
|
||||
uint8_t *output, size_t output_size)
|
||||
{
|
||||
return periph28335_build_read_registers(
|
||||
controller, start, count, output, output_size);
|
||||
}
|
||||
|
||||
size_t pcan_abi_periph28335_build_write(
|
||||
uint8_t controller, uint16_t address, uint16_t value,
|
||||
uint8_t *output, size_t output_size)
|
||||
{
|
||||
return periph28335_build_write_register(
|
||||
controller, address, value, output, output_size);
|
||||
}
|
||||
|
||||
size_t pcan_abi_periph28335_build_command(
|
||||
uint8_t controller, uint8_t command_index,
|
||||
uint8_t *output, size_t output_size)
|
||||
{
|
||||
return periph28335_build_command(
|
||||
controller, command_index, output, output_size);
|
||||
}
|
||||
|
||||
size_t pcan_abi_periph28335_expected_read_size(uint16_t count)
|
||||
{
|
||||
return periph28335_expected_read_response_size(count);
|
||||
}
|
||||
|
||||
int pcan_abi_periph28335_decode_read(
|
||||
const uint8_t *data, size_t size, uint8_t controller, uint16_t count,
|
||||
uint16_t *output, size_t output_count)
|
||||
{
|
||||
return periph28335_decode_read_response(
|
||||
data, size, controller, count, output, output_count);
|
||||
}
|
||||
|
||||
int pcan_abi_periph28335_validate_write(
|
||||
const uint8_t *response, size_t response_size,
|
||||
const uint8_t *request, size_t request_size)
|
||||
{
|
||||
return periph28335_validate_write_response(
|
||||
response, response_size, request, request_size);
|
||||
}
|
||||
|
||||
size_t pcan_abi_periph28335_project_count(void)
|
||||
{
|
||||
return periph28335_project_count();
|
||||
}
|
||||
|
||||
const char *pcan_abi_periph28335_project_name(size_t project_index)
|
||||
{
|
||||
return periph28335_project_name(project_index);
|
||||
}
|
||||
|
||||
const char *pcan_abi_periph28335_command_name(
|
||||
size_t project_index, size_t command_index)
|
||||
{
|
||||
return periph28335_command_name(project_index, command_index);
|
||||
}
|
||||
|
||||
size_t pcan_abi_frame_encode(uint8_t sequence, uint8_t flags,
|
||||
uint32_t can_id, const uint8_t *data, uint8_t dlc,
|
||||
uint8_t *output, size_t output_size)
|
||||
|
||||
171
c/set-protocol/src/periph28335.c
Normal file
171
c/set-protocol/src/periph28335.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "periph28335.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
const char *project;
|
||||
const char *commands[PERIPH28335_COMMAND_COUNT];
|
||||
} periph28335_project_t;
|
||||
|
||||
static const periph28335_project_t projects[] = {
|
||||
{ "По умолчанию", { "Test", "Def", "Save", "Load", "Calibr", "Calcul", "Secret", "Light", "Raw", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
|
||||
{ "23470", { "Test", "Def", "Save", "Load", "Calibr", "Read", "Secret", "-", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
|
||||
{ "23550", { "Test", "Def", "Save", "Load", "Calibr", "Read", "Secret", "Send", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
|
||||
{ "23550.2", { "Test", "Def", "Save", "Load", "Calibr", "Calcul", "Secret", "Send", "Raw", "Beep", "", "", "", "", "Log", "Reset", "Nothing at all" } },
|
||||
{ "ICE 22220.1-3", { "Test", "Zero", "Save", "Def", "Calibr", "Read", "ExtLamp", "ExtLite", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
|
||||
{ "ICE 22220.4-5", { "Test", "Def", "Save", "Load", "Raw", "Read", "ExtLamp", "ExtLite", "No log", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
|
||||
{ "Бальзам 161", { "Test", "Zero", "Save", "Def", "Calibr", "Clbr 400", "Stop", "Start", "Init", "Secret", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
|
||||
{ "Бальзам 162", { "Test", "Def", "Save", "Load", "Calibr", "Secret", "Stop", "Start", "Init", "Tune", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
|
||||
{ "Бальзам 163", { "Test", "Def", "Save", "Load", "Calibr", "Calcul", "Stop", "Start", "Init", "Tune", "Secret", "-", "-", "-", "-", "Reset", "Nothing at all" } }
|
||||
};
|
||||
|
||||
static void put_be16(uint8_t *output, uint16_t value)
|
||||
{
|
||||
output[0] = (uint8_t)(value >> 8);
|
||||
output[1] = (uint8_t)value;
|
||||
}
|
||||
|
||||
static uint16_t get_be16(const uint8_t *input)
|
||||
{
|
||||
return (uint16_t)(((uint16_t)input[0] << 8) | input[1]);
|
||||
}
|
||||
|
||||
uint16_t periph28335_crc16_modbus(const uint8_t *data, size_t size)
|
||||
{
|
||||
uint16_t crc = 0xFFFFU;
|
||||
size_t index;
|
||||
uint8_t bit;
|
||||
if ((data == NULL) && (size != 0U)) return 0U;
|
||||
for (index = 0U; index < size; ++index) {
|
||||
crc ^= data[index];
|
||||
for (bit = 0U; bit < 8U; ++bit) {
|
||||
crc = (crc & 1U) != 0U
|
||||
? (uint16_t)((crc >> 1) ^ 0xA001U)
|
||||
: (uint16_t)(crc >> 1);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
size_t periph28335_append_crc(const uint8_t *payload, size_t payload_size,
|
||||
uint8_t *output, size_t output_size)
|
||||
{
|
||||
uint16_t crc;
|
||||
if ((output == NULL) || ((payload == NULL) && (payload_size != 0U)) ||
|
||||
(payload_size > output_size) || (output_size - payload_size < 2U)) {
|
||||
return 0U;
|
||||
}
|
||||
if (payload_size != 0U) memcpy(output, payload, payload_size);
|
||||
crc = periph28335_crc16_modbus(payload, payload_size);
|
||||
output[payload_size] = (uint8_t)crc;
|
||||
output[payload_size + 1U] = (uint8_t)(crc >> 8);
|
||||
return payload_size + 2U;
|
||||
}
|
||||
|
||||
size_t periph28335_build_read_registers(
|
||||
uint8_t controller, uint16_t start, uint16_t count,
|
||||
uint8_t *output, size_t output_size)
|
||||
{
|
||||
uint8_t payload[6];
|
||||
if ((count == 0U) || (count > PERIPH28335_REGISTER_COUNT) ||
|
||||
(start >= PERIPH28335_REGISTER_COUNT) ||
|
||||
((uint32_t)start + count > PERIPH28335_REGISTER_COUNT)) return 0U;
|
||||
payload[0] = controller;
|
||||
payload[1] = 3U;
|
||||
put_be16(&payload[2], start);
|
||||
put_be16(&payload[4], count);
|
||||
return periph28335_append_crc(payload, sizeof payload, output, output_size);
|
||||
}
|
||||
|
||||
size_t periph28335_build_write_register(
|
||||
uint8_t controller, uint16_t address, uint16_t value,
|
||||
uint8_t *output, size_t output_size)
|
||||
{
|
||||
uint8_t payload[6];
|
||||
if (address >= PERIPH28335_REGISTER_COUNT) return 0U;
|
||||
payload[0] = controller;
|
||||
payload[1] = 6U;
|
||||
put_be16(&payload[2], address);
|
||||
put_be16(&payload[4], value);
|
||||
return periph28335_append_crc(payload, sizeof payload, output, output_size);
|
||||
}
|
||||
|
||||
size_t periph28335_build_command(
|
||||
uint8_t controller, uint8_t command_index,
|
||||
uint8_t *output, size_t output_size)
|
||||
{
|
||||
uint16_t value;
|
||||
if (command_index >= PERIPH28335_COMMAND_COUNT) return 0U;
|
||||
value = command_index < 16U ? (uint16_t)(1UL << command_index) : 0U;
|
||||
return periph28335_build_write_register(
|
||||
controller, PERIPH28335_REGISTER_COUNT - 1U, value,
|
||||
output, output_size);
|
||||
}
|
||||
|
||||
size_t periph28335_expected_read_response_size(uint16_t count)
|
||||
{
|
||||
return (count >= 1U && count <= PERIPH28335_REGISTER_COUNT)
|
||||
? (size_t)count * 2U + 5U : 0U;
|
||||
}
|
||||
|
||||
int periph28335_decode_read_response(
|
||||
const uint8_t *data, size_t size, uint8_t controller, uint16_t count,
|
||||
uint16_t *output, size_t output_count)
|
||||
{
|
||||
size_t expected = periph28335_expected_read_response_size(count);
|
||||
uint16_t crc;
|
||||
size_t index;
|
||||
if ((data == NULL) || (output == NULL)) return PERIPH28335_ERROR_ARGUMENT;
|
||||
if (expected == 0U) return PERIPH28335_ERROR_RANGE;
|
||||
if (size != expected) return PERIPH28335_ERROR_LENGTH;
|
||||
if (output_count < count) return PERIPH28335_ERROR_CAPACITY;
|
||||
crc = periph28335_crc16_modbus(data, size - 2U);
|
||||
if ((data[size - 2U] != (uint8_t)crc) ||
|
||||
(data[size - 1U] != (uint8_t)(crc >> 8))) {
|
||||
return PERIPH28335_ERROR_CRC;
|
||||
}
|
||||
if ((data[0] != controller) || (data[1] != 3U) ||
|
||||
(data[2] != (uint8_t)(count * 2U))) {
|
||||
return PERIPH28335_ERROR_HEADER;
|
||||
}
|
||||
for (index = 0U; index < count; ++index) {
|
||||
output[index] = get_be16(&data[3U + index * 2U]);
|
||||
}
|
||||
return PERIPH28335_OK;
|
||||
}
|
||||
|
||||
int periph28335_validate_write_response(
|
||||
const uint8_t *response, size_t response_size,
|
||||
const uint8_t *request, size_t request_size)
|
||||
{
|
||||
uint16_t crc;
|
||||
if ((response == NULL) || (request == NULL)) return PERIPH28335_ERROR_ARGUMENT;
|
||||
if ((response_size != PERIPH28335_REQUEST_SIZE) ||
|
||||
(request_size != PERIPH28335_REQUEST_SIZE)) return PERIPH28335_ERROR_LENGTH;
|
||||
crc = periph28335_crc16_modbus(response, response_size - 2U);
|
||||
if ((response[response_size - 2U] != (uint8_t)crc) ||
|
||||
(response[response_size - 1U] != (uint8_t)(crc >> 8))) {
|
||||
return PERIPH28335_ERROR_CRC;
|
||||
}
|
||||
return memcmp(response, request, PERIPH28335_REQUEST_SIZE) == 0
|
||||
? PERIPH28335_OK : PERIPH28335_ERROR_HEADER;
|
||||
}
|
||||
|
||||
size_t periph28335_project_count(void)
|
||||
{
|
||||
return sizeof projects / sizeof projects[0];
|
||||
}
|
||||
|
||||
const char *periph28335_project_name(size_t project_index)
|
||||
{
|
||||
return project_index < periph28335_project_count()
|
||||
? projects[project_index].project : NULL;
|
||||
}
|
||||
|
||||
const char *periph28335_command_name(size_t project_index,
|
||||
size_t command_index)
|
||||
{
|
||||
return project_index < periph28335_project_count() &&
|
||||
command_index < PERIPH28335_COMMAND_COUNT
|
||||
? projects[project_index].commands[command_index] : NULL;
|
||||
}
|
||||
32
c/set-protocol/tests/test_periph28335.c
Normal file
32
c/set-protocol/tests/test_periph28335.c
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "periph28335.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t request[PERIPH28335_REQUEST_SIZE];
|
||||
static const uint8_t read_prefix[] = { 0x10U, 0x03U, 0x00U, 0x18U, 0x00U, 0x40U };
|
||||
static const uint8_t response_payload[] = { 0x10U, 0x03U, 0x04U, 0x80U, 0x05U, 0x12U, 0x34U };
|
||||
uint8_t response[sizeof response_payload + 2U];
|
||||
uint16_t words[2];
|
||||
|
||||
if (periph28335_build_read_registers(16U, 24U, 64U, request, sizeof request)
|
||||
!= sizeof request || memcmp(request, read_prefix, sizeof read_prefix) != 0) return 1;
|
||||
if (periph28335_build_write_register(16U, 7U, 0x1234U, request, sizeof request)
|
||||
!= sizeof request || memcmp(request, "\x10\x06\x00\x07\x12\x34", 6U) != 0) return 2;
|
||||
if (periph28335_build_command(16U, 15U, request, sizeof request)
|
||||
!= sizeof request || memcmp(request, "\x10\x06\x00\x7F\x80\x00", 6U) != 0) return 3;
|
||||
if (periph28335_append_crc(response_payload, sizeof response_payload,
|
||||
response, sizeof response) != sizeof response) return 4;
|
||||
if (periph28335_decode_read_response(response, sizeof response, 16U, 2U,
|
||||
words, 2U) != PERIPH28335_OK || words[0] != 0x8005U || words[1] != 0x1234U) return 5;
|
||||
response[0] = 17U;
|
||||
if (periph28335_decode_read_response(response, sizeof response, 16U, 2U,
|
||||
words, 2U) != PERIPH28335_ERROR_CRC) return 6;
|
||||
if (periph28335_project_count() != 9U ||
|
||||
strcmp(periph28335_project_name(2U), "23550") != 0 ||
|
||||
strcmp(periph28335_command_name(2U, 7U), "Send") != 0) return 7;
|
||||
puts("PM35/TMS320F28335 protocol tests passed");
|
||||
return 0;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ JNI_INCLUDES: list[Path] = []
|
||||
SOURCES = [
|
||||
ROOT / "src" / name for name in (
|
||||
"set_protocol.c", "set_can.c", "set_firmware.c", "set_telemetry.c", "set_plot.c", "set_trends.c", "set_spectrum.c",
|
||||
"balsam_can.c", "gui_catalog.c", "gui_frame.c", "pcan_abi.c", "pcan_crc.c",
|
||||
"balsam_can.c", "periph28335.c", "gui_catalog.c", "gui_frame.c", "pcan_abi.c", "pcan_crc.c",
|
||||
"pcan_frame.c", "pcan_id.c", "pcan_link.c", "pcan_ring.c",
|
||||
"pcan_gas.c",
|
||||
)
|
||||
@@ -84,7 +84,10 @@ def main() -> int:
|
||||
if not (include / "jni.h").is_file():
|
||||
parser.error("--java-home must contain include/jni.h")
|
||||
JNI_INCLUDES.extend([include, include / {"Windows": "win32", "Darwin": "darwin"}.get(platform.system(), "linux")])
|
||||
SOURCES.append(ROOT / "ports" / "android" / "set_plot_jni.c")
|
||||
SOURCES.extend([
|
||||
ROOT / "ports" / "android" / "set_plot_jni.c",
|
||||
ROOT / "ports" / "android" / "setprotocol_jni.c",
|
||||
])
|
||||
output = args.output.resolve()
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
build_dir = output.parent / ".setprotocol-build"
|
||||
|
||||
Reference in New Issue
Block a user