diff --git a/c/set-protocol/ports/android/kotlin/ru/setcorp/setflash/core/CanBridgeProtocol.kt b/c/set-protocol/ports/android/kotlin/ru/setcorp/setflash/core/CanBridgeProtocol.kt index 599c6df..103f7fd 100644 --- a/c/set-protocol/ports/android/kotlin/ru/setcorp/setflash/core/CanBridgeProtocol.kt +++ b/c/set-protocol/ports/android/kotlin/ru/setcorp/setflash/core/CanBridgeProtocol.kt @@ -265,31 +265,14 @@ data class ProtoCanId( val messageType: Int, val body: Int, ) { - val messageTypeName: String get() = MESSAGE_TYPES[messageType] ?: "Reserved 0x${messageType.toString(16).uppercase()}" + val messageTypeName: String get() = ProtoCanMessageType.fromCode(messageType)?.title + ?: "Reserved 0x${messageType.toString(16).uppercase()}" val deviceName: String? get() = DEVICE_NAMES[device] fun summary(): String = "P$priority · PM$pm · Type $deviceType · Dev $device" + (deviceName?.let { " ($it)" } ?: "") + " · $messageTypeName · Body 0x%04X".format(body) companion object { - private val MESSAGE_TYPES = mapOf( - 0x0 to "Broadcast", - 0x1 to "Discrete", - 0x2 to "Analog", - 0x3 to "General Address Space", - 0x4 to "Modbus Coil", - 0x5 to "Modbus Discrete", - 0x6 to "Modbus Holding", - 0x7 to "Modbus Input", - 0x8 to "Error", - 0x9 to "Boot Control", - 0xA to "Boot Data A", - 0xB to "Boot Data B", - 0xC to "Boot Status", - 0xD to "Boot Discovery", - 0xE to "Settings", - 0xF to "Pulse", - ) private val DEVICE_NAMES = mapOf( 0xD to "DS_CONTROL", 0xE to "Android GUI", diff --git a/c/set-protocol/ports/android/kotlin/ru/setcorp/setflash/core/ProtoCanCatalog.kt b/c/set-protocol/ports/android/kotlin/ru/setcorp/setflash/core/ProtoCanCatalog.kt new file mode 100644 index 0000000..385865f --- /dev/null +++ b/c/set-protocol/ports/android/kotlin/ru/setcorp/setflash/core/ProtoCanCatalog.kt @@ -0,0 +1,67 @@ +package ru.setcorp.setflash.core + +/** Canonical ProtoCAN message-type registry shared by Android protocol clients. */ +enum class ProtoCanMessageType(val code: Int, val title: String) { + BROADCAST(0x0, "Broadcast"), + DISCRETE(0x1, "Discrete"), + ANALOG(0x2, "Analog"), + GENERAL_ADDRESS_SPACE(0x3, "General Address Space"), + MODBUS_COIL(0x4, "Modbus Coil"), + MODBUS_DISCRETE(0x5, "Modbus Discrete"), + MODBUS_HOLDING(0x6, "Modbus Holding"), + MODBUS_INPUT(0x7, "Modbus Input"), + ERROR(0x8, "Error"), + BOOT_CONTROL(0x9, "Boot Control"), + BOOT_DATA_A(0xA, "Boot Data A"), + BOOT_DATA_B(0xB, "Boot Data B"), + BOOT_STATUS(0xC, "Boot Status"), + BOOT_DISCOVERY(0xD, "Boot Discovery"), + SETTINGS(0xE, "Settings"), + PULSE(0xF, "Pulse"); + + companion object { + fun fromCode(code: Int): ProtoCanMessageType? = entries.firstOrNull { it.code == code } + } +} + +enum class ProtoCanBroadcastType(val code: Int, val title: String) { + STATUS(0x0, "STATUS · запрос статуса"), + ONOFF(0x1, "ONOFF · вкл/выкл пульса"), + RESTART_DEVICE(0x2, "RESTARTDEVICE · перезапуск"), + RTC_SETUP(0x3, "RTCSETUP · установка RTC"), + END(0xFFF, "END · конец диапазона"), +} + +enum class ProtoCanDiscreteType(val code: Int, val title: String) { + ACCIDENT(0x0, "ACCIDENT · авария"), + WARNING(0x1, "WARNING · предупреждение"), + CONTROL_SIGNALS(0x2, "CONTROL_SIGNALS · управление"), + FLAGS(0x3, "FLAGS · флаги"), + RESET(0x4, "RESET · сброс"), + CHANGE_MODE(0x5, "CHANGE_MODE · смена режима"), + REQUEST_PARAMETERS(0x6, "REQUEST_LIST_OF_PARAMETERS"), + END(0xF, "END · конец диапазона"), +} + +enum class ProtoCanAnalogType(val code: Int, val title: String) { + UNIVERSAL(0x0, "UNIVERSAL · универсальный"), + SETTINGS(0x1, "SETTINGS · уставки"), + U(0x2, "U · напряжение"), + I(0x3, "I · ток"), + T(0x4, "T · температура"), + END(0xF, "END · конец диапазона"), +} + +/** Portable MsgBody layouts matching `pcan_id.h` and SETGUI's frame builder. */ +object ProtoCanBody { + fun broadcast(type: Int, body: Int): Int = ((type and 0xFFF) shl 4) or (body and 0xF) + + fun typed(type: Int, body: Int): Int = ((type and 0xF) shl 12) or (body and 0xFFF) + + fun modbus(address: Int, registerCount: Int): Int = + ((address and 0xFFF) shl 4) or (registerCount and 0xF) + + fun error(info: Int, code: Int): Int = ((info and 0xFF) shl 8) or (code and 0xFF) + + fun settings(z: Int, y: Int): Int = ((z and 0xFF) shl 8) or (y and 0xFF) +} diff --git a/c/set-protocol/ports/android/tests/ru/setcorp/setflash/core/ProtoCanCatalogTest.kt b/c/set-protocol/ports/android/tests/ru/setcorp/setflash/core/ProtoCanCatalogTest.kt new file mode 100644 index 0000000..4b682d5 --- /dev/null +++ b/c/set-protocol/ports/android/tests/ru/setcorp/setflash/core/ProtoCanCatalogTest.kt @@ -0,0 +1,21 @@ +package ru.setcorp.setflash.core + +import org.junit.Assert.assertEquals +import org.junit.Test + +class ProtoCanCatalogTest { + @Test + fun bodyLayoutsMatchSetGuiBuilder() { + assertEquals(0x1234, ProtoCanBody.broadcast(0x123, 0x4)) + assertEquals(0x2001, ProtoCanBody.typed(ProtoCanAnalogType.U.code, 1)) + assertEquals(0x1234, ProtoCanBody.modbus(0x123, 4)) + assertEquals(0xABCD, ProtoCanBody.error(0xAB, 0xCD)) + assertEquals(0xFF01, ProtoCanBody.settings(0xFF, 0x01)) + } + + @Test + fun messageRegistryCoversAllFourBitValues() { + assertEquals((0..0xF).toList(), ProtoCanMessageType.entries.map { it.code }) + assertEquals("Settings", ProtoCanId.parse(0x17FE1234).messageTypeName) + } +}