diff --git a/c/set-protocol/CMakeLists.txt b/c/set-protocol/CMakeLists.txt index b0f027d..f757905 100644 --- a/c/set-protocol/CMakeLists.txt +++ b/c/set-protocol/CMakeLists.txt @@ -18,7 +18,9 @@ set(SETPROTOCOL_V2_SOURCES # Совместимые ProtoCAN/SETGUI v1 форматы переходного периода. set(SETPROTOCOL_LEGACY_SOURCES src/balsam_can.c + src/set_crc.c src/periph28335.c + src/tms2812.c src/gui_catalog.c src/gui_frame.c src/pcan_abi.c @@ -102,6 +104,9 @@ if(SETP_BUILD_TESTS) 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_tms2812 tests/test_tms2812.c) + target_link_libraries(test_tms2812 PRIVATE setprotocol_static) + add_test(NAME shared_tms2812 COMMAND test_tms2812) add_executable(test_trends tests/test_trends.c) target_link_libraries(test_trends PRIVATE setprotocol_static) add_test(NAME shared_trends COMMAND test_trends) diff --git a/c/set-protocol/include/pcan_abi.h b/c/set-protocol/include/pcan_abi.h index 2205331..7413a84 100644 --- a/c/set-protocol/include/pcan_abi.h +++ b/c/set-protocol/include/pcan_abi.h @@ -101,6 +101,21 @@ PCAN_ABI_API const char *pcan_abi_periph28335_project_name( PCAN_ABI_API const char *pcan_abi_periph28335_command_name( size_t project_index, size_t command_index); +/* PM67/TMS320F2812 legacy memory upload. */ +PCAN_ABI_API uint16_t pcan_abi_tms2812_crc16( + const uint8_t *data, size_t size); +PCAN_ABI_API size_t pcan_abi_tms2812_build_upload( + uint8_t controller, uint32_t word_address, uint32_t byte_count, + uint8_t *output, size_t output_size); +PCAN_ABI_API size_t pcan_abi_tms2812_expected_upload_size( + uint32_t byte_count); +PCAN_ABI_API int pcan_abi_tms2812_validate_upload( + const uint8_t *data, size_t size, uint8_t controller, + uint32_t byte_count); +PCAN_ABI_API int pcan_abi_tms2812_decode_upload( + const uint8_t *data, size_t size, uint8_t controller, + uint32_t byte_count, uint8_t *output, size_t output_size); + 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, diff --git a/c/set-protocol/include/set_crc.h b/c/set-protocol/include/set_crc.h new file mode 100644 index 0000000..efa9cf0 --- /dev/null +++ b/c/set-protocol/include/set_crc.h @@ -0,0 +1,19 @@ +/** @file set_crc.h @brief Shared checksums used by legacy SET controllers. */ +#ifndef SET_CRC_H +#define SET_CRC_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** CRC-16/Modbus: reflected polynomial 0xA001, initial value 0xFFFF. */ +uint16_t set_crc16_modbus(const uint8_t *data, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* SET_CRC_H */ diff --git a/c/set-protocol/include/setprotocol.h b/c/set-protocol/include/setprotocol.h index 03520c3..73b6929 100644 --- a/c/set-protocol/include/setprotocol.h +++ b/c/set-protocol/include/setprotocol.h @@ -7,6 +7,7 @@ #include "balsam_can.h" #include "periph28335.h" +#include "tms2812.h" /* Основной SET protocol v2. */ #include "set_protocol.h" diff --git a/c/set-protocol/include/tms2812.h b/c/set-protocol/include/tms2812.h new file mode 100644 index 0000000..d40ee56 --- /dev/null +++ b/c/set-protocol/include/tms2812.h @@ -0,0 +1,49 @@ +/** + * @file tms2812.h + * @brief Shared PM67/TMS320F2812 legacy upload protocol. + */ +#ifndef TMS2812_H +#define TMS2812_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TMS2812_CMD_UPLOAD 52U +#define TMS2812_UPLOAD_REQUEST_SIZE 12U +#define TMS2812_UPLOAD_RESPONSE_OVERHEAD 8U + +typedef enum { + TMS2812_OK = 0, + TMS2812_ERROR_ARGUMENT = -1, + TMS2812_ERROR_RANGE = -2, + TMS2812_ERROR_LENGTH = -3, + TMS2812_ERROR_CRC = -4, + TMS2812_ERROR_HEADER = -5, + TMS2812_ERROR_CAPACITY = -6 +} tms2812_status_t; + +uint16_t tms2812_crc16(const uint8_t *data, size_t size); + +size_t tms2812_build_upload_request( + uint8_t controller, uint32_t word_address, uint32_t byte_count, + uint8_t *output, size_t output_size); + +size_t tms2812_expected_upload_response_size(uint32_t byte_count); + +int tms2812_validate_upload_response( + const uint8_t *data, size_t size, uint8_t controller, + uint32_t byte_count); + +int tms2812_decode_upload_response( + const uint8_t *data, size_t size, uint8_t controller, + uint32_t byte_count, uint8_t *output, size_t output_size); + +#ifdef __cplusplus +} +#endif + +#endif /* TMS2812_H */ diff --git a/c/set-protocol/ports/android/Android.mk b/c/set-protocol/ports/android/Android.mk index 9eceeb8..530093f 100644 --- a/c/set-protocol/ports/android/Android.mk +++ b/c/set-protocol/ports/android/Android.mk @@ -13,7 +13,9 @@ LOCAL_SRC_FILES := \ ../../src/set_trends.c \ ../../src/set_spectrum.c \ ../../src/balsam_can.c \ + ../../src/set_crc.c \ ../../src/periph28335.c \ + ../../src/tms2812.c \ ../../src/gui_catalog.c \ ../../src/gui_frame.c \ ../../src/pcan_abi.c \ diff --git a/c/set-protocol/ports/android/kotlin/ru/setcorp/setprotocol/NativeSetProtocol.kt b/c/set-protocol/ports/android/kotlin/ru/setcorp/setprotocol/NativeSetProtocol.kt index 8aa7255..4e7b830 100644 --- a/c/set-protocol/ports/android/kotlin/ru/setcorp/setprotocol/NativeSetProtocol.kt +++ b/c/set-protocol/ports/android/kotlin/ru/setcorp/setprotocol/NativeSetProtocol.kt @@ -46,6 +46,11 @@ object NativeSetProtocol { external fun nativePeriph28335ProjectCount(): Int external fun nativePeriph28335ProjectName(projectIndex: Int): String? external fun nativePeriph28335CommandName(projectIndex: Int, commandIndex: Int): String? + external fun nativeTms2812Crc16(input: ByteArray): Int + external fun nativeTms2812BuildUpload(controller: Int, wordAddress: Long, byteCount: Long): ByteArray? + external fun nativeTms2812ExpectedUploadSize(byteCount: Long): Int + external fun nativeTms2812ValidateUpload(input: ByteArray, controller: Int, byteCount: Int): Boolean + external fun nativeTms2812DecodeUpload(input: ByteArray, controller: Int, byteCount: Int): ByteArray? external fun nativeEncodeFrame( sequence: Int, flags: Int, diff --git a/c/set-protocol/ports/android/setprotocol_jni.c b/c/set-protocol/ports/android/setprotocol_jni.c index 7f916f7..d95566a 100644 --- a/c/set-protocol/ports/android/setprotocol_jni.c +++ b/c/set-protocol/ports/android/setprotocol_jni.c @@ -8,6 +8,97 @@ #include "set_spectrum.h" #include "balsam_can.h" #include "periph28335.h" +#include "tms2812.h" + +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( diff --git a/c/set-protocol/src/pcan_abi.c b/c/set-protocol/src/pcan_abi.c index adf58a1..800b840 100644 --- a/c/set-protocol/src/pcan_abi.c +++ b/c/set-protocol/src/pcan_abi.c @@ -5,6 +5,7 @@ #include "pcan_crc.h" #include "balsam_can.h" #include "periph28335.h" +#include "tms2812.h" #include "pcan_frame.h" #include "pcan_id.h" #include "gui_frame.h" @@ -162,6 +163,40 @@ const char *pcan_abi_periph28335_command_name( return periph28335_command_name(project_index, command_index); } +uint16_t pcan_abi_tms2812_crc16(const uint8_t *data, size_t size) +{ + return tms2812_crc16(data, size); +} + +size_t pcan_abi_tms2812_build_upload( + uint8_t controller, uint32_t word_address, uint32_t byte_count, + uint8_t *output, size_t output_size) +{ + return tms2812_build_upload_request( + controller, word_address, byte_count, output, output_size); +} + +size_t pcan_abi_tms2812_expected_upload_size(uint32_t byte_count) +{ + return tms2812_expected_upload_response_size(byte_count); +} + +int pcan_abi_tms2812_validate_upload( + const uint8_t *data, size_t size, uint8_t controller, + uint32_t byte_count) +{ + return tms2812_validate_upload_response( + data, size, controller, byte_count); +} + +int pcan_abi_tms2812_decode_upload( + const uint8_t *data, size_t size, uint8_t controller, + uint32_t byte_count, uint8_t *output, size_t output_size) +{ + return tms2812_decode_upload_response( + data, size, controller, byte_count, output, output_size); +} + 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) diff --git a/c/set-protocol/src/periph28335.c b/c/set-protocol/src/periph28335.c index dee241f..3641ba3 100644 --- a/c/set-protocol/src/periph28335.c +++ b/c/set-protocol/src/periph28335.c @@ -2,6 +2,8 @@ #include +#include "set_crc.h" + typedef struct { const char *project; const char *commands[PERIPH28335_COMMAND_COUNT]; @@ -32,19 +34,7 @@ static uint16_t get_be16(const uint8_t *input) 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; + return set_crc16_modbus(data, size); } size_t periph28335_append_crc(const uint8_t *payload, size_t payload_size, diff --git a/c/set-protocol/src/set_crc.c b/c/set-protocol/src/set_crc.c new file mode 100644 index 0000000..65a2792 --- /dev/null +++ b/c/set-protocol/src/set_crc.c @@ -0,0 +1,18 @@ +#include "set_crc.h" + +uint16_t set_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; +} diff --git a/c/set-protocol/src/tms2812.c b/c/set-protocol/src/tms2812.c new file mode 100644 index 0000000..333923f --- /dev/null +++ b/c/set-protocol/src/tms2812.c @@ -0,0 +1,78 @@ +#include "tms2812.h" + +#include +#include + +#include "set_crc.h" + +static void put_le32(uint8_t *output, uint32_t value) +{ + output[0] = (uint8_t)value; + output[1] = (uint8_t)(value >> 8); + output[2] = (uint8_t)(value >> 16); + output[3] = (uint8_t)(value >> 24); +} + +uint16_t tms2812_crc16(const uint8_t *data, size_t size) +{ + return set_crc16_modbus(data, size); +} + +size_t tms2812_build_upload_request( + uint8_t controller, uint32_t word_address, uint32_t byte_count, + uint8_t *output, size_t output_size) +{ + uint16_t crc; + if ((output == NULL) || (output_size < TMS2812_UPLOAD_REQUEST_SIZE) || + (byte_count == 0U)) return 0U; + output[0] = controller; + output[1] = TMS2812_CMD_UPLOAD; + put_le32(&output[2], word_address); + put_le32(&output[6], byte_count); + crc = tms2812_crc16(output, 10U); + output[10] = (uint8_t)crc; + output[11] = (uint8_t)(crc >> 8); + return TMS2812_UPLOAD_REQUEST_SIZE; +} + +size_t tms2812_expected_upload_response_size(uint32_t byte_count) +{ + if (byte_count == 0U || + (uint64_t)byte_count + TMS2812_UPLOAD_RESPONSE_OVERHEAD > SIZE_MAX) { + return 0U; + } + return (size_t)byte_count + TMS2812_UPLOAD_RESPONSE_OVERHEAD; +} + +int tms2812_validate_upload_response( + const uint8_t *data, size_t size, uint8_t controller, + uint32_t byte_count) +{ + size_t expected = tms2812_expected_upload_response_size(byte_count); + size_t crc_offset; + uint16_t expected_crc; + uint16_t actual_crc; + if (data == NULL) return TMS2812_ERROR_ARGUMENT; + if (expected == 0U) return TMS2812_ERROR_RANGE; + if (size != expected) return TMS2812_ERROR_LENGTH; + if ((data[0] != controller) || (data[1] != TMS2812_CMD_UPLOAD)) + return TMS2812_ERROR_HEADER; + crc_offset = (size_t)byte_count + 2U; + expected_crc = tms2812_crc16(data, crc_offset); + actual_crc = (uint16_t)(data[crc_offset] | + ((uint16_t)data[crc_offset + 1U] << 8)); + return actual_crc == expected_crc ? TMS2812_OK : TMS2812_ERROR_CRC; +} + +int tms2812_decode_upload_response( + const uint8_t *data, size_t size, uint8_t controller, + uint32_t byte_count, uint8_t *output, size_t output_size) +{ + int status = tms2812_validate_upload_response( + data, size, controller, byte_count); + if (status != TMS2812_OK) return status; + if ((output == NULL) || (output_size < byte_count)) + return TMS2812_ERROR_CAPACITY; + memcpy(output, &data[2], byte_count); + return TMS2812_OK; +} diff --git a/c/set-protocol/tests/test_tms2812.c b/c/set-protocol/tests/test_tms2812.c new file mode 100644 index 0000000..50be35b --- /dev/null +++ b/c/set-protocol/tests/test_tms2812.c @@ -0,0 +1,35 @@ +#include +#include + +#include "tms2812.h" + +int main(void) +{ + uint8_t request[TMS2812_UPLOAD_REQUEST_SIZE]; + static const uint8_t prefix[] = { + 0x05U, 0x34U, 0x78U, 0x56U, 0x34U, 0x12U, + 0x00U, 0x10U, 0x00U, 0x00U + }; + uint8_t reply[4U + TMS2812_UPLOAD_RESPONSE_OVERHEAD]; + uint8_t decoded[4]; + uint16_t crc; + + if (tms2812_build_upload_request(5U, 0x12345678UL, 0x1000U, + request, sizeof request) != sizeof request || + memcmp(request, prefix, sizeof prefix) != 0) return 1; + reply[0] = 5U; + reply[1] = TMS2812_CMD_UPLOAD; + reply[2] = 0x10U; reply[3] = 0x20U; reply[4] = 0x30U; reply[5] = 0x40U; + crc = tms2812_crc16(reply, 6U); + reply[6] = (uint8_t)crc; + reply[7] = (uint8_t)(crc >> 8); + memset(&reply[8], 0, 4U); + if (tms2812_decode_upload_response(reply, sizeof reply, 5U, 4U, + decoded, sizeof decoded) != TMS2812_OK || + memcmp(decoded, &reply[2], sizeof decoded) != 0) return 2; + reply[6] ^= 1U; + if (tms2812_validate_upload_response(reply, sizeof reply, 5U, 4U) + != TMS2812_ERROR_CRC) return 3; + puts("PM67/TMS320F2812 upload protocol tests passed"); + return 0; +} diff --git a/c/set-protocol/tools/build_host.py b/c/set-protocol/tools/build_host.py index 4d6ed08..c586ddb 100644 --- a/c/set-protocol/tools/build_host.py +++ b/c/set-protocol/tools/build_host.py @@ -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", "periph28335.c", "gui_catalog.c", "gui_frame.c", "pcan_abi.c", "pcan_crc.c", + "balsam_can.c", "set_crc.c", "periph28335.c", "tms2812.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", ) diff --git a/doc/CROSS_PLATFORM_AUDIT.md b/doc/CROSS_PLATFORM_AUDIT.md index eaaba20..92bea7b 100644 --- a/doc/CROSS_PLATFORM_AUDIT.md +++ b/doc/CROSS_PLATFORM_AUDIT.md @@ -12,6 +12,7 @@ | CAN Бальзам | `c/set-protocol/src/balsam_can.c` | | График, тренды и спектр | `c/set-protocol/src/set_plot.c`, `set_trends.c`, `set_spectrum.c` | | ПМ35 / TMS320F28335: MODBUS-подобные запросы, CRC16/Modbus, ответы и каталог команд | `c/set-protocol/src/periph28335.c` | +| ПМ67 / TMS320F2812: CMD_UPLOAD, CRC16/Modbus и проверка ответа | `c/set-protocol/src/tms2812.c` | Python вызывает это ядро через `python/protocan/native.py`, Android — через `ports/android/setprotocol_jni.c`. Файлы на Python и Kotlin являются портами и @@ -24,7 +25,7 @@ Python вызывает это ядро через `python/protocan/native.py`, | Приоритет | Android | SETGUI | Что вынести в C | |---|---|---|---| | 0 | `ports/android/.../CanBridgeProtocol.kt`, `GuiProtocol.kt` | Python transport wrappers | удалить оставшиеся алгоритмические fallback, так как codec/parser уже есть в C | -| 1 | `core/Tms2812Protocol.kt` | `core/tms_flash.py`, `core/tms_terminal.py` | весь RS-протокол ПМ67/2812, CRC, команды и validators | +| 1 | остальные команды `core/Tms2812Protocol.kt` | `core/tms_terminal.py` | оставшиеся команды и parser непрерывной телеметрии ПМ67/2812; CMD_UPLOAD уже перенесён | | 2 | `core/Ds18b20Protocol.kt` | `core/ds18b20.py` | кодеки списков/данных/EEPROM, CRC8 и значения датчиков | | 3 | `core/CanFirmwareProtocol.kt` | `core/can_firmware.py` | автомат CAN boot, статусы, окна блоков | | 4 | `core/SlCanProtocol.kt` | `adapters/slcan_adapter.py` и CAN transport | ASCII codec/parser SLCAN; доступ к COM остаётся в портах | diff --git a/python/protocan/native.py b/python/protocan/native.py index b30f40f..4c2b131 100644 --- a/python/protocan/native.py +++ b/python/protocan/native.py @@ -177,6 +177,24 @@ class NativeProtocol: ctypes.c_size_t, ctypes.c_size_t, ] lib.pcan_abi_periph28335_command_name.restype = ctypes.c_char_p + lib.pcan_abi_tms2812_crc16.argtypes = [ctypes.c_void_p, ctypes.c_size_t] + lib.pcan_abi_tms2812_crc16.restype = ctypes.c_uint16 + lib.pcan_abi_tms2812_build_upload.argtypes = [ + ctypes.c_uint8, ctypes.c_uint32, ctypes.c_uint32, + ctypes.c_void_p, ctypes.c_size_t, + ] + lib.pcan_abi_tms2812_build_upload.restype = ctypes.c_size_t + lib.pcan_abi_tms2812_expected_upload_size.argtypes = [ctypes.c_uint32] + lib.pcan_abi_tms2812_expected_upload_size.restype = ctypes.c_size_t + lib.pcan_abi_tms2812_validate_upload.argtypes = [ + ctypes.c_void_p, ctypes.c_size_t, ctypes.c_uint8, ctypes.c_uint32, + ] + lib.pcan_abi_tms2812_validate_upload.restype = ctypes.c_int + lib.pcan_abi_tms2812_decode_upload.argtypes = [ + ctypes.c_void_p, ctypes.c_size_t, ctypes.c_uint8, ctypes.c_uint32, + ctypes.c_void_p, ctypes.c_size_t, + ] + lib.pcan_abi_tms2812_decode_upload.restype = ctypes.c_int lib.pcan_abi_frame_encode.argtypes = [ ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_uint8, ctypes.c_void_p, ctypes.c_size_t, @@ -335,6 +353,35 @@ class NativeProtocol: result[raw_name.decode("utf-8")] = tuple(commands) return result + def tms2812_crc16(self, data: bytes) -> int: + return int(self.lib.pcan_abi_tms2812_crc16( + self._bytes_buffer(data), len(data))) + + def tms2812_build_upload(self, controller: int, word_address: int, + byte_count: int) -> bytes: + output = (ctypes.c_uint8 * 12)() + size = int(self.lib.pcan_abi_tms2812_build_upload( + controller, word_address, byte_count, output, len(output))) + if size == 0: + raise ValueError("SETProtocol rejected PM67 upload request") + return bytes(output[:size]) + + def tms2812_expected_upload_size(self, byte_count: int) -> int: + return int(self.lib.pcan_abi_tms2812_expected_upload_size(byte_count)) + + def tms2812_validate_upload(self, data: bytes, controller: int, + byte_count: int) -> int: + return int(self.lib.pcan_abi_tms2812_validate_upload( + self._bytes_buffer(data), len(data), controller, byte_count)) + + def tms2812_decode_upload(self, data: bytes, controller: int, + byte_count: int) -> tuple[int, bytes]: + output = (ctypes.c_uint8 * byte_count)() + status = int(self.lib.pcan_abi_tms2812_decode_upload( + self._bytes_buffer(data), len(data), controller, byte_count, + output, len(output))) + return status, bytes(output) if status == 0 else b"" + def encode(self, sequence: int, flags: int, can_id: int, data: bytes) -> bytes: if len(data) > 8: raise ValueError("DLC cannot exceed 8 bytes") diff --git a/python/protocan/tms2812.py b/python/protocan/tms2812.py new file mode 100644 index 0000000..8bc2dd0 --- /dev/null +++ b/python/protocan/tms2812.py @@ -0,0 +1,60 @@ +"""Thin Python adapter for the shared C99 PM67/TMS320F2812 protocol.""" + +from __future__ import annotations + +from .native import get_native_protocol + + +CMD_UPLOAD = 52 +UPLOAD_RESPONSE_OVERHEAD = 8 + + +def _core(): + return get_native_protocol() + + +def crc16_modbus(data: bytes, initial: int = 0xFFFF) -> int: + if initial != 0xFFFF: + raise ValueError("Произвольное начальное значение CRC не входит в протокол ПМ67") + return _core().tms2812_crc16(bytes(data)) + + +def build_upload_request(controller: int, word_address: int, + byte_count: int) -> bytes: + _range("адрес контроллера", controller, 0xFF) + _range("адрес слова", word_address, 0xFFFFFFFF) + if not 1 <= byte_count <= 0xFFFFFFFF: + raise ValueError("размер блока вне диапазона 1..0xFFFFFFFF") + return _core().tms2812_build_upload(controller, word_address, byte_count) + + +def expected_upload_response_size(byte_count: int) -> int: + if not 1 <= byte_count <= 0xFFFFFFFF: + raise ValueError("размер блока вне диапазона 1..0xFFFFFFFF") + return _core().tms2812_expected_upload_size(byte_count) + + +def decode_upload_reply(raw: bytes, controller: int, byte_count: int) -> bytes: + expected = expected_upload_response_size(byte_count) + if len(raw) != expected: + raise ValueError( + f"ответ CMD_UPLOAD: ожидалось {expected} байт, получено {len(raw)}" + ) + status, data = _core().tms2812_decode_upload( + bytes(raw), controller, byte_count) + errors = { + -1: "неверные аргументы CMD_UPLOAD", + -2: "размер CMD_UPLOAD вне диапазона", + -3: "неверная длина ответа CMD_UPLOAD", + -4: "CRC ответа CMD_UPLOAD не совпадает", + -5: "ответ CMD_UPLOAD имеет неверный адрес или номер команды", + -6: "недостаточный буфер CMD_UPLOAD", + } + if status != 0: + raise ValueError(errors.get(status, f"ошибка ответа CMD_UPLOAD: {status}")) + return data + + +def _range(name: str, value: int, maximum: int) -> None: + if not 0 <= value <= maximum: + raise ValueError(f"{name} вне диапазона 0..{maximum}")