From 050be2f4d4ea2fd61e328989605eccb4ac6ecad5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 15 Jul 2024 12:51:39 +0200 Subject: [PATCH 001/359] [Updater] Update logback to 1.5.6 --- firmware_updater/updater/source/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 8887a4e4..6a7ada44 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -76,7 +76,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.5.0' // console and file logging - implementation 'ch.qos.logback:logback-classic:1.4.12' + implementation 'ch.qos.logback:logback-classic:1.5.6' // unit test testImplementation group: 'junit', name: 'junit', version: '4.13.2' From bdb510dbca473abcec04aa61a75c72c7f78bd372 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 15 Jul 2024 12:53:35 +0200 Subject: [PATCH 002/359] [Updater] Update Google Guava to 33.2.1 --- firmware_updater/updater/source/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 6a7ada44..83335816 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -70,7 +70,7 @@ dependencies { implementation 'net.harawata:appdirs:1.2.1' // For search in byte array - implementation 'com.google.guava:guava:32.0.0-jre' + implementation 'com.google.guava:guava:33.2.1-jre' // command line option implementation 'commons-cli:commons-cli:1.5.0' From 6951466262c8c4324de0260d1127bb9c029e7c11 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 15 Jul 2024 12:58:42 +0200 Subject: [PATCH 003/359] [Updater] Update gradle 8.9 --- .../updater/source/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/gradle/wrapper/gradle-wrapper.properties b/firmware_updater/updater/source/gradle/wrapper/gradle-wrapper.properties index 3499ded5..171d8761 100644 --- a/firmware_updater/updater/source/gradle/wrapper/gradle-wrapper.properties +++ b/firmware_updater/updater/source/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From af2f1a5511d5e4feb637065eec5a8e9bcc442ffe Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 15 Jul 2024 13:24:05 +0200 Subject: [PATCH 004/359] [Updater] Update calimero to 2.6-rc1 --- firmware_updater/updater/source/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 83335816..cdaf98ae 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -61,10 +61,10 @@ gradle.projectsEvaluated{ dependencies { // calimero knx bus access library - implementation 'com.github.calimero:calimero-core:2.5.1' + implementation 'com.github.calimero:calimero-core:2.6-rc1' // calimero serial tx/rx lib for ft1.2 and tpuart support - implementation 'com.github.calimero:calimero-rxtx:2.5.1' + implementation 'com.github.calimero:calimero-rxtx:2.6-rc1' // find specific directories under linux and windows implementation 'net.harawata:appdirs:1.2.1' From da88ac74166454723ccaed4bbe0786f5a04b7c37 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 15 Jul 2024 13:25:51 +0200 Subject: [PATCH 005/359] [Updater] Fix calimero deprecation warnings --- .../source/src/main/java/org/selfbus/updater/SBKNXLink.java | 6 +----- .../src/main/java/org/selfbus/updater/gui/GuiMain.java | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index e9497a82..32d93392 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -96,11 +96,7 @@ public KNXNetworkLink openLink() throws KNXException, logger.debug("Creating KNX network link {}...", medium); if (!cliOptions.ft12().isEmpty()) { // create FT1.2 network link - try { - return new KNXNetworkLinkFT12(Integer.parseInt(cliOptions.ft12()), medium); - } catch (final NumberFormatException e) { - return new KNXNetworkLinkFT12(cliOptions.ft12(), medium); - } + return new KNXNetworkLinkFT12(cliOptions.ft12(), medium); } else if (!cliOptions.tpuart().isEmpty()) { // create TPUART network link KNXNetworkLinkTpuart linkTpuart = new KNXNetworkLinkTpuart(cliOptions.tpuart(), medium, Collections.emptyList()); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 15314e9b..b0423273 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -428,8 +428,8 @@ private void LoadKnxIpInterfacesAndFillComboBox() { comboBoxIpGateways.addItem(new CalimeroSearchComboItem(bundle.getString("selectInterface"), null)); DiscoverKnxInterfaces.getAllInterfaces().forEach(r -> - comboBoxIpGateways.addItem(new CalimeroSearchComboItem(r.getResponse().getDevice().getName() + - " (" + r.getResponse().getControlEndpoint().getAddress().getHostAddress() + ")", r))); + comboBoxIpGateways.addItem(new CalimeroSearchComboItem(r.response().getDevice().getName() + + " (" + r.response().getControlEndpoint().endpoint().getAddress().getHostAddress() + ")", r))); comboBoxIpGateways.addActionListener(comboBoxIpGatewaysActionListener); reloadGatewaysButton.setEnabled(true); From 08c3f3bc032c13535063bda2794775a45ea2f6d9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 12 Jul 2024 00:31:57 +0200 Subject: [PATCH 006/359] [Updater] New option `--usb` for USB-Interfaces (experimental) --- firmware_updater/updater/README.md | 12 ++++---- .../java/org/selfbus/updater/CliOptions.java | 28 +++++++++++++++---- .../java/org/selfbus/updater/SBKNXLink.java | 5 ++++ .../src/main/resources/javax.usb.properties | 7 +++++ 4 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 firmware_updater/updater/source/src/main/resources/javax.usb.properties diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index e701eeed..34ef0f3f 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -20,11 +20,11 @@ windows: gradlew.bat fatJar ## Usage ``` java -jar SB_updater-x.xx-all.jar [-f ] [-m | -s | -t - ] [-d ] [-D ] [-o ] [--priority ] - [-bs <256|512|1024>] [--user ] [--user-pwd ] [--device-pwd ] [-u - ] [-f1] [-H ] [-P ] [-p ] [-t2] [-t1] [-n] [-r] [-h | -v] - [--delay ] [-l ] [--ERASEFLASH] [--DUMPFLASH ] [-f0] - [--statistic] + | --usb ] [-d ] [-D ] [-o ] + [--priority ] [-bs <256|512|1024>] [--user ] [--user-pwd + ] [--device-pwd ] [-u ] [-f1] [-H ] [-P ] [-p + ] [-t2] [-t1] [-n] [-r] [-h | -v] [--delay ] [-l ] + [--ERASEFLASH] [--DUMPFLASH ] [-f0] [--statistic] Selfbus KNX-Firmware update tool options: -f,--fileName Filename of hex file to program @@ -32,6 +32,8 @@ Selfbus KNX-Firmware update tool options: -s,--serial use FT1.2 serial communication -t,--tpuart use TPUART serial communication (experimental, needs serialcom or rxtx library in java.library.path) + --usb use USB-Interface. Specify VendorID and ProductID e.g. + 147B:5120 for the Selfbus USB-Interface (experimental) -d,--device KNX device address in normal operating mode (default none) -D,--progDevice KNX device address in bootloader mode (default diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 72a51573..722566a8 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -56,6 +56,7 @@ public class CliOptions { private static final String OPT_LONG_FT12 = "serial"; private static final String OPT_SHORT_TPUART = "t"; private static final String OPT_LONG_TPUART = "tpuart"; + private static final String OPT_LONG_USB = "usb"; private static final String OPT_SHORT_MEDIUM = "m"; private static final String OPT_LONG_MEDIUM = "medium"; @@ -131,6 +132,7 @@ public class CliOptions { private boolean nat = false; private String ft12 = ""; private String tpuart = ""; + private String usbInterface = ""; private boolean tunnelingV2 = false; private boolean tunnelingV1 = false; private boolean routing = false; @@ -224,6 +226,11 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe .hasArg() .required(false) .desc("use TPUART serial communication (experimental, needs serialcom or rxtx library in java.library.path)").build(); + Option usbInterface = Option.builder(null).longOpt(OPT_LONG_USB) + .argName("vendorId:productId") + .hasArg() + .required(false) + .desc("use USB-Interface. Specify VendorID and ProductID e.g. 147B:5120 for the Selfbus USB-Interface (experimental)").build(); Option medium = Option.builder(OPT_SHORT_MEDIUM).longOpt(OPT_LONG_MEDIUM) .argName("tp1|rf") .hasArg() @@ -305,11 +312,12 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe // options will be shown in order as they are added to cliOptions cliOptions.addOption(fileName); - // ft12 or medium, not both + // Only one bus access allowed OptionGroup grpBusAccess = new OptionGroup(); grpBusAccess.addOption(medium); grpBusAccess.addOption(ft12); grpBusAccess.addOption(tpuart); + grpBusAccess.addOption(usbInterface); cliOptions.addOptionGroup(grpBusAccess); @@ -511,10 +519,15 @@ private void parse(final String[] args) { } logger.debug("tpuart={}", tpuart); - if ((ft12.isEmpty()) && (tpuart.isEmpty())) { - // no ft12 or tpuart => get the - if (cmdLine.getArgs().length <= 0) { - throw new ParseException("No , ft12 or tpuart specified."); + if (cmdLine.hasOption(OPT_LONG_USB)) { + usbInterface = cmdLine.getOptionValue(OPT_LONG_USB); + } + logger.debug("usb={}", usbInterface); + + if ((ft12.isEmpty()) && (tpuart.isEmpty()) && (usbInterface.isEmpty())) { + // no ft12, tpuart or USB-Interface => get the + if (cmdLine.getArgs().length == 0) { + throw new ParseException("No , ft12, tpuart or USB-Interface specified."); } else { knxInterface = Utils.parseHost(cmdLine.getArgs()[0]); } @@ -559,6 +572,7 @@ private void parse(final String[] args) { if (routing()) interfacesSet++; if (!ft12().isEmpty()) interfacesSet++; if (!tpuart().isEmpty()) interfacesSet++; + if (!getUsbInterface().isEmpty()) interfacesSet++; if (interfacesSet > 1) { throw new ParseException(String.format("%sOnly one bus interface can be used.%s", ConColors.RED, ConColors.RESET)); @@ -731,4 +745,8 @@ public String getOptionLongFileName() { public int getBlockSize() { return blockSize; } + + public String getUsbInterface() { + return usbInterface; + } } \ No newline at end of file diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index 32d93392..08ec262f 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -11,6 +11,7 @@ import tuwien.auto.calimero.link.KNXNetworkLinkFT12; import tuwien.auto.calimero.link.KNXNetworkLinkIP; import tuwien.auto.calimero.link.KNXNetworkLinkTpuart; +import tuwien.auto.calimero.link.KNXNetworkLinkUsb; import tuwien.auto.calimero.link.medium.KNXMediumSettings; import tuwien.auto.calimero.link.medium.RFSettings; import tuwien.auto.calimero.link.medium.TPSettings; @@ -102,6 +103,10 @@ public KNXNetworkLink openLink() throws KNXException, KNXNetworkLinkTpuart linkTpuart = new KNXNetworkLinkTpuart(cliOptions.tpuart(), medium, Collections.emptyList()); linkTpuart.addAddress(cliOptions.ownAddress()); //\todo check if this is rly needed return linkTpuart; + } else if (!cliOptions.getUsbInterface().isEmpty()) { + // create USB network link + KNXNetworkLinkUsb linkUsb = new KNXNetworkLinkUsb(cliOptions.getUsbInterface(), medium); + return linkUsb; } // create local and remote socket address for network link diff --git a/firmware_updater/updater/source/src/main/resources/javax.usb.properties b/firmware_updater/updater/source/src/main/resources/javax.usb.properties new file mode 100644 index 00000000..cdd31269 --- /dev/null +++ b/firmware_updater/updater/source/src/main/resources/javax.usb.properties @@ -0,0 +1,7 @@ +# Same values like in calimero-core/javax.usb.properties +# tell the USB host manager of javax-usb to use usb4java +javax.usb.services = org.usb4java.javax.Services +# KNX USB communication timeout +org.usb4java.javax.timeout = 1000 +# USB device scan interval (defaults to 500 ms, assigning 0 will scan only once at startup) +org.usb4java.javax.scanInterval = 1000 \ No newline at end of file From dde3b5590a3bfbdfc6e9b6e3af56f0fdaae2041c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 19 Jul 2024 12:50:53 +0200 Subject: [PATCH 007/359] [Updater] Fix broken links --- firmware_updater/README.md | 4 ++-- firmware_updater/updater/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware_updater/README.md b/firmware_updater/README.md index b5ae3f60..858f1437 100644 --- a/firmware_updater/README.md +++ b/firmware_updater/README.md @@ -23,7 +23,7 @@ When using the debug version of the bootloader: |:------|:-------|:---------|:-------------|---------| |Flash |MFlash64|Flash |0x7000 |0x9000 | |RAM |RamLoc8 |RAM |0x10000100 |0x1f00 | -These memory settings can also be set automatically by this [linker-script](https://github.com/selfbus/software-arm-lib/tree/main/examples/example-linkerscripts/memory.ldt). +These memory settings can also be set automatically with this [linker-script](../examples/example-linkerscripts/memory.ldt). ## Build: All build configurations link against the *release version* of the sblib. @@ -56,4 +56,4 @@ Bei Verwendung der Debug Version des Bootloaders: |Flash |MFlash64|Flash |0x7000 |0x9000 | |RAM |RamLoc8 |RAM |0x10000100 |0x1f00 | -Diese Speichereinstellung können auch automatisch durch dieses [Linkerscript](https://github.com/selfbus/software-arm-lib/tree/main/examples/example-linkerscripts/memory.ldt) gesetzt werden. \ No newline at end of file +Diese Speichereinstellung können auch automatisch durch dieses [Linkerscript](../examples/example-linkerscripts/memory.ldt) gesetzt werden. \ No newline at end of file diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index e701eeed..05053b7e 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -4,7 +4,7 @@ * JDK 17+ * gradle >=7.4 -* Selfbus device with flashed [bootloader](../../bootloader) version 1.00 or higher +* Selfbus device with flashed [bootloader](../bootloader) version 1.00 or higher ## Build ``` @@ -93,7 +93,7 @@ java -jar SB_updater-x.xx-all.jar -fileName "in16-bim1 IntelliJ IDEA Community 2023.3.4 (Build -> Build Artifacts)
eclipse project is currently not maintained ## gradle: -update [gradle wrapper](gradle/wrapper) to the newest version: +update [gradle wrapper](source/gradle/wrapper) to the newest version: ``` gradlew wrapper ``` From d16322b89982b1fff162e4ec9c328b5c03fc14d9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 19 Jul 2024 12:54:23 +0200 Subject: [PATCH 008/359] [Updater] Fix missing spaces in table --- firmware_updater/README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/firmware_updater/README.md b/firmware_updater/README.md index 858f1437..1d03da62 100644 --- a/firmware_updater/README.md +++ b/firmware_updater/README.md @@ -12,17 +12,17 @@ The memory settings of the application to be flashed must be set in MCUxpresso a When using the release version of the bootloader: -|Type |Name |Alias |Location |Size | -|:------|:-------|:---------|:-------------|---------| -|Flash |MFlash64|Flash |0x3000 |0xD000 | -|RAM |RamLoc8 |RAM |0x10000100 |0x1f00 | +| Type | Name | Alias | Location | Size | +|:------|:---------|:------|:-----------|--------| +| Flash | MFlash64 | Flash | 0x3000 | 0xD000 | +| RAM | RamLoc8 | RAM | 0x10000100 | 0x1f00 | When using the debug version of the bootloader: -|Type |Name |Alias |Location |Size | -|:------|:-------|:---------|:-------------|---------| -|Flash |MFlash64|Flash |0x7000 |0x9000 | -|RAM |RamLoc8 |RAM |0x10000100 |0x1f00 | +| Type | Name | Alias | Location | Size | +|:------|:---------|:------|:-----------|--------| +| Flash | MFlash64 | Flash | 0x7000 | 0x9000 | +| RAM | RamLoc8 | RAM | 0x10000100 | 0x1f00 | These memory settings can also be set automatically with this [linker-script](../examples/example-linkerscripts/memory.ldt). ## Build: @@ -44,16 +44,16 @@ Die Speicher Einstellungen der zu flashenden App müssen in Bei Verwendung der Release Version des Bootloaders: -|Type |Name |Alias |Location |Size | -|:------|:-------|:---------|:-------------|---------| -|Flash |MFlash64|Flash |0x3000 |0xD000 | -|RAM |RamLoc8 |RAM |0x10000100 |0x1f00 | +| Type | Name | Alias | Location | Size | +|:------|:---------|:------|:-----------|--------| +| Flash | MFlash64 | Flash | 0x3000 | 0xD000 | +| RAM | RamLoc8 | RAM | 0x10000100 | 0x1f00 | Bei Verwendung der Debug Version des Bootloaders: -|Type |Name |Alias |Location |Size | -|:------|:-------|:---------|:-------------|---------| -|Flash |MFlash64|Flash |0x7000 |0x9000 | -|RAM |RamLoc8 |RAM |0x10000100 |0x1f00 | +| Type | Name | Alias | Location | Size | +|:------|:---------|:------|:-----------|--------| +| Flash | MFlash64 | Flash | 0x7000 | 0x9000 | +| RAM | RamLoc8 | RAM | 0x10000100 | 0x1f00 | Diese Speichereinstellung können auch automatisch durch dieses [Linkerscript](../examples/example-linkerscripts/memory.ldt) gesetzt werden. \ No newline at end of file From 987a4282c556e6a96f761a581aa98726e1745385 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 19 Jul 2024 12:58:51 +0200 Subject: [PATCH 009/359] [Updater] Fix flash and RAM values --- firmware_updater/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware_updater/README.md b/firmware_updater/README.md index 1d03da62..be571631 100644 --- a/firmware_updater/README.md +++ b/firmware_updater/README.md @@ -15,14 +15,14 @@ When using the release version of the bootloader: | Type | Name | Alias | Location | Size | |:------|:---------|:------|:-----------|--------| | Flash | MFlash64 | Flash | 0x3000 | 0xD000 | -| RAM | RamLoc8 | RAM | 0x10000100 | 0x1f00 | +| RAM | RamLoc8 | RAM | 0x100000c0 | 0x1f40 | When using the debug version of the bootloader: | Type | Name | Alias | Location | Size | |:------|:---------|:------|:-----------|--------| | Flash | MFlash64 | Flash | 0x7000 | 0x9000 | -| RAM | RamLoc8 | RAM | 0x10000100 | 0x1f00 | +| RAM | RamLoc8 | RAM | 0x100000c0 | 0x1f40 | These memory settings can also be set automatically with this [linker-script](../examples/example-linkerscripts/memory.ldt). ## Build: @@ -47,13 +47,13 @@ Bei Verwendung der Release Version des Bootloaders: | Type | Name | Alias | Location | Size | |:------|:---------|:------|:-----------|--------| | Flash | MFlash64 | Flash | 0x3000 | 0xD000 | -| RAM | RamLoc8 | RAM | 0x10000100 | 0x1f00 | +| RAM | RamLoc8 | RAM | 0x100000c0 | 0x1f40 | Bei Verwendung der Debug Version des Bootloaders: | Type | Name | Alias | Location | Size | |:------|:---------|:------|:-----------|--------| | Flash | MFlash64 | Flash | 0x7000 | 0x9000 | -| RAM | RamLoc8 | RAM | 0x10000100 | 0x1f00 | +| RAM | RamLoc8 | RAM | 0x100000c0 | 0x1f40 | Diese Speichereinstellung können auch automatisch durch dieses [Linkerscript](../examples/example-linkerscripts/memory.ldt) gesetzt werden. \ No newline at end of file From c18aca20098c053cd6758f4f892235380db670da Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 19 Jul 2024 12:59:22 +0200 Subject: [PATCH 010/359] [Updater] Fix gradle version --- firmware_updater/updater/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 05053b7e..dd7aad9f 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -3,7 +3,7 @@ ## Requirements * JDK 17+ -* gradle >=7.4 +* gradle >=8.5 * Selfbus device with flashed [bootloader](../bootloader) version 1.00 or higher ## Build From f12e9463fe87f1399191cbbbc8cddd924769b077 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 19 Jul 2024 13:02:52 +0200 Subject: [PATCH 011/359] [Updater] Fix changes made in PR #104 --- firmware_updater/README.md | 16 ++++++++-------- firmware_updater/updater/README.md | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/firmware_updater/README.md b/firmware_updater/README.md index be571631..04fd7a02 100644 --- a/firmware_updater/README.md +++ b/firmware_updater/README.md @@ -1,9 +1,9 @@ -Selfbus Bootloader and Updater -============================== +Selfbus Updater, Bootloader and Bootloaderupdater +================================================= Bootloader and Updater for updating the application software of Selfbus devices. -Always use the bootloader and the Updater with the same version number! +If possible, the Updater and Bootloader should be of the same version number. *[Wiki entry about the Bootloader and Updater](https://selfbus.org/wiki/software/tools/7-selfbus-bus-updater-tool)* ## Important: @@ -26,16 +26,16 @@ When using the debug version of the bootloader: These memory settings can also be set automatically with this [linker-script](../examples/example-linkerscripts/memory.ldt). ## Build: -All build configurations link against the *release version* of the sblib. -- "Debug with UART (LPC11XX)" is set to compile with -Od (Optimize for debug) to fit into the first 0x7000 of flash -- "Debug (LPC11XX)" is also set to compile with -Od -- "Release (LPC11xx)" is set to compile with -Os (Optimize for size) to fit into the first 0x3000 of flash +Most build configurations link against the *release version* of the sblib. +- "Debug low level" is set to compile with -Od (Optimize for debug) to fit into the first 0x7000 of flash +- "Debug" is also set to compile with -Od +- "Release" is set to compile with -Os (Optimize for size) to fit into the first 0x3000 of flash # Deutsch: Bootloader und Updater zum Updaten der Applikationssoftware von Selfbus Geräten -Immer den Bootloader und das PC-Tool mit der gleichen Versionsnummer verwenden! +Wenn möglich sollten Updater und Bootloader mit derselben Versionsnummer verwendet werden. *[Wiki-Eintrag zum Bootloader und Updater](https://selfbus.org/wiki/software/tools/7-selfbus-bus-updater-tool)* ## WICHTIG: diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index dd7aad9f..e32585f3 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -15,7 +15,7 @@ gradle fatJar linux: gradlew fatJar windows: gradlew.bat fatJar ``` -*SB_updater-x.xx-all.jar* file is created in build/libs directory. +*SB_updater-x.xx-all.jar* file is created in [build/libs](source/build/libs) directory. ## Usage ``` From 2d35214e9ac573a3538621e3113bd44608db79f4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 19 Jul 2024 13:05:57 +0200 Subject: [PATCH 012/359] [Updater] Add example use case GUI --- firmware_updater/updater/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index e32585f3..e264f0f2 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -73,6 +73,10 @@ Selfbus KNX-Firmware update tool options: --statistic show more statistic data ``` ## Common use cases: +Updater with graphical user interface (**experimental**) +``` +java -jar SB_updater-x.xx-all.jar +``` Read UID of the device: ``` java -jar SB_updater-x.xx-all.jar From 5dfd5dc66210566a08996b1938fbe73e6f1e5485 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 29 Jul 2024 02:49:29 +0200 Subject: [PATCH 013/359] [Updater] Delete obsolete comments This was an issue in the [IDEA IDE](https://www.jetbrains.com/idea/) which has been fixed at least one year ago. See [IDEA tracker](https://youtrack.jetbrains.com/issue/IDEA-279280/gradle-The-JavaExec.main-property-has-been-deprecated#focus=Comments-27-6912131.0-0) Quote: "Fixed, cherry-picked to 231" (2023.1) --- firmware_updater/updater/source/build.gradle | 2 +- firmware_updater/updater/source/gradle.properties | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index cdaf98ae..1a43a006 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -11,7 +11,7 @@ java { } application { - mainClass = 'org.selfbus.updater.Updater' // avoids warning "The JavaExec.main property has been deprecated" see https://youtrack.jetbrains.com/issue/IDEA-279280 + mainClass = 'org.selfbus.updater.Updater' } jar { diff --git a/firmware_updater/updater/source/gradle.properties b/firmware_updater/updater/source/gradle.properties index 520d7e3a..03b2e37a 100644 --- a/firmware_updater/updater/source/gradle.properties +++ b/firmware_updater/updater/source/gradle.properties @@ -1,3 +1 @@ -org.gradle.warning.mode=all -#see https://youtrack.jetbrains.com/issue/IDEA-279280 -#The JavaExec.main property has been deprecated. This is scheduled to be removed in Gradle 8.0. Please use the mainClass property instead. \ No newline at end of file +org.gradle.warning.mode=all \ No newline at end of file From 69ebdb6b38b84e56d09530ceb3bacd0f83fcffc0 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 00:56:14 +0200 Subject: [PATCH 014/359] [Updater] WIP Use jansi for ansi coloring --- firmware_updater/updater/source/build.gradle | 3 +++ .../source/src/main/java/org/selfbus/updater/Updater.java | 5 +++++ .../src/main/java/org/selfbus/updater/gui/GuiMain.java | 2 +- .../updater/source/src/main/resources/logback.xml | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 1a43a006..28c57632 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -81,6 +81,9 @@ dependencies { // unit test testImplementation group: 'junit', name: 'junit', version: '4.13.2' + // Console ansi color support + implementation 'org.fusesource.jansi:jansi:2.4.0' + // GUI implementation files('libs/forms_rt.jar') } \ No newline at end of file diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index f3d1de50..776634ac 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -1,5 +1,6 @@ package org.selfbus.updater; +import org.fusesource.jansi.AnsiConsole; import org.selfbus.updater.bootloader.BootloaderStatistic; import tuwien.auto.calimero.*; import org.apache.commons.cli.ParseException; @@ -14,6 +15,8 @@ import java.io.FileNotFoundException; import java.io.IOException; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; import static org.selfbus.updater.Utils.shortenPath; import org.selfbus.updater.gui.GuiMain; @@ -95,6 +98,7 @@ public Updater(CliOptions cliOptions){ } public static void main(final String[] args) { + AnsiConsole.systemInstall(); if(args.length == 0) { GuiMain.startSwingGui(); }else { @@ -110,6 +114,7 @@ public static void main(final String[] args) { logger.debug("main exit"); } } + AnsiConsole.systemUninstall(); } /** diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index b0423273..e87ef5b7 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -839,7 +839,7 @@ private void setGuiElementsVisibility() { if (fontName == null) { resultName = currentFont.getName(); } else { - Font testFont = new Font(fontName, PLAIN, 10); + Font testFont = new Font(fontName, Font.PLAIN, 10); if (testFont.canDisplay('a') && testFont.canDisplay('1')) { resultName = fontName; } else { diff --git a/firmware_updater/updater/source/src/main/resources/logback.xml b/firmware_updater/updater/source/src/main/resources/logback.xml index f9b4fde9..a93ed322 100644 --- a/firmware_updater/updater/source/src/main/resources/logback.xml +++ b/firmware_updater/updater/source/src/main/resources/logback.xml @@ -5,6 +5,7 @@ + true INFO From f219aa6074465d50e9c722e612610709d087236f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 00:57:22 +0200 Subject: [PATCH 015/359] [Updater] Replace `BRIGHT_BOLD_GREEN` --- .../source/src/main/java/org/selfbus/updater/Updater.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 776634ac..b267e935 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -70,14 +70,14 @@ public class Updater implements Runnable { public Updater(final String[] args) throws ParseException, KNXFormatException { logger.debug(ToolInfo.getFullInfo()); logger.debug(Settings.getLibraryHeader(false)); - logger.info(ConColors.BRIGHT_BOLD_GREEN + + logger.info(ansi().fgBright(GREEN).bold().a( " _____ ________ __________ __ _______ __ ______ ____ ___ ________________ \n" + " / ___// ____/ / / ____/ __ )/ / / / ___/ / / / / __ \\/ __ \\/ |/_ __/ ____/ __ \\\n" + " \\__ \\/ __/ / / / /_ / __ / / / /\\__ \\ / / / / /_/ / / / / /| | / / / __/ / /_/ /\n" + " ___/ / /___/ /___/ __/ / /_/ / /_/ /___/ / / /_/ / ____/ /_/ / ___ |/ / / /___/ _, _/ \n" + "/____/_____/_____/_/ /_____/\\____//____/ \\____/_/ /_____/_/ |_/_/ /_____/_/ |_| \n" + - "by Dr. Stefan Haller, Oliver Stefan et al. " + ToolInfo.getToolAndVersion() + - ConColors.RESET); + "by Dr. Stefan Haller, Oliver Stefan et al. {}").reset().toString(), + ToolInfo.getToolAndVersion()); try { // read in user-supplied command line options this.cliOptions = new CliOptions(args, String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()) , From fd3f36d5716002447cf75647931a38f2751d1ef4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 01:00:22 +0200 Subject: [PATCH 016/359] [Updater] Delete not used `ConColors` --- .../java/org/selfbus/updater/ConColors.java | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java index 2ac6d61a..e0b4213b 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java @@ -10,61 +10,15 @@ public class ConColors { // Regular Colors public static final String BLACK = "\033[0;30m"; // BLACK public static final String RED = "\033[0;31m"; // RED - public static final String GREEN = "\033[0;32m"; // GREEN public static final String YELLOW = "\033[0;33m"; // YELLOW - public static final String BLUE = "\033[0;34m"; // BLUE - public static final String PURPLE = "\033[0;35m"; // PURPLE - public static final String CYAN = "\033[0;36m"; // CYAN - public static final String WHITE = "\033[0;37m"; // WHITE - - // Regular Bold - public static final String BOLD_BLACK = "\033[1;30m"; // BLACK - public static final String BOLD_RED = "\033[1;31m"; // RED - public static final String BOLD_GREEN = "\033[1;32m"; // GREEN - public static final String BOLD_YELLOW = "\033[1;33m"; // YELLOW - public static final String BOLD_BLUE = "\033[1;34m"; // BLUE - public static final String BOLD_PURPLE = "\033[1;35m"; // PURPLE - public static final String BOLD_CYAN = "\033[1;36m"; // CYAN - public static final String BOLD_WHITE = "\033[1;37m"; // WHITE // Regular Background - public static final String BG_BLACK = "\033[40m"; // BLACK public static final String BG_RED = "\033[41m"; // RED public static final String BG_GREEN = "\033[42m"; // GREEN - public static final String BG_YELLOW = "\033[43m"; // YELLOW - public static final String BG_BLUE = "\033[44m"; // BLUE - public static final String BG_PURPLE = "\033[45m"; // PURPLE - public static final String BG_CYAN = "\033[46m"; // CYAN - public static final String BG_WHITE = "\033[47m"; // WHITE // High Intensity - public static final String BRIGHT_BLACK = "\033[0;90m"; // BLACK public static final String BRIGHT_RED = "\033[0;91m"; // RED public static final String BRIGHT_GREEN = "\033[0;92m"; // GREEN public static final String BRIGHT_YELLOW = "\033[0;93m"; // YELLOW - public static final String BRIGHT_BLUE = "\033[0;94m"; // BLUE - public static final String BRIGHT_PURPLE = "\033[0;95m"; // PURPLE - public static final String BRIGHT_CYAN = "\033[0;96m"; // CYAN - public static final String BRIGHT_WHITE = "\033[0;97m"; // WHITE - - // High Intensity Bold - public static final String BRIGHT_BOLD_BLACK = "\033[1;90m"; // BLACK - public static final String BRIGHT_BOLD_RED = "\033[1;91m"; // RED - public static final String BRIGHT_BOLD_GREEN = "\033[1;92m"; // GREEN - public static final String BRIGHT_BOLD_YELLOW = "\033[1;93m"; // YELLOW - public static final String BRIGHT_BOLD_BLUE = "\033[1;94m"; // BLUE - public static final String BRIGHT_BOLD_PURPLE = "\033[1;95m"; // PURPLE - public static final String BRIGHT_BOLD_CYAN = "\033[1;96m"; // CYAN - public static final String BRIGHT_BOLD_WHITE = "\033[1;97m"; // WHITE - - // High Intensity Background - public static final String BRIGHT_BG_BLACK = "\033[0;100m"; // BLACK - public static final String BRIGHT_BG_RED = "\033[0;101m"; // RED - public static final String BRIGHT_BG_GREEN = "\033[0;102m"; // GREEN - public static final String BRIGHT_BG_YELLOW = "\033[0;103m"; // YELLOW - public static final String BRIGHT_BG_BLUE = "\033[0;104m"; // BLUE - public static final String BRIGHT_BG_PURPLE = "\033[0;105m"; // PURPLE - public static final String BRIGHT_BG_CYAN = "\033[0;106m"; // CYAN - public static final String BRIGHT_BG_WHITE = "\033[0;107m"; // WHITE } From b203725cc1b4201d3731e98368dfa141974bc7c1 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 01:32:47 +0200 Subject: [PATCH 017/359] [Updater] Replace `BLACK`, `BG_RED`, `BG_GREEN` --- .../main/java/org/selfbus/updater/ConColors.java | 5 ----- .../src/main/java/org/selfbus/updater/Updater.java | 14 +++++++------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java index e0b4213b..37e2cf72 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java @@ -8,14 +8,9 @@ public class ConColors { public static final String RESET = "\033[0m"; // Reset // Regular Colors - public static final String BLACK = "\033[0;30m"; // BLACK public static final String RED = "\033[0;31m"; // RED public static final String YELLOW = "\033[0;33m"; // YELLOW - // Regular Background - public static final String BG_RED = "\033[41m"; // RED - public static final String BG_GREEN = "\033[42m"; // GREEN - // High Intensity public static final String BRIGHT_RED = "\033[0;91m"; // RED public static final String BRIGHT_GREEN = "\033[0;92m"; // GREEN diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index b267e935..032ff7fb 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -212,9 +212,9 @@ public void run() { } else { System.out.println(); - logger.info("{}{}No firmware file (*.hex) specified! Specify with --{}{}", ConColors.BLACK, ConColors.BG_RED, - cliOptions.getOptionLongFileName(), ConColors.RESET); - logger.info("{}Reading only device information{}", ConColors.BRIGHT_YELLOW, ConColors.RESET); + logger.info(ansi().bg(RED).fg(BLACK).a("No firmware file (*.hex) specified! Specify with --{}").reset().toString(), + cliOptions.getOptionLongFileName()); + logger.info(ansi().fgBright(YELLOW).a("Reading only device information").reset().toString()); System.out.println(); } @@ -325,7 +325,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd // Start to flash the new firmware long flashTimeStart = System.currentTimeMillis(); // time flash process started ResponseResult resultTotal; - logger.info("{}{}Starting to send new firmware now:{}", ConColors.BLACK, ConColors.BG_GREEN, ConColors.RESET); + logger.info(ansi().bg(GREEN).fg(BLACK).a("Starting to send new firmware now:").reset().toString()); if (diffMode && FlashDiffMode.isInitialized()) { logger.error("{}Differential mode is EXPERIMENTAL -> Use with caution.{}", ConColors.BRIGHT_RED, ConColors.RESET); resultTotal = FlashDiffMode.doDifferentialFlash(dm, newFirmware.startAddress(), newFirmware.getBinData()); @@ -368,12 +368,12 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd deviceInfo = cliOptions.device().toString(); } logger.info("Finished programming {}device {} with {}{}", ConColors.BRIGHT_YELLOW, deviceInfo, shortenPath(cliOptions.fileName(), 1), ConColors.RESET); - logger.info("{}{}Firmware Update done, Restarting device now...{}", ConColors.BLACK, ConColors.BG_GREEN, ConColors.RESET); + logger.info(ansi().bg(GREEN).fg(BLACK).a("Firmware Update done, Restarting device now...").reset().toString()); dm.restartProgrammingDevice(); if (newFirmware.getAppVersion().contains(BootloaderUpdater.BOOTLOADER_UPDATER_ID_STRING)) { - logger.info("{}{}Wait {} second(s) for Bootloader Updater to finish its job...{}", ConColors.BLACK, ConColors.BG_GREEN, - String.format("%.2f", BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS / 1000.0f), ConColors.RESET); + logger.info(ansi().bg(GREEN).fg(BLACK).a("Wait {} second(s) for Bootloader Updater to finish its job...").reset().toString(), + String.format("%.2f", BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS / 1000.0f)); Thread.sleep(BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS); } } catch (final KNXException | UpdaterException | RuntimeException e) { From d2c9861f2a88a6358dc57cc97241ede9a307cfb8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 01:58:40 +0200 Subject: [PATCH 018/359] [Updater] Replace `YELLOW` --- .../src/main/java/org/selfbus/updater/CliOptions.java | 5 ++++- .../src/main/java/org/selfbus/updater/ConColors.java | 1 - .../src/main/java/org/selfbus/updater/SBKNXLink.java | 9 ++++++--- .../src/main/java/org/selfbus/updater/Updater.java | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 72a51573..2a1578d0 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -26,6 +26,9 @@ import java.net.InetAddress; import java.util.Arrays; import java.util.List; + +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; import java.util.Locale; /** @@ -473,7 +476,7 @@ private void parse(final String[] args) { blockSize = newBlockSize; } else { - logger.info("{}--{} {} is not supported => Set --{} to default {} bytes{}", ConColors.YELLOW, OPT_LONG_BLOCKSIZE, newBlockSize, OPT_LONG_BLOCKSIZE, blockSize, ConColors.RESET); + logger.info(ansi().fg(YELLOW).a("--{} {} is not supported => Set --{} to default {} bytes").reset().toString(), OPT_LONG_BLOCKSIZE, newBlockSize, OPT_LONG_BLOCKSIZE, blockSize); } } logger.debug("{}={}", OPT_LONG_BLOCKSIZE, delay); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java index 37e2cf72..c7af2b1c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java @@ -9,7 +9,6 @@ public class ConColors { // Regular Colors public static final String RED = "\033[0;31m"; // RED - public static final String YELLOW = "\033[0;33m"; // YELLOW // High Intensity public static final String BRIGHT_RED = "\033[0;91m"; // RED diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index 32d93392..1072d90f 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -19,6 +19,9 @@ import java.net.InetSocketAddress; import java.util.Collections; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; + public class SBKNXLink { private static final Logger logger = LoggerFactory.getLogger(CliOptions.class.getName()); private CliOptions cliOptions; @@ -46,12 +49,12 @@ private KNXNetworkLink createTunnelingLinkV2(InetSocketAddress local, InetSocket private KNXNetworkLink createTunnelingLinkV1(InetSocketAddress local, InetSocketAddress remote, boolean useNat, KNXMediumSettings medium) throws KNXException, InterruptedException { - logger.info("{}Connect using UDP tunneling v1 (nat:{})...{}", ConColors.YELLOW, useNat, ConColors.RESET); + logger.info(ansi().fg(YELLOW).a("Connect using UDP tunneling v1 (nat:{})...").reset().toString(), useNat); return KNXNetworkLinkIP.newTunnelingLink(local, remote, useNat, medium); } private KNXNetworkLink createRoutingLink(InetSocketAddress local, KNXMediumSettings medium) throws KNXException { - logger.info("{}Connect using routing (multicast:{})...{}", ConColors.YELLOW, KNXnetIPRouting.DefaultMulticast, ConColors.RESET); + logger.info(ansi().fg(YELLOW).a("Connect using routing (multicast:{})...").reset().toString(), KNXnetIPRouting.DefaultMulticast); return KNXNetworkLinkIP.newRoutingLink(local.getAddress(), KNXnetIPRouting.DefaultMulticast, medium); } @@ -137,7 +140,7 @@ public KNXNetworkLink openLink() throws KNXException, try { return createTunnelingLinkV1(local, remote, cliOptions.nat(), medium); } catch (final KNXException | InterruptedException e) { - logger.info("{}failed with {}{}", ConColors.YELLOW, e, ConColors.RESET); + logger.info(ansi().fg(YELLOW).a("failed with {}").reset().toString(), e.toString()); } // last chance try unsecure UDP tunneling v1 connection with INVERTED nat option set on cli diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 032ff7fb..506291a1 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -317,8 +317,8 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd } if (!dm.setBlockSize(cliOptions.getBlockSize())) { - logger.info("{}Connected bootloader doesn't support block size {}. Using {} bytes.{}", ConColors.YELLOW, - cliOptions.getBlockSize(), dm.getBlockSize(), ConColors.RESET); + logger.info(ansi().fg(YELLOW).a("Connected bootloader doesn't support block size {}. Using {} bytes.").reset().toString(), + cliOptions.getBlockSize(), dm.getBlockSize()); } if (!cliOptions.NO_FLASH()) { // is flashing firmware disabled? for debugging use only! From bfb5179bdefdc089803ebfcf470942c10ed4075e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 02:38:42 +0200 Subject: [PATCH 019/359] [Updater] Replace `BRIGHT_YELLOW` and some `BRIGHT_GREEN` --- .../java/org/selfbus/updater/ConColors.java | 1 - .../org/selfbus/updater/DeviceManagement.java | 4 +++- .../org/selfbus/updater/FlashFullMode.java | 6 ++++-- .../java/org/selfbus/updater/Updater.java | 19 ++++++++++--------- .../bootloader/BootloaderStatistic.java | 15 +++++++++------ .../updater/BootloaderStatisticTest.java | 8 +++++--- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java index c7af2b1c..5f66cbe0 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java @@ -13,6 +13,5 @@ public class ConColors { // High Intensity public static final String BRIGHT_RED = "\033[0;91m"; // RED public static final String BRIGHT_GREEN = "\033[0;92m"; // GREEN - public static final String BRIGHT_YELLOW = "\033[0;93m"; // YELLOW } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index 01c81579..df5dbc13 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -20,6 +20,8 @@ import java.time.Duration; import java.util.Arrays; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; import static org.selfbus.updater.Mcu.MAX_FLASH_ERASE_TIMEOUT; import static org.selfbus.updater.upd.UPDProtocol.COMMAND_POSITION; import static org.selfbus.updater.upd.UPDProtocol.DATA_POSITION; @@ -154,7 +156,7 @@ public BootloaderIdentity requestBootloaderIdentity() } BootloaderIdentity bl = BootloaderIdentity.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info(" Device Bootloader: {}{}{}", ConColors.BRIGHT_YELLOW, bl, ConColors.RESET); + logger.info(" Device Bootloader: {}", ansi().fgBright(YELLOW).a(bl).reset().toString()); boolean versionsMatch = (bl.getVersionMajor() > ToolInfo.minMajorVersionBootloader()) || ((bl.getVersionMajor() == ToolInfo.minMajorVersionBootloader()) && (bl.getVersionMinor() >= ToolInfo.minMinorVersionBootloader())); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java index d568dadb..4bcea02a 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java @@ -11,6 +11,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; import static org.selfbus.updater.upd.UDPResult.*; /** @@ -69,9 +71,9 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa float bytesPerSecond = (float) resultSendData.written() / (flashTimeDuration / 1000f); String col; if (bytesPerSecond >= 50.0) { - col = ConColors.BRIGHT_GREEN; + col = ansi().fgBright(GREEN).toString(); } else { - col = ConColors.BRIGHT_YELLOW; + col = ansi().fgBright(YELLOW).toString(); } String percentageDone = String.format("%5.1f", (float) 100 * (resultTotal.written()) / totalLength); String progressInfo = String.format("%s %s%% %s%6.2f B/s%s", ConColors.BRIGHT_GREEN, percentageDone, col, bytesPerSecond, ConColors.RESET); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 506291a1..6dec57eb 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -295,8 +295,8 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd logger.info(" {}Firmware starts directly beyond bootloader.{}", ConColors.BRIGHT_GREEN, ConColors.RESET); } else { - logger.info(" {}Info: There are {} bytes of unused flash between bootloader and firmware.{}", - ConColors.BRIGHT_YELLOW, newFirmware.startAddress() - bootLoaderIdentity.getApplicationFirstAddress(), ConColors.RESET); + logger.info(ansi().fgBright(YELLOW).a(" Info: There are {} bytes of unused flash between bootloader and firmware.").reset().toString(), + newFirmware.startAddress() - bootLoaderIdentity.getApplicationFirstAddress()); } boolean diffMode = false; @@ -339,17 +339,17 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd String updaterStatisticMsg = " Updater: "; String colored; if (resultTotal.dropCount() > BootloaderStatistic.THRESHOLD_DISCONNECT) { - colored = ConColors.BRIGHT_YELLOW; + colored = ansi().fgBright(YELLOW).toString(); } else { - colored = ConColors.BRIGHT_GREEN; + colored = ansi().fgBright(GREEN).toString(); } - updaterStatisticMsg += String.format("#Disconnect: %s%2d%s", colored, resultTotal.dropCount(), ConColors.RESET); + updaterStatisticMsg += String.format("#Disconnect: %s%2d%s", colored, resultTotal.dropCount(), ansi().reset().toString()); if (resultTotal.timeoutCount() > BootloaderStatistic.THRESHOLD_REPEATED) { - colored = ConColors.BRIGHT_YELLOW; + colored = ansi().fgBright(YELLOW).toString(); } else { - colored = ConColors.BRIGHT_GREEN; + colored = ansi().fgBright(GREEN).toString(); } - updaterStatisticMsg += String.format(" #Timeout : %s%2d%s", colored, resultTotal.timeoutCount(), ConColors.RESET); + updaterStatisticMsg += String.format(" #Timeout : %s%2d%s", colored, resultTotal.timeoutCount(), ansi().reset().toString()); logger.info("{}", updaterStatisticMsg); printStatisticData(flashTimeStart, resultTotal); } @@ -367,7 +367,8 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd if (cliOptions.device() != null) { deviceInfo = cliOptions.device().toString(); } - logger.info("Finished programming {}device {} with {}{}", ConColors.BRIGHT_YELLOW, deviceInfo, shortenPath(cliOptions.fileName(), 1), ConColors.RESET); + logger.info("Finished programming device {} with {}", ansi().fgBright(YELLOW).a(deviceInfo).reset().toString(), + ansi().fgBright(YELLOW).a(shortenPath(cliOptions.fileName(), 1)).reset().toString()); logger.info(ansi().bg(GREEN).fg(BLACK).a("Firmware Update done, Restarting device now...").reset().toString()); dm.restartProgrammingDevice(); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java index 6c7ba350..f2c8132f 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java @@ -3,6 +3,9 @@ import org.selfbus.updater.ConColors; import org.selfbus.updater.Utils; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; + public class BootloaderStatistic { public static final int THRESHOLD_DISCONNECT = 1; public static final int THRESHOLD_REPEATED = 1; @@ -25,17 +28,17 @@ public String toString() { String result; String colored; if (getDisconnectCount() > BootloaderStatistic.THRESHOLD_DISCONNECT) { - colored = ConColors.BRIGHT_YELLOW; + colored = ansi().fgBright(YELLOW).toString(); } else { - colored = ConColors.BRIGHT_GREEN; + colored = ansi().fgBright(GREEN).toString(); } - result = String.format("#Disconnect: %s%2d%s", colored, getDisconnectCount(), ConColors.RESET); + result = String.format("#Disconnect: %s%2d%s", colored, getDisconnectCount(), ansi().reset().toString()); if (getRepeatedT_ACKcount() > BootloaderStatistic.THRESHOLD_REPEATED) { - colored = ConColors.BRIGHT_YELLOW; + colored = ansi().fgBright(YELLOW).toString(); } else { - colored = ConColors.BRIGHT_GREEN; + colored = ansi().fgBright(GREEN).toString(); } - result += String.format(" #repeated T_ACK: %s%2d%s", colored, getRepeatedT_ACKcount(), ConColors.RESET); + result += String.format(" #repeated T_ACK: %s%2d%s", colored, getRepeatedT_ACKcount(), ansi().reset().toString()); return result; } diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java index ce55dca3..01099dfe 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java @@ -4,13 +4,15 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; public class BootloaderStatisticTest { @Test public void testToString() { - String colorOK = ConColors.BRIGHT_GREEN; - String colorWarn = ConColors.BRIGHT_YELLOW; - String colorReset = ConColors.RESET; + String colorOK = ansi().fgBright(GREEN).toString(); + String colorWarn = ansi().fgBright(YELLOW).toString(); + String colorReset = ansi().reset().toString(); String info1 = "#Disconnect: "; String info2 = " #repeated T_ACK: "; From 365c0dded9306123e12a478fb77f7653dbb03dd8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 03:35:34 +0200 Subject: [PATCH 020/359] [Updater] Replace `BRIGHT_RED` and some `BRIGHT_GREEN` --- .../java/org/selfbus/updater/ConColors.java | 1 - .../org/selfbus/updater/DeviceManagement.java | 4 ++-- .../org/selfbus/updater/FlashDiffMode.java | 6 +++-- .../java/org/selfbus/updater/Updater.java | 24 +++++++++---------- .../org/selfbus/updater/upd/UPDProtocol.java | 5 +++- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java index 5f66cbe0..74b556f9 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java @@ -11,7 +11,6 @@ public class ConColors { public static final String RED = "\033[0;31m"; // RED // High Intensity - public static final String BRIGHT_RED = "\033[0;91m"; // RED public static final String BRIGHT_GREEN = "\033[0;92m"; // GREEN } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index df5dbc13..72a8b817 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -97,7 +97,7 @@ public boolean restartDeviceToBootloader(IndividualAddress device) System.out.println(); return true; } catch (final KNXException | InterruptedException e) { - logger.info("{}Restart state of device {} unknown. {}{}", ConColors.BRIGHT_RED, device, e.getMessage(), ConColors.RESET); + logger.info(ansi().fgBright(RED).a("Restart state of device {} unknown. {}").reset().toString(), device, e.getMessage()); logger.debug("KNXException ", e); logger.info("Waiting {}{} seconds{} for device {} to restart", ConColors.BRIGHT_GREEN, restartProcessTime, ConColors.RESET, device); waitRestartTime(restartProcessTime); @@ -391,7 +391,7 @@ public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throw else if ((devices.length == 1) && (progDeviceAddr != null) && (progDeviceAddr.equals(devices[0]))) { // correct device in prog mode return; } - logger.warn("{}{} Device(s) in bootloader/programming mode: {}{}", ConColors.BRIGHT_RED, devices.length, Arrays.stream(devices).toArray(), ConColors.RESET); + logger.warn(ansi().fgBright(RED).a("{} Device(s) in bootloader/programming mode: {}").reset().toString(), devices.length, Arrays.stream(devices).toArray()); if (devices.length == 0) { throw new UpdaterException("No device in programming mode."); } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 7178b77f..2e670e2b 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -17,6 +17,8 @@ import java.util.Arrays; import java.util.concurrent.atomic.AtomicReference; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; import static org.selfbus.updater.Mcu.MAX_PAYLOAD; /** @@ -59,12 +61,12 @@ public static boolean setupDifferentialMode(BootDescriptor bootDescriptor) { // if they are not the same app, we should switch to full flash mode if (Utils.fileExists(oldFileName)) { logger.info(" Found current device firmware in cache {}", Utils.shortenPath(oldFileName, DEFAULT_DISPLAYED_PATH_DEPTH)); - logger.info(" {}--> switching to diff upload mode{}", ConColors.BRIGHT_GREEN, ConColors.RESET); + logger.info(ansi().fgBright(GREEN).a(" --> switching to diff upload mode").reset().toString()); initialized = true; } else { logger.warn(" Current device firmware not found in cache {}", Utils.shortenPath(oldFileName, DEFAULT_DISPLAYED_PATH_DEPTH)); - logger.warn(" {}--> switching to FULL upload mode{}", ConColors.BRIGHT_RED, ConColors.RESET); + logger.warn(ansi().fgBright(RED).a(" --> switching to FULL upload mode").reset().toString()); initialized = false; } return initialized; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 6dec57eb..10d1105d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -157,12 +157,12 @@ private static void printStatisticData(long flashTimeStart, ResponseResult resul float bytesPerSecond = (float) result.written() / (flashTimeDuration / 1000f); String col; if (bytesPerSecond >= 50.0) { - col = ConColors.BRIGHT_GREEN; + col = ansi().fgBright(GREEN).toString(); } else { - col = ConColors.BRIGHT_RED; + col = ansi().fgBright(RED).toString(); } String infoMsg = String.format("Wrote %d bytes from file to device in %tM:% switching to full mode{}", ConColors.BRIGHT_RED, ConColors.RESET); + logger.error(ansi().fgBright(RED).a(" BootDescriptor is not valid -> switching to full mode").reset().toString()); } } @@ -327,7 +327,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd ResponseResult resultTotal; logger.info(ansi().bg(GREEN).fg(BLACK).a("Starting to send new firmware now:").reset().toString()); if (diffMode && FlashDiffMode.isInitialized()) { - logger.error("{}Differential mode is EXPERIMENTAL -> Use with caution.{}", ConColors.BRIGHT_RED, ConColors.RESET); + logger.error(ansi().fgBright(RED).a("Differential mode is EXPERIMENTAL -> Use with caution.").reset().toString()); resultTotal = FlashDiffMode.doDifferentialFlash(dm, newFirmware.startAddress(), newFirmware.getBinData()); } else { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java index f8e4ad52..6b579f10 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java @@ -6,6 +6,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; + /** * Implementation of the UPD/UDP protocol handling */ @@ -34,7 +37,7 @@ public static long checkResult(byte[] result, boolean verbose) { UDPResult udpResult = UDPResult.valueOfIndex(result[DATA_POSITION]); if (udpResult.isError()) { - logger.error("{}{} resultCode=0x{}{}", ConColors.BRIGHT_RED, udpResult, String.format("%02X", udpResult.id), ConColors.RESET); + logger.error(ansi().fgBright(RED).a("{} resultCode=0x{}").reset().toString(), udpResult, String.format("%02X", udpResult.id)); } else { if (verbose) { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); From c5c699d7a4a27afda6b1fd20d8f5acc36d5007c0 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 04:12:35 +0200 Subject: [PATCH 021/359] [Updater] Replace `BRIGHT_GREEN` --- .../src/main/java/org/selfbus/updater/ConColors.java | 3 --- .../java/org/selfbus/updater/DeviceManagement.java | 6 +++--- .../main/java/org/selfbus/updater/FlashFullMode.java | 2 +- .../src/main/java/org/selfbus/updater/Updater.java | 6 +++--- .../selfbus/updater/bootloader/BootDescriptor.java | 11 +++++++---- .../selfbus/updater/tests/flashdiff/FlashDiff.java | 5 ++++- .../java/org/selfbus/updater/upd/UPDProtocol.java | 6 +++--- 7 files changed, 21 insertions(+), 18 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java index 74b556f9..3160ae0c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java @@ -9,8 +9,5 @@ public class ConColors { // Regular Colors public static final String RED = "\033[0;31m"; // RED - - // High Intensity - public static final String BRIGHT_GREEN = "\033[0;92m"; // GREEN } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index 72a8b817..6801d63a 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -70,7 +70,7 @@ private void waitRestartTime(int restartTimeSeconds) { try { while (restartTimeSeconds > 0) { Thread.sleep(1000); - System.out.printf("%s%s%s", ConColors.BRIGHT_GREEN, Utils.PROGRESS_MARKER, ConColors.RESET); + System.out.printf(ansi().fgBright(GREEN).a(Utils.PROGRESS_MARKER).reset().toString()); restartTimeSeconds--; } } @@ -92,14 +92,14 @@ public boolean restartDeviceToBootloader(IndividualAddress device) try { logger.info("Restarting device {} into bootloader mode using {}", device, this.link); restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); - logger.info("Device {} reported {}{} second(s){} for restarting", device, ConColors.BRIGHT_GREEN, restartProcessTime, ConColors.RESET); + logger.info("Device {} reported {} second(s) for restarting", device, ansi().fgBright(GREEN).a(restartProcessTime).reset().toString()); waitRestartTime(restartProcessTime); System.out.println(); return true; } catch (final KNXException | InterruptedException e) { logger.info(ansi().fgBright(RED).a("Restart state of device {} unknown. {}").reset().toString(), device, e.getMessage()); logger.debug("KNXException ", e); - logger.info("Waiting {}{} seconds{} for device {} to restart", ConColors.BRIGHT_GREEN, restartProcessTime, ConColors.RESET, device); + logger.info("Waiting {} seconds for device {} to restart", ansi().fgBright(GREEN).a(restartProcessTime).reset().toString(), device); waitRestartTime(restartProcessTime); } finally { dest.close(); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java index 4bcea02a..5656286c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java @@ -76,7 +76,7 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa col = ansi().fgBright(YELLOW).toString(); } String percentageDone = String.format("%5.1f", (float) 100 * (resultTotal.written()) / totalLength); - String progressInfo = String.format("%s %s%% %s%6.2f B/s%s", ConColors.BRIGHT_GREEN, percentageDone, col, bytesPerSecond, ConColors.RESET); + String progressInfo = ansi().fgBright(GREEN).a(String.format("%s%% %s%6.2f B/s", percentageDone, col, bytesPerSecond)).reset().toString(); // Check if printed Utils.PROGRESS_MARKER and progressInfo would exceed console width int progressMarkerLength = dm.getBlockSize()/(dm.getMaxPayload() * Utils.PROGRESS_MARKER.length()); if ((progressMarkerLength + progressInfo.length()) > Utils.CONSOLE_WIDTH) { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 10d1105d..db765a26 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -240,8 +240,8 @@ public void run() { dm.unlockDeviceWithUID(uid); if ((cliOptions.dumpFlashStartAddress() >= 0) && (cliOptions.dumpFlashEndAddress() >= 0)) { - logger.warn("{}Dumping flash content range 0x{}-0x{} to bootloader's serial port.{}", - ConColors.BRIGHT_GREEN, String.format("%04X", cliOptions.dumpFlashStartAddress()), String.format("%04X", cliOptions.dumpFlashEndAddress()), ConColors.RESET); + logger.warn(ansi().fgBright(GREEN).a("Dumping flash content range 0x{}-0x{} to bootloader's serial port.").reset().toString(), + String.format("%04X", cliOptions.dumpFlashStartAddress()), String.format("%04X", cliOptions.dumpFlashEndAddress())); dm.dumpFlashRange(cliOptions.dumpFlashStartAddress(), cliOptions.dumpFlashEndAddress()); return; } @@ -283,7 +283,7 @@ public void run() { BinImage imageCache = BinImage.copyFromArray(newFirmware.getBinData(), newFirmware.startAddress()); imageCache.writeToBinFile(cacheFileName); - logger.info(" File APP_VERSION : {}{}{}", ConColors.BRIGHT_GREEN, newFirmware.getAppVersion(), ConColors.RESET); + logger.info(" File APP_VERSION : {}", ansi().fgBright(GREEN).a(newFirmware.getAppVersion()).reset().toString()); // Check if FW image has correct offset for MCUs bootloader size if (newFirmware.startAddress() < bootLoaderIdentity.getApplicationFirstAddress()) { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java index 5cad5434..16562429 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java @@ -4,6 +4,9 @@ import org.selfbus.updater.UpdaterException; import org.selfbus.updater.Utils; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; + /** * Holds Application Boot Block Descriptor the MCU's bootloader * see software-arm-lib/Bus-Updater/inc/boot_descriptor_block.h for more information @@ -78,12 +81,12 @@ public long length() { public String toString() { String res; if (valid) { - res = String.format(" %svalid%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X", - ConColors.BRIGHT_GREEN, ConColors.RESET, startAddress, endAddress, length(), crc32); + res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X", + ansi().fgBright(GREEN).a(" valid").reset().toString(), startAddress, endAddress, length(), crc32); } else { - res = String.format("%sinvalid%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X", - ConColors.RED, ConColors.RESET, INVALID_ADDRESS & 0x0000FFFF, + res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X", + ansi().fg(RED).a("invalid").reset().toString(), INVALID_ADDRESS & 0x0000FFFF, INVALID_ADDRESS & 0x0000FFFF, length(), crc32); } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java index e0aeef62..469ccfc7 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java @@ -14,6 +14,9 @@ import java.util.List; import java.util.zip.CRC32; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; + public class FlashDiff { private final static Logger logger = LoggerFactory.getLogger(FlashDiff.class.getName()); private static final int MINIMUM_PATTERN_LENGTH = 6; // less that this is not efficient (metadata would be larger than data) @@ -240,7 +243,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash System.out.print("Page " + pages + ", "); flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); totalBytesTransferred = size; - logger.debug("OK! Total diff stream length={}{}{} bytes", ConColors.BRIGHT_GREEN, size, ConColors.RESET); + logger.debug("OK! Total diff stream length={} bytes", ansi().fgBright(GREEN).a(size).reset().toString()); } //dumpSideBySide(img1, img2); } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java index 6b579f10..64bb978d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java @@ -43,14 +43,14 @@ public static long checkResult(byte[] result, boolean verbose) { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); if (root.getLevel() == Level.TRACE) { // only to display the message on the console - logger.debug("{}done ({}){}", ConColors.BRIGHT_GREEN, udpResult.id, ConColors.RESET); + logger.debug("done ({})", ansi().fgBright(GREEN).a(udpResult.id).reset().toString()); } else { - logger.trace("{}done ({}){}", ConColors.BRIGHT_GREEN, udpResult.id, ConColors.RESET); + logger.trace("done ({})", ansi().fgBright(GREEN).a(udpResult.id).reset().toString()); } } else { - System.out.printf("%s%s%s", ConColors.BRIGHT_GREEN, Utils.PROGRESS_MARKER, ConColors.RESET); // Success in green + System.out.printf(ansi().fgBright(GREEN).a(Utils.PROGRESS_MARKER).reset().toString()); // Success in green logger.debug(Utils.PROGRESS_MARKER); } } From 7fa8af2d13b3326cec8c69c71cb11acd30372e02 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 05:12:52 +0200 Subject: [PATCH 022/359] [Updater] Replace `RED` --- .../java/org/selfbus/updater/BinImage.java | 10 ++++++---- .../java/org/selfbus/updater/CliOptions.java | 17 ++++++++++------- .../java/org/selfbus/updater/ConColors.java | 4 ---- .../org/selfbus/updater/DeviceManagement.java | 18 +++++++++--------- .../org/selfbus/updater/FlashFullMode.java | 4 ++-- .../main/java/org/selfbus/updater/Updater.java | 5 +++-- 6 files changed, 30 insertions(+), 28 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java index 5b2df0d8..c7bbfe6e 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java @@ -17,6 +17,9 @@ import java.util.Arrays; import java.util.zip.CRC32; +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; + public class BinImage { private final static Logger logger = LoggerFactory.getLogger(BinImage.class.getName()); private byte[] binData; @@ -126,8 +129,8 @@ public final boolean writeToBinFile(String fileName) { File binFile = new File(fileName); if (!binFile.getParentFile().exists()) { if (!binFile.getParentFile().mkdirs()) { - logger.warn("{}Could not create bin-file directory {}{}", - ConColors.RED, binFile.getParentFile().toString(), ConColors.RESET); + logger.warn(ansi().fg(RED).a("Could not create bin-file directory {}").reset().toString(), + binFile.getParentFile().toString()); } return false; } @@ -136,8 +139,7 @@ public final boolean writeToBinFile(String fileName) { fos.write(binData); return true; } catch (IOException e) { - logger.warn("{}Could not write bin-file {}{}", - ConColors.RED, binFile.getPath(), ConColors.RESET); + logger.warn(ansi().fg(RED).a("Could not write bin-file {}").reset().toString(), binFile.getPath()); } return false; } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 2a1578d0..98463bc0 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -366,7 +366,7 @@ private void parse(final String[] args) { root.setLevel(logLevel); } else { - logger.warn("{}invalid {} {}, using {}{}", ConColors.RED, OPT_LONG_LOGLEVEL, cliLogLevel, root.getLevel().toString(), ConColors.RESET); + logger.warn(ansi().fg(RED).a("invalid {} {}, using {}").reset().toString(), OPT_LONG_LOGLEVEL, cliLogLevel, root.getLevel().toString()); } } logger.debug("logLevel={}", root.getLevel().toString()); @@ -376,7 +376,8 @@ private void parse(final String[] args) { priority = Priority.get(cmdLine.getOptionValue(OPT_LONG_PRIORITY)); } catch (KNXIllegalArgumentException e) { - logger.warn("{}invalid {} {}, using {}{}", ConColors.RED, OPT_LONG_PRIORITY, cmdLine.getOptionValue(OPT_LONG_PRIORITY), priority, ConColors.RESET); + logger.warn(ansi().fg(RED).a("invalid {} {}, using {}").reset().toString(), + OPT_LONG_PRIORITY, cmdLine.getOptionValue(OPT_LONG_PRIORITY), priority); } } logger.debug("priority={}", priority.toString()); @@ -464,7 +465,8 @@ private void parse(final String[] args) { if (cmdLine.hasOption(OPT_LONG_DELAY)) { delay = ((Number)cmdLine.getParsedOptionValue(OPT_LONG_DELAY)).intValue(); if ((delay < Updater.DELAY_MIN) || (delay > Updater.DELAY_MAX)) { - logger.warn("{}option --delay {} is invalid (min:{}, max:{}) => setting --delay {}{}", ConColors.RED, delay, Updater.DELAY_MIN, Updater.DELAY_MAX, Updater.DELAY_DEFAULT, ConColors.RESET); + logger.warn(ansi().fg(RED).a("option --delay {} is invalid (min:{}, max:{}) => setting --delay {}").reset().toString(), + delay, Updater.DELAY_MIN, Updater.DELAY_MAX, Updater.DELAY_DEFAULT); delay = Updater.DELAY_DEFAULT; // set to DELAY_DEFAULT in case of invalid waiting time } } @@ -548,11 +550,12 @@ private void parse(final String[] args) { // differential mode and eraseflash makes no sense if (eraseFullFlash() && (!full())) { full = true; - logger.info("{}--{} is set. --> switching to full flash mode{}", ConColors.RED, OPT_LONG_ERASEFLASH, ConColors.RESET); + logger.info(ansi().fg(RED).a("--{} is set. --> switching to full flash mode").reset().toString(), OPT_LONG_ERASEFLASH); } if (nat() && (!tunnelingV1())) { - throw new ParseException(String.format("%sOption --%s can only be used together with --%s%s", ConColors.RED, OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ConColors.RESET)); + throw new ParseException(String.format(ansi().fg(RED).a("Option --%s can only be used together with --%s").reset().toString(), + OPT_LONG_NAT, OPT_LONG_TUNNEL_V1)); } int interfacesSet = 0; @@ -564,7 +567,7 @@ private void parse(final String[] args) { if (!tpuart().isEmpty()) interfacesSet++; if (interfacesSet > 1) { - throw new ParseException(String.format("%sOnly one bus interface can be used.%s", ConColors.RED, ConColors.RESET)); + throw new ParseException(ansi().fg(RED).a("Only one bus interface can be used.").reset().toString()); } } catch (ParseException | KNXFormatException e) { @@ -574,7 +577,7 @@ private void parse(final String[] args) { } logger.error("Invalid command line parameters:"); logger.error("{}", cliParsed); - logger.error("{}{}{}", ConColors.RED, e.getMessage(), ConColors.RESET); + logger.error(ansi().fg(RED).a(e.getMessage()).reset().toString()); logger.error("For more information about the usage start with --{}", OPT_LONG_HELP); logger.error("", e); System.exit(0); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java index 3160ae0c..49839664 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java @@ -4,10 +4,6 @@ * Provides constants for console color output (font color/style and background) */ public class ConColors { - // Reset to default colors - public static final String RESET = "\033[0m"; // Reset - // Regular Colors - public static final String RED = "\033[0;31m"; // RED } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index 6801d63a..38eabc39 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -145,8 +145,8 @@ public BootloaderIdentity requestBootloaderIdentity() if (result[COMMAND_POSITION] == UPDCommand.RESPONSE_BL_VERSION_MISMATCH.id) { long minMajorVersion = result[DATA_POSITION] & 0xff; long minMinorVersion = result[DATA_POSITION + 1] & 0xff; - logger.error("{}Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.{}", - ConColors.RED, ToolInfo.getVersion(), minMajorVersion, minMinorVersion, ConColors.RESET); + logger.error(ansi().fg(RED).a("Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.").reset().toString(), + ToolInfo.getVersion(), minMajorVersion, minMinorVersion); } else { UPDProtocol.checkResult(result); @@ -163,8 +163,8 @@ public BootloaderIdentity requestBootloaderIdentity() if (!versionsMatch) { - logger.error("{}Bootloader version {} is not compatible, please update Bootloader to version {} or higher{}", - ConColors.RED, bl.getVersion(), ToolInfo.minVersionBootloader(), ConColors.RESET); + logger.error(ansi().fg(RED).a("Bootloader version {} is not compatible, please update Bootloader to version {} or higher").reset().toString(), + bl.getVersion(), ToolInfo.minVersionBootloader()); throw new UpdaterException("Bootloader version not compatible!"); } return bl; @@ -276,7 +276,7 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay) result.addCounters(tmp); if ((tmp.dropCount() > 0) || (tmp.timeoutCount() > 0)) { - logger.warn("{}x{}", ConColors.RED, ConColors.RESET); + logger.warn(ansi().fg(RED).a("x").reset().toString()); continue; } @@ -336,10 +336,10 @@ public void requestBootLoaderStatistic() logger.info(" Bootloader: {}", blStatistic); } else { - logger.warn(" {}{}{}", ConColors.RED, + logger.warn(ansi().fg(RED).a(" {}").reset().toString(), String.format("Requesting Bootloader statistic failed! result[%d]=0x%02X, result[%d]=0x%02X", COMMAND_POSITION, result[COMMAND_POSITION], - DATA_POSITION, result[DATA_POSITION]), ConColors.RESET); + DATA_POSITION, result[DATA_POSITION])); } } @@ -353,11 +353,11 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr return result; } catch (KNXTimeoutException e) { - logger.warn("{}{} {} : {}{}", ConColors.RED, command, e.getMessage(), e.getClass().getSimpleName(), ConColors.RESET); + logger.warn(ansi().fg(RED).a("{} {} : {}").reset().toString(), command, e.getMessage(), e.getClass().getSimpleName()); result.incTimeoutCount(); } catch (KNXDisconnectException | KNXRemoteException e) { ///\todo check causes of KNXRemoteException, if think they are unrecoverable - logger.warn("{}{} {} : {}{}", ConColors.RED, command, e.getMessage(), e.getClass().getSimpleName(), ConColors.RESET); + logger.warn(ansi().fg(RED).a("{} {} : {}").reset().toString(), command, e.getMessage(), e.getClass().getSimpleName()); result.incDropCount(); } catch (KNXIllegalArgumentException e) { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java index 5656286c..8e2e6e57 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java @@ -106,8 +106,8 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa resultTotal.setWritten(resultTotal.written() - txBuffer.length); // do not count failed transfer } else if (result == IAP_COMPARE_ERROR.id) { - throw new UpdaterException(String.format("ProgramData update failed. %sTry again with option '--%s 256'%s", - ConColors.RED, CliOptions.OPT_LONG_BLOCKSIZE, ConColors.RESET)); + throw new UpdaterException(String.format("ProgramData update failed. %s", + String.format(ansi().fg(RED).a("Try again with option '--%s 256'").reset().toString(), CliOptions.OPT_LONG_BLOCKSIZE))); } else if (result == IAP_SUCCESS.id) { progAddress += txBuffer.length; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index db765a26..749d2508 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -198,7 +198,7 @@ public void run() { if (!hexFileName.isEmpty()) { // check if the firmware file exists if (!Utils.fileExists(hexFileName)) { - logger.error("{}File {} does not exist!{}", ConColors.RED, cliOptions.fileName(), ConColors.RESET); + logger.error(ansi().fg(RED).a("File {} does not exist!").reset().toString(), cliOptions.fileName()); throw new UpdaterException("Selfbus update failed."); } // Load Firmware hex file @@ -354,7 +354,8 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd printStatisticData(flashTimeStart, resultTotal); } else { - logger.warn("--{} => {}only boot description block will be written{}", CliOptions.OPT_LONG_NO_FLASH, ConColors.RED, ConColors.RESET); + logger.warn("--{} => {}", CliOptions.OPT_LONG_NO_FLASH, + ansi().fg(RED).a("only boot description block will be written").reset().toString()); } BootDescriptor newBootDescriptor = new BootDescriptor(newFirmware.startAddress(), From 2fa655e8db03ed8b3737b1e548258fa670afbfdc Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 1 Aug 2024 05:15:47 +0200 Subject: [PATCH 023/359] [Updater] Delete with jansi replaced `ConColors` --- .../src/main/java/org/selfbus/updater/ConColors.java | 9 --------- .../org/selfbus/updater/bootloader/BootDescriptor.java | 1 - .../selfbus/updater/bootloader/BootloaderStatistic.java | 1 - .../org/selfbus/updater/tests/flashdiff/FlashDiff.java | 1 - .../main/java/org/selfbus/updater/upd/UPDProtocol.java | 1 - 5 files changed, 13 deletions(-) delete mode 100644 firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java deleted file mode 100644 index 49839664..00000000 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ConColors.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.selfbus.updater; - -/** - * Provides constants for console color output (font color/style and background) - */ -public class ConColors { - -} - diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java index 16562429..10a2fb3f 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java @@ -1,6 +1,5 @@ package org.selfbus.updater.bootloader; -import org.selfbus.updater.ConColors; import org.selfbus.updater.UpdaterException; import org.selfbus.updater.Utils; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java index f2c8132f..671389ba 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java @@ -1,6 +1,5 @@ package org.selfbus.updater.bootloader; -import org.selfbus.updater.ConColors; import org.selfbus.updater.Utils; import static org.fusesource.jansi.Ansi.*; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java index 469ccfc7..a1aac93d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java @@ -1,7 +1,6 @@ package org.selfbus.updater.tests.flashdiff; import org.selfbus.updater.BinImage; -import org.selfbus.updater.ConColors; import org.selfbus.updater.UpdaterException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java index 64bb978d..748a2301 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java @@ -1,7 +1,6 @@ package org.selfbus.updater.upd; import ch.qos.logback.classic.Level; -import org.selfbus.updater.ConColors; import org.selfbus.updater.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From ceb9662968081b3f8136a7775e847a1cb1b18b97 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 3 Aug 2024 13:11:07 +0200 Subject: [PATCH 024/359] [Updater] Update to JUnit 5 test framework --- firmware_updater/updater/source/build.gradle | 34 +++++++++++++++++-- .../updater/BootloaderIdentityTest.java | 4 +-- .../updater/BootloaderStatisticTest.java | 4 +-- .../org/selfbus/updater/ToolInfoTest.java | 7 ++-- .../java/org/selfbus/updater/UtilsTest.java | 22 ++++++------ .../selfbus/updater/tests/FlashDiffTests.java | 2 +- .../selfbus/updater/tests/FlashDiffUtils.java | 4 +-- 7 files changed, 53 insertions(+), 24 deletions(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 28c57632..079c9465 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java' id 'application' + id 'jvm-test-suite' } group 'org.selfbus' @@ -59,6 +60,36 @@ gradle.projectsEvaluated{ } } +testing { + // + // comment avoids warning "suites' cannot be applied to '(groovy.lang.closure)'" + // + //noinspection GroovyAssignabilityCheck + suites { + test { + useJUnitJupiter() + } + + integrationTest(JvmTestSuite) { + dependencies { + implementation project() + } + + targets { + all { + testTask.configure { + shouldRunAfter(test) + } + } + } + } + } +} + +tasks.named('check') { + dependsOn(testing.suites.integrationTest) +} + dependencies { // calimero knx bus access library implementation 'com.github.calimero:calimero-core:2.6-rc1' @@ -78,9 +109,6 @@ dependencies { // console and file logging implementation 'ch.qos.logback:logback-classic:1.5.6' - // unit test - testImplementation group: 'junit', name: 'junit', version: '4.13.2' - // Console ansi color support implementation 'org.fusesource.jansi:jansi:2.4.0' diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java index 94a2f674..e0210889 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java @@ -1,9 +1,9 @@ package org.selfbus.updater; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.selfbus.updater.bootloader.BootloaderIdentity; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*;; public class BootloaderIdentityTest { diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java index 01099dfe..1ca41f0e 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java @@ -1,9 +1,9 @@ package org.selfbus.updater; import org.selfbus.updater.bootloader.BootloaderStatistic; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/ToolInfoTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/ToolInfoTest.java index 8746170b..d0b25100 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/ToolInfoTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/ToolInfoTest.java @@ -1,15 +1,18 @@ package org.selfbus.updater; -import junit.framework.TestCase; +import org.junit.jupiter.api.Test; -public class ToolInfoTest extends TestCase { +public class ToolInfoTest { + @Test public void testGetVersion() { } + @Test public void testGetAuthor() { } + @Test public void testGetTool() { } } \ No newline at end of file diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java index e968a6d4..6913b68b 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java @@ -1,9 +1,7 @@ package org.selfbus.updater; -import org.hamcrest.CoreMatchers; -import org.junit.Test; - -import static org.hamcrest.MatcherAssert.assertThat; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class UtilsTest { @@ -36,7 +34,7 @@ public void testStreamToLong() { long expectedLong = l + (k << 8) + (j << 16) + (i << 24); long result; result = Utils.streamToLong(testStream, 0); - assertThat(result, CoreMatchers.is(expectedLong)); + assertEquals(result, expectedLong); } } } @@ -60,7 +58,7 @@ public void testLongToStream() { for (int i = 0; i < underTest.length; i++) { Utils.longToStream(testStream, 0, underTest[i]); expectedLong = Utils.streamToLong(testStream, 0); - assertThat(underTest[i], CoreMatchers.is(expectedLong)); + assertEquals(underTest[i], expectedLong); } } @@ -83,26 +81,26 @@ public void testShortenPath() { // test windows path testFileName = "C:\\Users\\user\\AppData\\Local\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin"; testFileName = Utils.shortenPath(testFileName, thresholdToTest + 1); - assertThat(testFileName, CoreMatchers.is("C:\\...\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin")); + assertEquals(testFileName, "C:\\...\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin"); // test linux path testFileName = Utils.shortenPath("/home/username/.cache/Selfbus-Updater/0.61/image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertThat(testFileName, CoreMatchers.is("/.../.cache/Selfbus-Updater/0.61/image-0x7000-34624-0xe8b27ade.bin")); + assertEquals(testFileName, "/.../.cache/Selfbus-Updater/0.61/image-0x7000-34624-0xe8b27ade.bin"); // test UNC path testFileName = Utils.shortenPath("\\\\share\\funstuff\\running\\from\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertThat(testFileName, CoreMatchers.is("\\\\...\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin")); + assertEquals(testFileName, "\\\\...\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin"); // test http testFileName = Utils.shortenPath("http://tester.com/a1/b2/c3/d4/image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertThat(testFileName, CoreMatchers.is("http://.../b2/c3/d4/image-0x7000-34624-0xe8b27ade.bin")); + assertEquals(testFileName, "http://.../b2/c3/d4/image-0x7000-34624-0xe8b27ade.bin"); // test ftp testFileName = Utils.shortenPath("ftp://tester.com/a1/b2/c3/d4/e5/image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertThat(testFileName, CoreMatchers.is("ftp://.../c3/d4/e5/image-0x7000-34624-0xe8b27ade.bin")); + assertEquals(testFileName, "ftp://.../c3/d4/e5/image-0x7000-34624-0xe8b27ade.bin"); // test nothing to shorten testFileName = Utils.shortenPath("C:\\Local\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertThat(testFileName, CoreMatchers.is(testFileName)); + assertEquals(testFileName, testFileName); } } \ No newline at end of file diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java index ef0b5567..f4eddb50 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java @@ -1,6 +1,6 @@ package org.selfbus.updater.tests; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.selfbus.updater.BinImage; import org.selfbus.updater.UpdaterException; import org.selfbus.updater.tests.flashdiff.Decompressor; diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffUtils.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffUtils.java index ac6b2f5b..17d5d2a0 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffUtils.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffUtils.java @@ -1,6 +1,6 @@ package org.selfbus.updater.tests; -import org.junit.Assert; +import static org.junit.jupiter.api.Assertions.fail; import org.selfbus.updater.BinImage; public class FlashDiffUtils { @@ -29,7 +29,7 @@ public static void dumpSideBySide(byte[] ar1, byte[] ar2, int offset) { System.out.println(" EQ"); } else { System.out.println(" NOT EQUAL !!!"); - Assert.fail("Arrays not equals"); + fail("Arrays not equals"); } } } From 678bcdcc9601b316e4c2760749f7617ea1a7ff88 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 3 Aug 2024 13:37:46 +0200 Subject: [PATCH 025/359] [Updater] Fix gui `jLoggingPane` colors Fixed/refactored Ansi parsing in `ConColorsToStyledDoc.Convert` to work more reliably with `jansi` and additional Ansi codes, e.g. `strike`, `italic`, `underline`. Added some testing for `ConColorsToStyledDoc`. --- .../updater/gui/ConColorsToStyledDoc.java | 312 +++++++++++++----- .../java/org/selfbus/updater/gui/GuiMain.java | 6 +- .../gui/ConColorsToStyledDocTests.java | 208 ++++++++++++ 3 files changed, 438 insertions(+), 88 deletions(-) create mode 100644 firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java index c512881f..84f53879 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -1,113 +1,253 @@ package org.selfbus.updater.gui; import javax.swing.text.*; -import java.awt.*; -import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class ConColorsToStyledDoc { - private static final StyleContext sc = new StyleContext(); - private static final Style stringStyle = sc.getStyle(StyleContext.DEFAULT_STYLE); + private static final Style stringStyle = new javax.swing.text.StyleContext().addStyle("testStyle", null); + + public static final java.awt.Color DefaultForegroundColor = java.awt.Color.white; + public static final java.awt.Color DefaultBackgroundColor = java.awt.Color.black; + private static final String RegExAnsi = "\033\\[([;\\d]*)m"; // Regular expression to match ANSI escape sequences + private static final String AnsiCodeSeparator = ";"; + + private static void insertStyledString(String toInsert, String colorCode, StyledDocument document) throws BadLocationException { + Style newStyle = colorCodeToStyle(colorCode.split(AnsiCodeSeparator)); + document.insertString(document.getLength(), toInsert, newStyle); + } /* * hier werden die Steuerzeichen für die Farben der Windows Konsole in Java Parameter gewandelt * z.B. wird ein String "\033[0;31m dieser Text wird rot sein,\033[44m jetzt auf blauem Hintergrund" übergeben * Dieser String wird entsprechend der Steuerbefehle in ein StyledDocument gefüllt */ - public static void Convert(String docWithConColors, StyledDocument originDocument) throws BadLocationException { - - if(!docWithConColors.contains("\033")) { - // ohne ConColors im Datenstring - originDocument.insertString(originDocument.getLength(), docWithConColors, null); - return; + public static void Convert(String input, StyledDocument originDocument) throws BadLocationException { + Pattern pattern = Pattern.compile(RegExAnsi); + Matcher matcher = pattern.matcher(input); + + String cleanedString = ""; + String colorCode = ""; + int index = 0; + while (matcher.find()) { + if (matcher.start() > index) { // possible text before the ANSI escape sequence + cleanedString = input.substring(index, matcher.start()); + insertStyledString(cleanedString, colorCode, originDocument); + } + colorCode = matcher.group(1); + index = matcher.end(); } - String colorCode =""; - int startPart = 0; - int startIndex = docWithConColors.indexOf("\033"); - String[] splitDoc = docWithConColors.split("\033"); - - // wenn der String vor dem ersten ConColor Zeichen hat, werden diese ohne Formatierung angehängt - if(startIndex != 0){ - startPart = 1; - originDocument.insertString(originDocument.getLength(), splitDoc[0], null); - } - - for (int docPartCnt = startPart; docPartCnt < splitDoc.length; docPartCnt++) { - String docPart = splitDoc[docPartCnt]; - if(!docPart.isEmpty()) { - for (int i = 2; i < 7; i++) { - String endChar = docPart.substring(i, i + 1); - if (endChar.equals("m")) { - colorCode = docPart.substring(1, i); - break; - } - } - String cleanedString = docPart.replace("[" + colorCode + "m", ""); - - Style partStyle = convertConColorToStyle(colorCode); - - int oldDocLength = originDocument.getLength(); - - originDocument.insertString(originDocument.getLength(), cleanedString, null); - - originDocument.setCharacterAttributes(oldDocLength, cleanedString.length(), partStyle,false); - } + if (index < input.length()) { // possible remaining text after the last ANSI escape sequence + cleanedString = input.substring(index); + insertStyledString(cleanedString, colorCode, originDocument); } } - private static Style convertConColorToStyle(String colorCode){ - // ColorCode kommt z.B. 0;30 oder 0;105 oder 41 oder 0 oder ... + private static void resetStringStyle() { + StyleConstants.setForeground(ConColorsToStyledDoc.stringStyle, DefaultForegroundColor); + StyleConstants.setBackground(ConColorsToStyledDoc.stringStyle, DefaultBackgroundColor); + StyleConstants.setBold(ConColorsToStyledDoc.stringStyle,false); + StyleConstants.setItalic(ConColorsToStyledDoc.stringStyle,false); + StyleConstants.setUnderline(ConColorsToStyledDoc.stringStyle,false); + StyleConstants.setStrikeThrough(ConColorsToStyledDoc.stringStyle,false); + } - if(colorCode.contains(";")){ - // Regular Colors or High Intensity or High Intensity Background or Regular Bold or High Intensity Bold - String[] colorCodeParts = colorCode.split(";"); - if(Objects.equals(colorCodeParts[0], "0")){ // Regular Colors or High Intensity or High Intensity Background - if(Integer.parseInt(colorCodeParts[1]) < 100){ - // Regular Colors or High Intensity - Color color = ConColorToColor(Integer.parseInt(colorCodeParts[1].substring(1,2))); - StyleConstants.setForeground(stringStyle, color); - }else{ - // High Intensity Background - Color color = ConColorToColor(Integer.parseInt(colorCodeParts[1].substring(2,3))); - StyleConstants.setBackground(stringStyle, color); - } - }else if(Objects.equals(colorCodeParts[0], "1")){ - // Regular Bold or High Intensity Bold - Color color = ConColorToColor(Integer.parseInt(colorCodeParts[1].substring(1,2))); - StyleConstants.setForeground(stringStyle, color); - StyleConstants.setBold(stringStyle,true); - } - }else{ - // Regular Background or Reset - if(colorCode.equals("0")){ - // Reset - StyleConstants.setForeground(stringStyle, Color.white); - StyleConstants.setBackground(stringStyle, Color.black); - StyleConstants.setBold(stringStyle,false); + private static Style colorCodeToStyle(String [] colorCodes) { + for (String code : colorCodes) { + if (code.isEmpty()) { + resetStringStyle(); + continue; } - else{ - // Regular Background - Color color = ConColorToColor(Integer.parseInt(colorCode.substring(1,2))); - StyleConstants.setBackground(stringStyle, color); + + switch (Integer.parseInt(code)) { + case 0: // Reset + resetStringStyle(); + break; + case 1: // Bold on + StyleConstants.setBold(stringStyle,true); + break; + case 2: // Bold off + StyleConstants.setBold(stringStyle,false); + break; + case 3: // Italic on + StyleConstants.setItalic(stringStyle,true); + break; + case 4: // Underline on + StyleConstants.setUnderline(stringStyle,true); + break; + // + // 5-8 reserved + // + case 9: // Strike on + StyleConstants.setStrikeThrough(stringStyle,true); + break; + // + // 10-20 reserved + // + case 21: // Bold off (same as 2) + StyleConstants.setBold(stringStyle,false); + break; + // 22 reserved + case 23: // Italic off + StyleConstants.setItalic(stringStyle,false); + break; + case 24: // Underline off + StyleConstants.setUnderline(stringStyle,false); + break; + // 25-28 reserved + case 29: // Strike off + StyleConstants.setStrikeThrough(stringStyle,false); + break; + // + // Foreground colors + // + case 30: // Foreground color black + StyleConstants.setForeground(stringStyle, java.awt.Color.black); + break; + case 31: // Foreground color red + StyleConstants.setForeground(stringStyle, java.awt.Color.red); + break; + case 32: // Foreground color green + StyleConstants.setForeground(stringStyle, java.awt.Color.green); + break; + case 33: // Foreground color yellow + // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO + StyleConstants.setForeground(stringStyle, java.awt.Color.orange); + break; + case 34: // Foreground color blue + StyleConstants.setForeground(stringStyle, java.awt.Color.blue); + break; + case 35: // Foreground color magenta + //todo check which one looks better: java.awt.Color.magenta or java.awt.Color.pink? + StyleConstants.setForeground(stringStyle, java.awt.Color.pink); + break; + case 36: // Foreground color cyan + StyleConstants.setForeground(stringStyle, java.awt.Color.cyan); + break; + case 37: // Foreground color white + StyleConstants.setForeground(stringStyle, java.awt.Color.white); + break; + case 38: + throw new IllegalStateException("Extended foreground colors not implemented"); + case 39: // Foreground color default + StyleConstants.setForeground(stringStyle, DefaultForegroundColor); + break; + // + // Background colors + // + case 40: // Background color black + StyleConstants.setBackground(stringStyle, java.awt.Color.black); + break; + case 41: // Background color red + StyleConstants.setBackground(stringStyle, java.awt.Color.red); + break; + case 42: // Background color green + StyleConstants.setBackground(stringStyle, java.awt.Color.green); + break; + case 43: // Background color yellow + // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO + StyleConstants.setBackground(stringStyle, java.awt.Color.orange); + break; + case 44: // Background color blue + StyleConstants.setBackground(stringStyle, java.awt.Color.blue); + break; + case 45: // Background color magenta + //todo check which one looks better: java.awt.Color.magenta or java.awt.Color.pink? + StyleConstants.setBackground(stringStyle, java.awt.Color.pink); + break; + case 46: // Background color cyan + StyleConstants.setBackground(stringStyle, java.awt.Color.cyan); + break; + case 47: // Background color white + StyleConstants.setBackground(stringStyle, java.awt.Color.white); + break; + case 48: + throw new IllegalStateException("Extended background colors not implemented"); + case 49: // Background color default + StyleConstants.setBackground(stringStyle, DefaultBackgroundColor); + break; + // + // 50-89 reserved + // + + // + // Bright foreground colors + // + //todo find better java.awt.Color values, right now, they are the same as "normal" + case 90: // Bright foreground color black + StyleConstants.setForeground(stringStyle, java.awt.Color.black); + break; + case 91: // Bright foreground color red + StyleConstants.setForeground(stringStyle, java.awt.Color.red); + break; + case 92: // Bright foreground color green + StyleConstants.setForeground(stringStyle, java.awt.Color.green); + break; + case 93: // Bright foreground color yellow + // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO + StyleConstants.setForeground(stringStyle, java.awt.Color.orange); + break; + case 94: // Bright foreground color blue + StyleConstants.setForeground(stringStyle, java.awt.Color.blue); + break; + case 95: // Bright foreground color magenta + //todo check which one looks better: java.awt.Color.magenta or java.awt.Color.pink? + StyleConstants.setForeground(stringStyle, java.awt.Color.pink); + break; + case 96: // Bright foreground color cyan + StyleConstants.setForeground(stringStyle, java.awt.Color.cyan); + break; + case 97: // Bright foreground color white + StyleConstants.setForeground(stringStyle, java.awt.Color.white); + break; + // + // 98-99 reserved + // + + // + // Bright background colors + // + //todo find better java.awt.Color values, right now, they are the same as "normal" + case 100: // Bright background color black + StyleConstants.setBackground(stringStyle, java.awt.Color.black); + break; + case 101: // Bright background color red + StyleConstants.setBackground(stringStyle, java.awt.Color.red); + break; + case 102: // Bright background color green + StyleConstants.setBackground(stringStyle, java.awt.Color.green); + break; + case 103: // Bright background color yellow + // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO + StyleConstants.setBackground(stringStyle, java.awt.Color.orange); + break; + case 104: // Bright background color blue + StyleConstants.setBackground(stringStyle, java.awt.Color.blue); + break; + case 105: // Bright background color magenta + //todo check which one looks better: java.awt.Color.magenta or java.awt.Color.pink? + StyleConstants.setBackground(stringStyle, java.awt.Color.pink); + break; + case 106: // Bright background color cyan + StyleConstants.setBackground(stringStyle, java.awt.Color.cyan); + break; + case 107: // Bright background color white + StyleConstants.setBackground(stringStyle, java.awt.Color.white); + break; + default: + throw new IllegalStateException("Unexpected code value: " + Integer.parseInt(code)); } } return stringStyle; } - private static Color ConColorToColor(int colorNumber){ - Color retColor = switch (colorNumber) { - case 0 -> Color.black; - case 1 -> Color.red; - case 2 -> Color.green; - case 3 -> Color.orange; - case 4 -> Color.blue; - case 5 -> Color.pink; - case 6 -> Color.cyan; - case 7 -> Color.white; - default -> null; - }; + public static Style testColorCodeToStyle(String [] colorCodes) { + return colorCodeToStyle(colorCodes); + } - return retColor; + public static Style testStringStyle() { + return stringStyle; } } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index e87ef5b7..b6cea70b 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -32,6 +32,8 @@ import java.util.List; import static java.awt.Font.PLAIN; +import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultBackgroundColor; +import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultForegroundColor; @SuppressWarnings("serial") public class GuiMain extends JFrame { @@ -390,8 +392,8 @@ public void startUpdaterGui() { this.setTitle(ToolInfo.getToolAndVersion()); this.setSize(1000, 800); this.jLoggingPane.setFont(new Font("Courier New", PLAIN, 12)); - this.jLoggingPane.setBackground(Color.black); - this.jLoggingPane.setForeground(Color.white); + this.jLoggingPane.setBackground(DefaultBackgroundColor); + this.jLoggingPane.setForeground(DefaultForegroundColor); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java new file mode 100644 index 00000000..55798ff3 --- /dev/null +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java @@ -0,0 +1,208 @@ +package org.selfbus.updater.gui; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import static org.selfbus.updater.gui.ConColorsToStyledDoc.*; + +import javax.swing.text.*; +import java.awt.*; + +public class ConColorsToStyledDocTests { + + @BeforeEach + public void setup() { + shuffleStyle(false); + } + + private void assertStyleForeground(java.awt.Color expectedColor) { + assertEquals(expectedColor, StyleConstants.getForeground(testStringStyle())); + } + + private void assertStyleBackground(java.awt.Color expectedColor) { + assertEquals(expectedColor, StyleConstants.getBackground(testStringStyle())); + } + + private void assertStyleBold(boolean expectedBold) { + assertEquals(expectedBold, StyleConstants.isBold(testStringStyle())); + } + + private void assertStyleItalic(boolean expectedItalic) { + assertEquals(expectedItalic, StyleConstants.isItalic(testStringStyle())); + } + + private void assertStyleUnderline(boolean expectedUnderline) { + assertEquals(expectedUnderline, StyleConstants.isUnderline(testStringStyle())); + } + + private void assertStyleStrikeThrough(boolean expectedStrikeThrough) { + assertEquals(expectedStrikeThrough, StyleConstants.isStrikeThrough(testStringStyle())); + } + + private void shuffleStyle(boolean setAttribute) { + // fore and background intentionally swapped + StyleConstants.setForeground(testStringStyle(), DefaultBackgroundColor); + StyleConstants.setBackground(testStringStyle(), DefaultForegroundColor); + StyleConstants.setBold(testStringStyle(),setAttribute); + StyleConstants.setItalic(testStringStyle(),setAttribute); + StyleConstants.setUnderline(testStringStyle(),setAttribute); + StyleConstants.setStrikeThrough(testStringStyle(),setAttribute); + } + + @Test + public void testJansiReset() { + shuffleStyle(true); + testColorCodeToStyle(new String[]{""}); + assertStyleForeground(DefaultForegroundColor); + assertStyleBackground(DefaultBackgroundColor); + assertStyleBold(false); + assertStyleItalic(false); + assertStyleUnderline(false); + assertStyleStrikeThrough(false); + } + + @Test + public void testStandardReset() { + shuffleStyle(true); + testColorCodeToStyle(new String[]{"0"}); + // Assuming empty string should reset the style + assertStyleForeground(DefaultForegroundColor); + assertStyleBackground(DefaultBackgroundColor); + assertStyleBold(false); + assertStyleItalic(false); + assertStyleUnderline(false); + assertStyleStrikeThrough(false); + } + + @Test + public void testForegroundColors() { + java.awt.Color backgroundColor = StyleConstants.getBackground(testStringStyle()); + testColorCodeToStyle(new String[]{"30"}); + assertStyleForeground(Color.black); + assertStyleBackground(backgroundColor); + + testColorCodeToStyle(new String[]{"1", "31"}); + assertStyleForeground(Color.red); + assertStyleBackground(backgroundColor); + + testColorCodeToStyle(new String[]{"2", "32", "3"}); + assertStyleForeground(Color.green); + assertStyleBackground(backgroundColor); + } + + @Test + public void testBackgroundColors() { + java.awt.Color foregroundColor = StyleConstants.getForeground(testStringStyle()); + testColorCodeToStyle(new String[]{"1", "40"}); + assertStyleBackground(java.awt.Color.black); + assertStyleForeground(foregroundColor); + + testColorCodeToStyle(new String[]{"2", "41"}); + assertStyleBackground(java.awt.Color.red); + assertStyleForeground(foregroundColor); + + testColorCodeToStyle(new String[]{"42", "3"}); + assertStyleBackground(java.awt.Color.green); + assertStyleForeground(foregroundColor); + + testColorCodeToStyle(new String[]{"43"}); + assertStyleBackground(java.awt.Color.orange); + assertStyleForeground(foregroundColor); + } + + @Test + public void testBoldOn() { + shuffleStyle(false); + testColorCodeToStyle(new String[]{"1"}); + assertStyleBold(true); + } + + @Test + public void testBoldOff() { + shuffleStyle(true); + testColorCodeToStyle(new String[]{"2"}); + assertStyleBold(false); + } + + @Test + public void testItalicOn() { + shuffleStyle(false); + testColorCodeToStyle(new String[]{"3"}); + assertStyleItalic(true); + } + + @Test + public void testItalicOff() { + shuffleStyle(true); + testColorCodeToStyle(new String[]{"23"}); + assertStyleItalic(false); + } + + @Test + public void testUnderlineOn() { + shuffleStyle(false); + testColorCodeToStyle(new String[]{"4"}); + assertStyleUnderline(true); + } + + @Test + public void testUnderlineOff() { + shuffleStyle(false); + testColorCodeToStyle(new String[]{"24"}); + assertStyleUnderline(false); + } + + @Test + public void testStrikeOn() { + shuffleStyle(false); + testColorCodeToStyle(new String[]{"9"}); + assertStyleStrikeThrough(true); + } + + @Test + public void testStrikeOff() { + shuffleStyle(false); + testColorCodeToStyle(new String[]{"29"}); + assertStyleStrikeThrough(false); + } + + @Test + public void testExceptionOnInvalidCode() { + assertThrows(IllegalStateException.class, () -> { + testColorCodeToStyle(new String[]{"999"}); + }); + } + + @Test + public void testExceptionOnExtendedColors() { + assertThrows(IllegalStateException.class, () -> { + testColorCodeToStyle(new String[]{"0","38"}); + }); + assertThrows(IllegalStateException.class, () -> { + testColorCodeToStyle(new String[]{"1", "48"}); + }); + } + + @Test + public void testConvert() { + StyledDocument document = new DefaultStyledDocument(); + try { + //todo add more testcases + String[][] testCases = { + {"\033[mreset_jansi\033[m", "reset_jansi"}, // reset like jansi it sends + {"\033[0mreset_standard\033[0m", "reset_standard"}, // Ansi default reset + {"normal_start\033[1;32mboldGreen\033[0mnormal_end", "normal_startboldGreennormal_end"}, + {"Device 1.1.32 reported \033[92m1\033[m second(s) for restarting", "Device 1.1.32 reported 1 second(s) for restarting"} + }; + + for (String[] underTest : testCases) { + document.remove(0, document.getLength()); + ConColorsToStyledDoc.Convert(underTest[0], document); + //todo check also the style and not only the text + assertEquals(underTest[1], document.getText(0, document.getLength())); + } + } catch (BadLocationException e) { + throw new RuntimeException(e); + } + } +} From eb13c3eb178df21cff40c55c16ff63b99a4d993c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 3 Aug 2024 14:30:36 +0200 Subject: [PATCH 026/359] [Updater] Enclose hex filename with apostrophes --- .../source/src/main/java/org/selfbus/updater/Updater.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 749d2508..e2a5ab29 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -198,7 +198,7 @@ public void run() { if (!hexFileName.isEmpty()) { // check if the firmware file exists if (!Utils.fileExists(hexFileName)) { - logger.error(ansi().fg(RED).a("File {} does not exist!").reset().toString(), cliOptions.fileName()); + logger.error(ansi().fg(RED).a("File '{}' does not exist!").reset().toString(), cliOptions.fileName()); throw new UpdaterException("Selfbus update failed."); } // Load Firmware hex file @@ -368,7 +368,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd if (cliOptions.device() != null) { deviceInfo = cliOptions.device().toString(); } - logger.info("Finished programming device {} with {}", ansi().fgBright(YELLOW).a(deviceInfo).reset().toString(), + logger.info("Finished programming device {} with '{}'", ansi().fgBright(YELLOW).a(deviceInfo).reset().toString(), ansi().fgBright(YELLOW).a(shortenPath(cliOptions.fileName(), 1)).reset().toString()); logger.info(ansi().bg(GREEN).fg(BLACK).a("Firmware Update done, Restarting device now...").reset().toString()); dm.restartProgrammingDevice(); From cb775ed1f4054d3f3f59983b93c7743b4aecef96 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 3 Aug 2024 14:31:44 +0200 Subject: [PATCH 027/359] [Updater] Use monospace font on all platforms `Courier New` did only work on Windows. --- .../source/src/main/java/org/selfbus/updater/gui/GuiMain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index b6cea70b..c26b7eec 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -391,7 +391,7 @@ public void startUpdaterGui() { this.setContentPane(this.panelMain); this.setTitle(ToolInfo.getToolAndVersion()); this.setSize(1000, 800); - this.jLoggingPane.setFont(new Font("Courier New", PLAIN, 12)); + this.jLoggingPane.setFont(new Font(Font.MONOSPACED, PLAIN, 12)); this.jLoggingPane.setBackground(DefaultBackgroundColor); this.jLoggingPane.setForeground(DefaultForegroundColor); this.setVisible(true); From 5551b227ebae129f527246545bed6a14f5449cf5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 5 Aug 2024 16:49:51 +0200 Subject: [PATCH 028/359] [Updater] Fix double `//` in `createCacheFileName(..)` --- .../source/src/main/java/org/selfbus/updater/FlashDiffMode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 2e670e2b..0fb46ffe 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -42,7 +42,7 @@ public final class FlashDiffMode { private FlashDiffMode() {} public static String createCacheFileName(long startAddress, long length, long crc32) { - return String.format("%s%s%s%s0x%s%s%s%s0x%s%s", hexCacheDirectory, File.separator, IMAGE_IDENTIFIER, FILE_NAME_SEP, + return String.format("%s%s%s0x%s%s%s%s0x%s%s", hexCacheDirectory, IMAGE_IDENTIFIER, FILE_NAME_SEP, Long.toHexString(startAddress), FILE_NAME_SEP, length, FILE_NAME_SEP, Integer.toHexString((int)crc32), FILE_EXTENSION); } From 79c2c07ded6da0e8d88f76dfa9203b88e4dcb81e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 04:49:30 +0200 Subject: [PATCH 029/359] [Updater] Add icon to main frame --- .../java/org/selfbus/updater/gui/GuiMain.java | 11 ++++++++++- .../src/main/resources/selfbus_logo_16x16.png | Bin 0 -> 413 bytes 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 firmware_updater/updater/source/src/main/resources/selfbus_logo_16x16.png diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index c26b7eec..5390047a 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -16,6 +16,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.imageio.ImageIO; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.plaf.FontUIResource; @@ -94,7 +95,6 @@ public class GuiMain extends JFrame { private JScrollPane mainScrollPane; private JButton reloadGatewaysButton; - private CliOptions cliOptions; private Thread updaterThread; public static GuiMain guiMainInstance; @@ -467,6 +467,15 @@ private void createUIComponents() { comboBoxMedium = new JComboBox(); comboBoxScenario = new JComboBox(); comboBoxKnxTelegramPriority = new JComboBox(); + + String logoResourceName = "/selfbus_logo_16x16.png"; + try { + Image frameIcon = ImageIO.read(Objects.requireNonNull(this.getClass().getResource(logoResourceName))); + this.setIconImage(frameIcon); + } + catch (IOException e) { + logger.debug("Could not load '{}' {}", logoResourceName, e.toString()); + } } public static class ComboItem { diff --git a/firmware_updater/updater/source/src/main/resources/selfbus_logo_16x16.png b/firmware_updater/updater/source/src/main/resources/selfbus_logo_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..e379a63bb32aec4c9b7a1864cf31cc7d02d251af GIT binary patch literal 413 zcmV;O0b>4%P)Llb#`&n*ZBbPs`T-y!LbSPDTQ}F?lVdJKPlmO+6(Y0UR;yM&OQlM&C=q)q z@YSJVA|fd@eQ7-!#aivFT+Ss^m12=`-mc$NASorHrt2#Q`>%<_%yMup97e;Ch*+;T z(=Y%)m`sYI2#c!fJKNKNz;i72a(kD1O0Hd;FK%v(Oicn~t@*0gJLd7EE~CQf_XYQM zGxrbK_#+eIJ)e&s9{D2?P1oD@kxsAAK0jx+S>bBIGaCxMWwNDox-pWZ?Dl8CY8`Yq zJ_-dm9oGm^VT`f1n{)sG4P$^Y0RSLGr+ENf1pv;K|9HM{riPlk4IdLb00000NkvXX Hu0mjf3gf8# literal 0 HcmV?d00001 From 07b278ee9084a5ecf4e58df65bb2d8f695360b7c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 04:51:12 +0200 Subject: [PATCH 030/359] [Updater] Delete not used import --- .../source/src/main/java/org/selfbus/updater/CliOptions.java | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 98463bc0..a379e069 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -29,7 +29,6 @@ import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; -import java.util.Locale; /** * Parses command line interface (cli) options for the application From fea4d88326246bb506eabd95ada6518e610acdf4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 04:51:49 +0200 Subject: [PATCH 031/359] [Updater] Fix space at end --- .../updater/source/src/main/resources/GuiTranslation.properties | 2 +- .../source/src/main/resources/GuiTranslation_en.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties index a6095dc0..de2feb82 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties @@ -41,7 +41,7 @@ mode into the bootloader KnxGatewayConnectionSettings=KNX gateway connection settings KnxBusSettings=KNX bus settings UpdaterSettings=Updater settings -advancedSettings=Advanced settings +advancedSettings=Advanced settings messagePriority=KNX telegram priority knxSecureUser=KNX Secure User knxSecureUserHint=KNX IP Secure tunneling user identifier
\ diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties index 12264956..d93661fd 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties @@ -42,7 +42,7 @@ mode into the bootloader UpdaterSettings=Updater settings KnxBusSettings=KNX bus settings KnxGatewayConnectionSettings=KNX gateway connection settings -advancedSettings=Advanced settings +advancedSettings=Advanced settings knxSecureUserPwd=User password messagePriority=KNX telegram priority knxSecureUserHint=KNX IP Secure tunneling user identifier
\ From d2c37e97ac9af5154b15b757347c0b4b51a950cc Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:05:26 +0200 Subject: [PATCH 032/359] [Updater] Fix diff-header size calculation With `FLAG_LONG` set, the header size is 5 bytes - 1 command byte - 2 length bytes - 3 address bytes Without `FLAG_LONG` the header size is 4 bytes - 1 command byte - 1 length byte - 3 address bytes --- .../src/main/java/org/selfbus/updater/FlashDiffMode.java | 3 +-- .../java/org/selfbus/updater/tests/flashdiff/FlashDiff.java | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 0fb46ffe..990421a5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -103,8 +103,7 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start differ.generateDiff(img1, img2, (outputDiffStream, crc32) -> { // process compressed page - //TODO check why 5 bytes are added to size in FlashDiff.java / generateDiff(...) - logger.info("Sending new firmware ({} diff bytes)", (outputDiffStream.size() - 5)); + logger.info("Sending new firmware ({} diff bytes)", (outputDiffStream.size())); byte[] buf = new byte[MAX_PAYLOAD]; int i = 0; while (i < outputDiffStream.size()) { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java index a1aac93d..7eb24607 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java @@ -155,7 +155,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash size += possiblyFinishRawBuffer(rawBuffer, outputDiffStream); logger.trace("{} bestResult={}", String.format("%08x ", i), bestResult); i += bestResult.length; - size += 5; + size += 1; byte cmdByte = (byte)CMD_COPY; if (bestResult.length <= MAX_LENGTH_SHORT) { cmdByte = (byte)(cmdByte | FLAG_SHORT | (bestResult.length & 0b111111)); // command + 6 bits of the length @@ -166,6 +166,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash debug("@ b=%02X i=%d CMD_COPY FLAG_LONG", (cmdByte & 0xff), i); byte lengthLowByte = (byte)(bestResult.length & 0xff); // 8 low bits of the length outputDiffStream.add(cmdByte); + size += 1; outputDiffStream.add(lengthLowByte); } // 3 bytes are enough to address ROM or RAM buffer, the highest bit indicates ROM or RAM source @@ -185,6 +186,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash } debug("%02X ", (srcData[bestResult.offset + k] & 0xff)); } + size += 3; addr3 = (byte)(addr3 | addrFromFlag); outputDiffStream.add(addr3); outputDiffStream.add(addr2); From b6c7f23ee33fd949428a65a24b7ad6db7160b00c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:08:46 +0200 Subject: [PATCH 033/359] [Updater] Dont restart device then diff-mode errors out. It is very likely that the application of the device will be bricked. So if we leave it in bootloader mode, we have the option to to flash in full mode again. --- .../source/src/main/java/org/selfbus/updater/FlashDiffMode.java | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 990421a5..74b008d5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -129,7 +129,6 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start logger.info("Program device next page diff, crc32 0x{}", String.format("%08X", crc32)); result.set(dm.sendWithRetry(UPDCommand.PROGRAM_DECOMPRESSED_DATA, progPars, dm.getMaxUpdCommandRetry())); if (UPDProtocol.checkResult(result.get().data()) != UDPResult.IAP_SUCCESS.id) { - dm.restartProgrammingDevice(); throw new UpdaterException("Selfbus update failed."); } }); From 5c3b10ec2be42c6848a989090c9e8a95b960522c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:19:50 +0200 Subject: [PATCH 034/359] [Updater] Return value of `restartDeviceToBootloader` was never used --- .../main/java/org/selfbus/updater/DeviceManagement.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index 38eabc39..db8ff9b3 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -81,11 +81,10 @@ private void waitRestartTime(int restartTimeSeconds) { /** * Restarts the device running in normal into the Bootloader mode - * @param device - * the IndividualAddress of the device to restart - * @return true if successful, otherwise false + * + * @param device the IndividualAddress of the device to restart */ - public boolean restartDeviceToBootloader(IndividualAddress device) + public void restartDeviceToBootloader(IndividualAddress device) throws KNXLinkClosedException { Destination dest = this.mc.createDestination(device, true, false, false); int restartProcessTime = Mcu.DEFAULT_RESTART_TIME_SECONDS; @@ -95,7 +94,6 @@ public boolean restartDeviceToBootloader(IndividualAddress device) logger.info("Device {} reported {} second(s) for restarting", device, ansi().fgBright(GREEN).a(restartProcessTime).reset().toString()); waitRestartTime(restartProcessTime); System.out.println(); - return true; } catch (final KNXException | InterruptedException e) { logger.info(ansi().fgBright(RED).a("Restart state of device {} unknown. {}").reset().toString(), device, e.getMessage()); logger.debug("KNXException ", e); @@ -104,7 +102,6 @@ public boolean restartDeviceToBootloader(IndividualAddress device) } finally { dest.close(); } - return false; } public byte[] requestUIDFromDevice() From 0686cd8fc0251aca71b30e450a47d1473ec7843b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:24:19 +0200 Subject: [PATCH 035/359] [Updater] Use try-with-resources --- .../main/java/org/selfbus/updater/DeviceManagement.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index db8ff9b3..92a4b045 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -86,10 +86,9 @@ private void waitRestartTime(int restartTimeSeconds) { */ public void restartDeviceToBootloader(IndividualAddress device) throws KNXLinkClosedException { - Destination dest = this.mc.createDestination(device, true, false, false); int restartProcessTime = Mcu.DEFAULT_RESTART_TIME_SECONDS; - try { - logger.info("Restarting device {} into bootloader mode using {}", device, this.link); + try (Destination dest = this.mc.createDestination(device, true, false, false)) { + logger.info("Restarting device {} into bootloader", device); restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); logger.info("Device {} reported {} second(s) for restarting", device, ansi().fgBright(GREEN).a(restartProcessTime).reset().toString()); waitRestartTime(restartProcessTime); @@ -99,8 +98,6 @@ public void restartDeviceToBootloader(IndividualAddress device) logger.debug("KNXException ", e); logger.info("Waiting {} seconds for device {} to restart", ansi().fgBright(GREEN).a(restartProcessTime).reset().toString(), device); waitRestartTime(restartProcessTime); - } finally { - dest.close(); } } From a8d8ec73f7d2b48627e43fe297c94f15f6329d59 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:25:33 +0200 Subject: [PATCH 036/359] [Updater] Move new line to `waitRestartTime` --- .../src/main/java/org/selfbus/updater/DeviceManagement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index 92a4b045..d94c1d5d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -73,6 +73,7 @@ private void waitRestartTime(int restartTimeSeconds) { System.out.printf(ansi().fgBright(GREEN).a(Utils.PROGRESS_MARKER).reset().toString()); restartTimeSeconds--; } + System.out.println(); } catch (final InterruptedException e) { logger.error("InterruptedException ", e); @@ -92,7 +93,6 @@ public void restartDeviceToBootloader(IndividualAddress device) restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); logger.info("Device {} reported {} second(s) for restarting", device, ansi().fgBright(GREEN).a(restartProcessTime).reset().toString()); waitRestartTime(restartProcessTime); - System.out.println(); } catch (final KNXException | InterruptedException e) { logger.info(ansi().fgBright(RED).a("Restart state of device {} unknown. {}").reset().toString(), device, e.getMessage()); logger.debug("KNXException ", e); From 1a199d2c60b184a8a599be0da2e163545da7587a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:34:05 +0200 Subject: [PATCH 037/359] [Updater] Reformat and move some `info` logging to `debug` --- .../org/selfbus/updater/DeviceManagement.java | 21 +++++++------- .../org/selfbus/updater/FlashDiffMode.java | 5 ++-- .../java/org/selfbus/updater/SBKNXLink.java | 10 +++---- .../java/org/selfbus/updater/Updater.java | 28 +++++++++---------- 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index d94c1d5d..da334426 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -103,7 +103,7 @@ public void restartDeviceToBootloader(IndividualAddress device) public byte[] requestUIDFromDevice() throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { - logger.info("\nRequesting UID from {}...", progDestination.getAddress()); + logger.info("Requesting UID from {}", progDestination.getAddress()); byte[] result = sendWithRetry(UPDCommand.REQUEST_UID, new byte[0], getMaxUpdCommandRetry()).data(); if (result[COMMAND_POSITION] != UPDCommand.RESPONSE_UID.id) { UPDProtocol.checkResult(result, true); @@ -127,7 +127,7 @@ public byte[] requestUIDFromDevice() public BootloaderIdentity requestBootloaderIdentity() throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { - logger.info("Requesting Bootloader Identity..."); + logger.debug("Requesting Bootloader Identity"); byte[] telegram = new byte[2]; telegram[0] = (byte) ToolInfo.versionMajor(); @@ -150,7 +150,7 @@ public BootloaderIdentity requestBootloaderIdentity() } BootloaderIdentity bl = BootloaderIdentity.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info(" Device Bootloader: {}", ansi().fgBright(YELLOW).a(bl).reset().toString()); + logger.info("Device Bootloader: {}", ansi().fgBright(YELLOW).a(bl).reset().toString()); boolean versionsMatch = (bl.getVersionMajor() > ToolInfo.minMajorVersionBootloader()) || ((bl.getVersionMajor() == ToolInfo.minMajorVersionBootloader()) && (bl.getVersionMinor() >= ToolInfo.minMinorVersionBootloader())); @@ -166,7 +166,7 @@ public BootloaderIdentity requestBootloaderIdentity() public BootDescriptor requestBootDescriptor() throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { - logger.info("Requesting Boot Descriptor..."); + logger.debug("Requesting Boot Descriptor"); byte[] result = sendWithRetry(UPDCommand.REQUEST_BOOT_DESC, new byte[0], getMaxUpdCommandRetry()).data(); if (result[COMMAND_POSITION] != UPDCommand.RESPONSE_BOOT_DESC.id) { UPDProtocol.checkResult(result); @@ -176,7 +176,7 @@ public BootDescriptor requestBootDescriptor() DATA_POSITION, result[DATA_POSITION])); } BootDescriptor bootDescriptor = BootDescriptor.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info(" Current firmware: {}", bootDescriptor); + logger.info("Current firmware: {}", bootDescriptor); return bootDescriptor; } @@ -192,7 +192,7 @@ public String requestAppVersionString() public void unlockDeviceWithUID(byte[] uid) throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { - logger.info("Unlocking device {} with UID {}...", progDestination.getAddress(), Utils.byteArrayToHex(uid)); + logger.info("Unlocking device {} with UID {}", progDestination.getAddress(), Utils.byteArrayToHex(uid)); byte[] result = sendWithRetry(UPDCommand.UNLOCK_DEVICE, uid, getMaxUpdCommandRetry()).data(); if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS.id) { restartProgrammingDevice(); @@ -206,7 +206,7 @@ public void eraseAddressRange(long startAddress, long totalLength) byte[] telegram = new byte[8]; Utils.longToStream(telegram, 0 , startAddress); Utils.longToStream(telegram, 4 , endAddress); - logger.info(String.format("Erasing firmware address range: 0x%04X - 0x%04X...", startAddress, endAddress)); + logger.info(String.format("Erasing firmware address range: 0x%04X-0x%04X", startAddress, endAddress)); Duration oldResponseTimeout = mc.responseTimeout(); Duration newResponseTimeout = MAX_FLASH_ERASE_TIMEOUT.multipliedBy(2); if (oldResponseTimeout.compareTo(newResponseTimeout) < 0) { @@ -305,8 +305,7 @@ public ResponseResult programBootDescriptor(BootDescriptor bootDescriptor, int d byte[] programBootDescriptor = new byte[8]; Utils.longToStream(programBootDescriptor, 0, streamBootDescriptor.length); Utils.longToStream(programBootDescriptor, 4, crc32Value); - System.out.println(); - logger.info(String.format("Updating boot descriptor with crc32 0x%08X, length %d", + logger.debug(String.format("Updating boot descriptor with crc32 0x%08X, length %d", crc32Value, streamBootDescriptor.length)); ResponseResult programResult = sendWithRetry(UPDCommand.UPDATE_BOOT_DESC, programBootDescriptor, getMaxUpdCommandRetry()); if (UPDProtocol.checkResult(programResult.data()) != UDPResult.IAP_SUCCESS.id) { @@ -322,12 +321,12 @@ public ResponseResult programBootDescriptor(BootDescriptor bootDescriptor, int d public void requestBootLoaderStatistic() throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { - + logger.debug("Requesting Bootloader statistic"); byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); if (result[COMMAND_POSITION] == UPDCommand.RESPONSE_STATISTIC.id) { BootloaderStatistic blStatistic = BootloaderStatistic.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info(" Bootloader: {}", blStatistic); + logger.info("Bootloader: {}", blStatistic); } else { logger.warn(ansi().fg(RED).a(" {}").reset().toString(), diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 74b008d5..e39fd0a5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -60,12 +60,11 @@ public static boolean setupDifferentialMode(BootDescriptor bootDescriptor) { //TODO check that "Current App Version String" and "File App Version String" represent the same application, // if they are not the same app, we should switch to full flash mode if (Utils.fileExists(oldFileName)) { - logger.info(" Found current device firmware in cache {}", Utils.shortenPath(oldFileName, DEFAULT_DISPLAYED_PATH_DEPTH)); - logger.info(ansi().fgBright(GREEN).a(" --> switching to diff upload mode").reset().toString()); + logger.info("Found current device firmware in cache {}", Utils.shortenPath(oldFileName, DEFAULT_DISPLAYED_PATH_DEPTH)); initialized = true; } else { - logger.warn(" Current device firmware not found in cache {}", Utils.shortenPath(oldFileName, DEFAULT_DISPLAYED_PATH_DEPTH)); + logger.warn("Current device firmware not found in cache {}", Utils.shortenPath(oldFileName, DEFAULT_DISPLAYED_PATH_DEPTH)); logger.warn(ansi().fgBright(RED).a(" --> switching to FULL upload mode").reset().toString()); initialized = false; } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index 1072d90f..c2759fe3 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -33,7 +33,7 @@ public SBKNXLink() { private KNXNetworkLink createSecureTunnelingLink(InetSocketAddress local, InetSocketAddress remote, KNXMediumSettings medium) throws KNXException, InterruptedException { // KNX IP Secure TCP tunneling v2 connection - logger.info("Connect using KNX IP Secure tunneling..."); + logger.info("Connect using KNX IP Secure tunneling"); byte[] deviceAuthCode = SecureConnection.hashDeviceAuthenticationPassword(cliOptions.devicePassword().toCharArray()); byte[] userKey = SecureConnection.hashUserPassword(cliOptions.userPassword().toCharArray()); final var session = Utils.tcpConnection(local, remote).newSecureSession(cliOptions.userId(), userKey, deviceAuthCode); @@ -42,19 +42,19 @@ private KNXNetworkLink createSecureTunnelingLink(InetSocketAddress local, InetSo private KNXNetworkLink createTunnelingLinkV2(InetSocketAddress local, InetSocketAddress remote, KNXMediumSettings medium) throws KNXException, InterruptedException { - logger.info("Connect using TCP tunneling v2..."); + logger.info("Connect using TCP tunneling v2"); final var session = Utils.tcpConnection(local, remote); return KNXNetworkLinkIP.newTunnelingLink(session, medium); } private KNXNetworkLink createTunnelingLinkV1(InetSocketAddress local, InetSocketAddress remote, boolean useNat, KNXMediumSettings medium) throws KNXException, InterruptedException { - logger.info(ansi().fg(YELLOW).a("Connect using UDP tunneling v1 (nat:{})...").reset().toString(), useNat); + logger.info(ansi().fg(YELLOW).a("Connect using UDP tunneling v1 (nat:{})").reset().toString(), useNat); return KNXNetworkLinkIP.newTunnelingLink(local, remote, useNat, medium); } private KNXNetworkLink createRoutingLink(InetSocketAddress local, KNXMediumSettings medium) throws KNXException { - logger.info(ansi().fg(YELLOW).a("Connect using routing (multicast:{})...").reset().toString(), KNXnetIPRouting.DefaultMulticast); + logger.info(ansi().fg(YELLOW).a("Connect using routing (multicast:{})").reset().toString(), KNXnetIPRouting.DefaultMulticast); return KNXNetworkLinkIP.newRoutingLink(local.getAddress(), KNXnetIPRouting.DefaultMulticast, medium); } @@ -96,7 +96,7 @@ public void setCliOptions(CliOptions cliOptions) { public KNXNetworkLink openLink() throws KNXException, InterruptedException, UpdaterException { final KNXMediumSettings medium = getMedium(cliOptions.medium(), cliOptions.ownAddress()); - logger.debug("Creating KNX network link {}...", medium); + logger.debug("Creating KNX network link {}", medium); if (!cliOptions.ft12().isEmpty()) { // create FT1.2 network link return new KNXNetworkLinkFT12(cliOptions.ft12(), medium); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index e2a5ab29..548dad02 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -110,7 +110,6 @@ public static void main(final String[] args) { } catch (final Throwable t) { logger.error("parsing options ", t); } finally { - logger.info("\n\n"); logger.debug("main exit"); } } @@ -202,7 +201,7 @@ public void run() { throw new UpdaterException("Selfbus update failed."); } // Load Firmware hex file - logger.info("Loading file '{}'...", hexFileName); + logger.info("Loading file '{}'", hexFileName); newFirmware = BinImage.readFromHex(hexFileName); // Check for APP_VERSION string in new firmware if (newFirmware.getAppVersion().isEmpty()) { @@ -223,7 +222,7 @@ public void run() { DeviceManagement dm = new DeviceManagement(link, cliOptions.progDevice(), cliOptions.priority()); logger.info("KNX connection: {}", link); - logger.info("Telegram priority: {}", cliOptions.priority()); + logger.debug("Telegram priority: {}", cliOptions.priority()); //for option --device restart the device in bootloader mode if (cliOptions.device() != null) { // phys. knx address of the device in normal operation @@ -251,16 +250,16 @@ public void run() { // Request current main firmware boot descriptor from device BootDescriptor bootDescriptor = dm.requestBootDescriptor(); if (newFirmware != null) { - logger.info(" New firmware: {}", newFirmware); + logger.info("New firmware: {}", newFirmware); } - logger.info("Requesting APP_VERSION..."); + logger.debug("Requesting APP_VERSION"); String appVersion = dm.requestAppVersionString(); if (appVersion != null) { - logger.info(" Current APP_VERSION: {}", ansi().fgBright(GREEN).a(appVersion).reset().toString()); + logger.info("Current APP_VERSION: {}", ansi().fgBright(GREEN).a(appVersion).reset().toString()); } else { - logger.info(ansi().fgBright(RED).a(" failed!").reset().toString()); + logger.info("Requesting APP_VERSION {}", ansi().fgBright(RED).a(" failed!").reset().toString()); } // From here on we need a valid firmware @@ -283,7 +282,7 @@ public void run() { BinImage imageCache = BinImage.copyFromArray(newFirmware.getBinData(), newFirmware.startAddress()); imageCache.writeToBinFile(cacheFileName); - logger.info(" File APP_VERSION : {}", ansi().fgBright(GREEN).a(newFirmware.getAppVersion()).reset().toString()); + logger.info("File APP_VERSION : {}", ansi().fgBright(GREEN).a(newFirmware.getAppVersion()).reset().toString()); // Check if FW image has correct offset for MCUs bootloader size if (newFirmware.startAddress() < bootLoaderIdentity.getApplicationFirstAddress()) { @@ -292,10 +291,10 @@ public void run() { throw new UpdaterException("Firmware offset not correct!"); } else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAddress()) { - logger.info(ansi().fgBright(GREEN).a(" Firmware starts directly beyond bootloader.").reset().toString()); + logger.debug(ansi().fgBright(GREEN).a(" Firmware starts directly beyond bootloader.").reset().toString()); } else { - logger.info(ansi().fgBright(YELLOW).a(" Info: There are {} bytes of unused flash between bootloader and firmware.").reset().toString(), + logger.debug(ansi().fgBright(YELLOW).a(" Info: There are {} bytes of unused flash between bootloader and firmware.").reset().toString(), newFirmware.startAddress() - bootLoaderIdentity.getApplicationFirstAddress()); } @@ -333,10 +332,9 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd else { resultTotal = FlashFullMode.doFullFlash(dm, newFirmware, cliOptions.delay(), !cliOptions.eraseFullFlash(), cliOptions.logStatistics()); } - logger.info("Requesting Bootloader statistic..."); dm.requestBootLoaderStatistic(); - String updaterStatisticMsg = " Updater: "; + String updaterStatisticMsg = "Updater: "; String colored; if (resultTotal.dropCount() > BootloaderStatistic.THRESHOLD_DISCONNECT) { colored = ansi().fgBright(YELLOW).toString(); @@ -362,7 +360,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd newFirmware.endAddress(), (int) newFirmware.crc32(), newFirmware.startAddress() + newFirmware.getAppVersionAddress()); - logger.info("Preparing boot descriptor with {}", newBootDescriptor); + logger.info("Updating boot descriptor with {}", newBootDescriptor); dm.programBootDescriptor(newBootDescriptor, cliOptions.delay()); String deviceInfo = cliOptions.progDevice().toString(); if (cliOptions.device() != null) { @@ -370,11 +368,11 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd } logger.info("Finished programming device {} with '{}'", ansi().fgBright(YELLOW).a(deviceInfo).reset().toString(), ansi().fgBright(YELLOW).a(shortenPath(cliOptions.fileName(), 1)).reset().toString()); - logger.info(ansi().bg(GREEN).fg(BLACK).a("Firmware Update done, Restarting device now...").reset().toString()); + logger.info(ansi().bg(GREEN).fg(BLACK).a("Firmware Update done, Restarting device").reset().toString()); dm.restartProgrammingDevice(); if (newFirmware.getAppVersion().contains(BootloaderUpdater.BOOTLOADER_UPDATER_ID_STRING)) { - logger.info(ansi().bg(GREEN).fg(BLACK).a("Wait {} second(s) for Bootloader Updater to finish its job...").reset().toString(), + logger.info(ansi().bg(GREEN).fg(BLACK).a("Wait {} second(s) for Bootloader Updater to finish its job").reset().toString(), String.format("%.2f", BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS / 1000.0f)); Thread.sleep(BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS); } From 9f6862ef9f74917c8122917c12ea1587de0262b6 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:38:16 +0200 Subject: [PATCH 038/359] [Updater] Cleanup/formatting --- .../src/main/java/org/selfbus/updater/DeviceManagement.java | 2 +- .../source/src/main/java/org/selfbus/updater/FlashDiffMode.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index da334426..b61ebab3 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -258,7 +258,7 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay) txBuffer = new byte[maxPayload]; } else { - txBuffer = new byte[data.length + updSendDataOffset- nIndex]; + txBuffer = new byte[data.length + updSendDataOffset - nIndex]; } if (protocolVersion == UDPProtocolVersion.UDP_V0) { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index e39fd0a5..86c2c950 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -81,7 +81,6 @@ public static boolean isInitialized() { } // Differential update routine - @SuppressWarnings("unused") public static ResponseResult doDifferentialFlash(DeviceManagement dm, long startAddress, byte[] binData) throws KNXDisconnectException, KNXTimeoutException, KNXRemoteException, KNXLinkClosedException, InterruptedException, UpdaterException { ///\todo add connection reset and sending again on failure, like in full flash mode @@ -93,7 +92,6 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start } File oldImageCacheFile = new File(oldFileName); - FlashDiff differ = new FlashDiff(); BinImage img2 = BinImage.copyFromArray(binData, startAddress); BinImage img1 = BinImage.readFromBin(oldImageCacheFile.getAbsolutePath(), bootDsc.startAddress()); From 540fad12fbcfce0bbbc965a4b203f69096936915 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:39:36 +0200 Subject: [PATCH 039/359] [Updater] Use `Math.max` and ensure non null objects --- .../java/org/selfbus/updater/tests/FlashDiffTests.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java index f4eddb50..7c641f25 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java @@ -9,13 +9,14 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; public class FlashDiffTests { private void performTest(BinImage img1, BinImage img2) { int pages1 = (int) Math.ceil(img2.getBinData().length / (double) FlashPage.PAGE_SIZE); int pages2 = (int) Math.ceil(img2.getBinData().length / (double)FlashPage.PAGE_SIZE); - int pagesMax = pages1 > pages2 ? pages1 : pages2; + int pagesMax = Math.max(pages1, pages2); BinImage rom = new BinImage(img1, pagesMax * FlashPage.PAGE_SIZE); BinImage img2wholePages = new BinImage(img2, pagesMax * FlashPage.PAGE_SIZE); FlashDiff differ = new FlashDiff(); @@ -45,8 +46,8 @@ private void performTest(BinImage img1, BinImage img2) { public void testDiff() throws URISyntaxException { // test of upgrade from old version to newer (longer) ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - URI uri1 = contextClassLoader.getResource("updater.ino.slto.v1.hex").toURI(); - URI uri2 = contextClassLoader.getResource("updater.ino.slto.v2.hex").toURI(); + URI uri1 = Objects.requireNonNull(contextClassLoader.getResource("updater.ino.slto.v1.hex")).toURI(); + URI uri2 = Objects.requireNonNull(contextClassLoader.getResource("updater.ino.slto.v2.hex")).toURI(); BinImage img1 = BinImage.readFromHex(uri1.getPath()); BinImage img2 = BinImage.readFromHex(uri2.getPath()); performTest(img1, img2); @@ -55,7 +56,7 @@ public void testDiff() throws URISyntaxException { @Test public void testDiff2() throws URISyntaxException, UpdaterException { // test of new firmware into empty MCU - URI uri2 = Thread.currentThread().getContextClassLoader().getResource("updater.ino.slto.v2.hex").toURI(); + URI uri2 = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("updater.ino.slto.v2.hex")).toURI(); BinImage img2 = BinImage.readFromHex(uri2.getPath()); BinImage img1 = BinImage.dummyFilled(img2.getBinData().length, 0xff); performTest(img1, img2); From 0cf6df77933f706bfb528bce4fcccc47b8466bc7 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:41:55 +0200 Subject: [PATCH 040/359] [Updater] Use getter methods --- .../main/java/org/selfbus/updater/DeviceManagement.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index b61ebab3..cd4b3338 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -254,14 +254,14 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay) while (nIndex < data.length) { byte[] txBuffer; - if ((data.length + updSendDataOffset - nIndex) >= maxPayload) { - txBuffer = new byte[maxPayload]; + if ((data.length + updSendDataOffset - nIndex) >= getMaxPayload()) { + txBuffer = new byte[getMaxPayload()]; } else { txBuffer = new byte[data.length + updSendDataOffset - nIndex]; } - if (protocolVersion == UDPProtocolVersion.UDP_V0) { + if (getProtocolVersion() == UDPProtocolVersion.UDP_V0) { txBuffer[0] = (byte) nIndex; // First byte contains mcu's ramBuffer start position to copy to } System.arraycopy(data, nIndex, txBuffer, updSendDataOffset, txBuffer.length - updSendDataOffset); @@ -418,7 +418,7 @@ public int getBlockSize() { } public boolean setBlockSize(int blockSize) { - if (this.protocolVersion == UDPProtocolVersion.UDP_V0) { + if (getProtocolVersion() == UDPProtocolVersion.UDP_V0) { return false; } this.blockSize = blockSize; From 5ca2549a6866265735671d9bff0988f6b086aa5f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:47:28 +0200 Subject: [PATCH 041/359] [Updater] Run `generateDiff` to get number of affected bytes --- .../src/main/java/org/selfbus/updater/FlashDiffMode.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 86c2c950..f15aabe6 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -92,11 +92,16 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start } File oldImageCacheFile = new File(oldFileName); - FlashDiff differ = new FlashDiff(); BinImage img2 = BinImage.copyFromArray(binData, startAddress); BinImage img1 = BinImage.readFromBin(oldImageCacheFile.getAbsolutePath(), bootDsc.startAddress()); + //Execute lambda once to get total byte count to transfer + FlashDiff differ = new FlashDiff(); + differ.generateDiff(img1, img2, (outputDiffStream, crc32) -> {}); + long bytesToFlash = differ.getTotalBytesTransferred(); + logger.info("Start sending differential data ({} bytes)", bytesToFlash); AtomicReference result = new AtomicReference<>(); + differ = new FlashDiff(); differ.generateDiff(img1, img2, (outputDiffStream, crc32) -> { // process compressed page From 5a06384784fd8cd097e9c4f4f344e07c132d3b63 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 05:58:33 +0200 Subject: [PATCH 042/359] [Updater] Simplify `if` for not very useful logmessage "done" --- .../main/java/org/selfbus/updater/upd/UPDProtocol.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java index 748a2301..8ea8ab4c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java @@ -39,15 +39,7 @@ public static long checkResult(byte[] result, boolean verbose) { logger.error(ansi().fgBright(RED).a("{} resultCode=0x{}").reset().toString(), udpResult, String.format("%02X", udpResult.id)); } else { if (verbose) { - ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - if (root.getLevel() == Level.TRACE) { - // only to display the message on the console - logger.debug("done ({})", ansi().fgBright(GREEN).a(udpResult.id).reset().toString()); - } - else { - logger.trace("done ({})", ansi().fgBright(GREEN).a(udpResult.id).reset().toString()); - } - + logger.trace("done ({})", ansi().fgBright(GREEN).a(udpResult.id).reset().toString()); } else { System.out.printf(ansi().fgBright(GREEN).a(Utils.PROGRESS_MARKER).reset().toString()); // Success in green logger.debug(Utils.PROGRESS_MARKER); From 004e7bd1d21c74678ec9fd01b90004fc5e5835d5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 06:04:24 +0200 Subject: [PATCH 043/359] [Updater] Use `jansi` for console width --- .../main/java/org/selfbus/updater/FlashFullMode.java | 2 +- .../src/main/java/org/selfbus/updater/Utils.java | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java index 8e2e6e57..f101cd1b 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java @@ -79,7 +79,7 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa String progressInfo = ansi().fgBright(GREEN).a(String.format("%s%% %s%6.2f B/s", percentageDone, col, bytesPerSecond)).reset().toString(); // Check if printed Utils.PROGRESS_MARKER and progressInfo would exceed console width int progressMarkerLength = dm.getBlockSize()/(dm.getMaxPayload() * Utils.PROGRESS_MARKER.length()); - if ((progressMarkerLength + progressInfo.length()) > Utils.CONSOLE_WIDTH) { + if ((progressMarkerLength + progressInfo.length()) > Utils.getConsoleWidth()) { System.out.println(); } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java index e390904c..e5585fc6 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java @@ -1,5 +1,6 @@ package org.selfbus.updater; +import org.fusesource.jansi.AnsiConsole; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tuwien.auto.calimero.KNXException; @@ -20,7 +21,7 @@ public class Utils { private static final Logger logger = LoggerFactory.getLogger(Utils.class.getName()); public static final String PROGRESS_MARKER = "."; //!< symbol to print progress to console - public static final int CONSOLE_WIDTH = 80; + private static final int FALLBACK_CONSOLE_WIDTH = 80; public static long streamToLong(byte[] stream, int offset) { return ((stream[offset] & 0xFF)) | @@ -177,4 +178,12 @@ private static void closeTcpConnections() { c.close(); } } + + public static int getConsoleWidth() { + int jansiConsoleWidth = AnsiConsole.getTerminalWidth(); + if (jansiConsoleWidth != 0) { + return jansiConsoleWidth; + } + return FALLBACK_CONSOLE_WIDTH; + } } From ff80c3afa3dffdfb9201604dc92bbff1a2bc96c7 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 06:13:31 +0200 Subject: [PATCH 044/359] =?UTF-8?q?[Updater]=20It=C2=B4s=20256=20color=20n?= =?UTF-8?q?ot=20extended?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/selfbus/updater/gui/ConColorsToStyledDoc.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java index 84f53879..5ad56fc3 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -130,7 +130,7 @@ private static Style colorCodeToStyle(String [] colorCodes) { StyleConstants.setForeground(stringStyle, java.awt.Color.white); break; case 38: - throw new IllegalStateException("Extended foreground colors not implemented"); + throw new IllegalStateException("256 color foreground not implemented"); case 39: // Foreground color default StyleConstants.setForeground(stringStyle, DefaultForegroundColor); break; @@ -164,7 +164,7 @@ private static Style colorCodeToStyle(String [] colorCodes) { StyleConstants.setBackground(stringStyle, java.awt.Color.white); break; case 48: - throw new IllegalStateException("Extended background colors not implemented"); + throw new IllegalStateException("256 color background not implemented"); case 49: // Background color default StyleConstants.setBackground(stringStyle, DefaultBackgroundColor); break; From 603347a3709289cd6cf607cdb14b63fa513ff1df Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 06:18:05 +0200 Subject: [PATCH 045/359] [Updater] New class `SpinningCursor` to simulate a rotating cursor --- .../org/selfbus/updater/SpinningCursor.java | 22 +++++++++ .../selfbus/updater/SpinningCursorTest.java | 46 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 firmware_updater/updater/source/src/main/java/org/selfbus/updater/SpinningCursor.java create mode 100644 firmware_updater/updater/source/src/test/java/org/selfbus/updater/SpinningCursorTest.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SpinningCursor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SpinningCursor.java new file mode 100644 index 00000000..41702c3f --- /dev/null +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SpinningCursor.java @@ -0,0 +1,22 @@ +package org.selfbus.updater; + +public final class SpinningCursor { + @SuppressWarnings("unused") + private SpinningCursor() {} // avoids instance creation + + private static final char[] cursorPool = {'|', '/', '-', '\\'}; + private static int index = 0; + + public static void reset() { + index = 0; + } + public static char getNext() { + if (index > (cursorPool.length - 1)) { + reset(); + } + char nextCursor = cursorPool[index]; + index++; + + return nextCursor; + } +} diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/SpinningCursorTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/SpinningCursorTest.java new file mode 100644 index 00000000..8cba5f2c --- /dev/null +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/SpinningCursorTest.java @@ -0,0 +1,46 @@ +package org.selfbus.updater; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class SpinningCursorTest { + + @Test + public void testInitialCursorState() { + // Ensure the initial state is the first character in the cursor array + SpinningCursor.reset(); + assertEquals('|', SpinningCursor.getNext()); + } + + @Test + public void testCursorRotation() { + SpinningCursor.reset(); + // Simulate rotating through the cursor characters + assertEquals('|', SpinningCursor.getNext()); + assertEquals('/', SpinningCursor.getNext()); + assertEquals('-', SpinningCursor.getNext()); + assertEquals('\\', SpinningCursor.getNext()); + // After the last character, it should reset and start over + assertEquals('|', SpinningCursor.getNext()); + } + + @Test + public void testCursorReset() { + SpinningCursor.reset(); + SpinningCursor.getNext(); // Move to the first character + SpinningCursor.reset(); // Reset to initial state + assertEquals('|', SpinningCursor.getNext()); // Should still be at the beginning + } + + @Test + public void testCursorIndexWrapping() { + SpinningCursor.reset(); + // Rotate through all characters and check wrapping behavior + for (int i = 0; i < 100; i++) { + SpinningCursor.getNext(); + } + assertEquals('|', SpinningCursor.getNext()); + } +} + From 88bec0d080e6a4d18172615a2f2585be50f7def6 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 06:19:56 +0200 Subject: [PATCH 046/359] [Updater] New class `ProgressInfo` for easier connection speed logging --- .../org/selfbus/updater/ProgressInfo.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java new file mode 100644 index 00000000..03f6a2a0 --- /dev/null +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java @@ -0,0 +1,111 @@ +package org.selfbus.updater; + +import org.fusesource.jansi.Ansi; + +import static org.fusesource.jansi.Ansi.Color.GREEN; +import static org.fusesource.jansi.Ansi.Color.YELLOW; +import static org.fusesource.jansi.Ansi.ansi; + +public class ProgressInfo { + private long startTimeMs; + private long lastUpdateTimeMs; + private float minBytesPerSecond; + private float maxBytesPerSecond; + private float currentBytesPerSecond; + private long totalBytes; + private long bytesDone; + private float percentageDone; + + @SuppressWarnings("unused") + private ProgressInfo() {} + + public ProgressInfo(long totalByteCount) { + minBytesPerSecond = -1.0f; + maxBytesPerSecond = -1.0f; + currentBytesPerSecond = -1.0f; + totalBytes = totalByteCount; + bytesDone = 0; + startTimeMs = System.currentTimeMillis(); + lastUpdateTimeMs = startTimeMs;; + } + + public void update(long byteCount) { + // logging of connection speed + long duration = System.currentTimeMillis() - lastUpdateTimeMs; + currentBytesPerSecond = (float) byteCount / (duration / 1000.0f); + lastUpdateTimeMs = System.currentTimeMillis(); + + if (minBytesPerSecond < 0) { + minBytesPerSecond = currentBytesPerSecond; + } + + if (maxBytesPerSecond < 0) { + maxBytesPerSecond = currentBytesPerSecond; + } + + minBytesPerSecond = Math.min(minBytesPerSecond, currentBytesPerSecond); + maxBytesPerSecond = Math.max(maxBytesPerSecond, currentBytesPerSecond); + bytesDone += byteCount; + percentageDone = 100.0f * (bytesDone) / getTotalBytes(); + } + + private String getSpeed(boolean averageSpeed) { + float bytesPerSecond; + if (averageSpeed) { + long duration = getLastUpdateTimeMs() - getStartTimeMs(); + bytesPerSecond = (float) getBytesDone() / (duration / 1000.0f); + } + else { + bytesPerSecond = getCurrentBytesPerSecond(); + } + Ansi.Color color; + if (bytesPerSecond >= 50.0) { + color = GREEN; + } + else { + color = YELLOW; + } + return ansi().fgBright(color).a(String.format("%5.1f", bytesPerSecond)).reset().toString(); + } + + public String toString() { + return String.format("%5.1f%% %s %s %5.1f %5.1f %tM:% Date: Wed, 7 Aug 2024 06:20:45 +0200 Subject: [PATCH 047/359] [Updater] New class `ListTextAppender` for easier logging into the GUI --- .../selfbus/updater/ListTextAppenders.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 firmware_updater/updater/source/src/main/java/org/selfbus/updater/ListTextAppenders.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ListTextAppenders.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ListTextAppenders.java new file mode 100644 index 00000000..6900a81c --- /dev/null +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ListTextAppenders.java @@ -0,0 +1,66 @@ +package org.selfbus.updater; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import org.selfbus.updater.gui.TextAppender; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; + +public final class ListTextAppenders { + @SuppressWarnings("unused") + private ListTextAppenders() {} // avoids instance creation + + private static TextAppender jTextPaneAppender = null; + private static Logger logger = null; + + public static TextAppender searchAppender() { + // Get the LoggerContext from the LoggerFactory + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + + // Iterate over all loggers in the context + for (Logger iterLogger : loggerContext.getLoggerList()) { + // Get all appenders for the iterLogger + Iterator> appenderIterator = iterLogger.iteratorForAppenders(); + + while (appenderIterator.hasNext()) { + Appender appender = appenderIterator.next(); + if (appender instanceof TextAppender) { + logger = iterLogger; + jTextPaneAppender = (TextAppender) appender; + return jTextPaneAppender; + } + } + } + jTextPaneAppender = null; + return jTextPaneAppender; + } + + public static TextAppender getJTextPaneAppender() { + if (jTextPaneAppender != null) { + return jTextPaneAppender; + } + else { + return searchAppender(); + } + } + + public static void appendEvent(Level level, String logMessage) { + if (getJTextPaneAppender() == null) { + return; + } + + if (logger == null) { + return; + } + + ILoggingEvent loggingEvent = new LoggingEvent(logger.getName(), logger, + level, logMessage, null, null); + getJTextPaneAppender().doAppend(loggingEvent); + } +} + From 73935d7293732b3cffdbc96aa4a7610c817467df Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 7 Aug 2024 06:29:46 +0200 Subject: [PATCH 048/359] [Update] Optimize speed and progress visualization Instead of dots as progress, there is now a rotating cursor and continuously updated status information about the connection, both on the cli and in the GUI. --- .../java/org/selfbus/updater/AnsiCursor.java | 15 ++ .../org/selfbus/updater/DeviceManagement.java | 55 +++- .../org/selfbus/updater/FlashDiffMode.java | 11 +- .../org/selfbus/updater/FlashFullMode.java | 39 +-- .../java/org/selfbus/updater/Updater.java | 18 +- .../main/java/org/selfbus/updater/Utils.java | 11 - .../updater/gui/ConColorsToStyledDoc.java | 236 +++++++++++++++++- .../java/org/selfbus/updater/gui/GuiMain.java | 11 +- .../org/selfbus/updater/gui/TextAppender.java | 71 +++--- .../updater/tests/flashdiff/FlashDiff.java | 6 +- .../org/selfbus/updater/upd/UPDProtocol.java | 5 - .../gui/ConColorsToStyledDocTests.java | 126 ++++++++-- 12 files changed, 456 insertions(+), 148 deletions(-) create mode 100644 firmware_updater/updater/source/src/main/java/org/selfbus/updater/AnsiCursor.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/AnsiCursor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/AnsiCursor.java new file mode 100644 index 00000000..38264a25 --- /dev/null +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/AnsiCursor.java @@ -0,0 +1,15 @@ +package org.selfbus.updater; + +public final class AnsiCursor { + @SuppressWarnings("unused") + private AnsiCursor() {} // avoids instance creation + + public static String on() { + // cursor on + return "\033[?25h"; + } + public static String off() { + // cursor off + return "\033[?25l"; + } +} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index cd4b3338..2b72bbb2 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -1,5 +1,6 @@ package org.selfbus.updater; +import ch.qos.logback.classic.Level; import org.selfbus.updater.bootloader.BootDescriptor; import org.selfbus.updater.bootloader.BootloaderIdentity; import org.selfbus.updater.bootloader.BootloaderStatistic; @@ -70,7 +71,7 @@ private void waitRestartTime(int restartTimeSeconds) { try { while (restartTimeSeconds > 0) { Thread.sleep(1000); - System.out.printf(ansi().fgBright(GREEN).a(Utils.PROGRESS_MARKER).reset().toString()); + System.out.printf(ansi().fgBright(GREEN).a(".").reset().toString()); restartTimeSeconds--; } System.out.println(); @@ -247,7 +248,7 @@ public void dumpFlashRange(long startAddress, long endAddress) } } - public ResponseResult doFlash(byte[] data, int maxRetry, int delay) + public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo progressInfo) throws UpdaterException, KNXLinkClosedException, InterruptedException, KNXTimeoutException { int nIndex = 0; ResponseResult result = new ResponseResult(); @@ -269,16 +270,11 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay) ResponseResult tmp = sendWithRetry(UPDCommand.SEND_DATA, txBuffer, maxRetry); result.addCounters(tmp); - if ((tmp.dropCount() > 0) || (tmp.timeoutCount() > 0)) { - logger.warn(ansi().fg(RED).a("x").reset().toString()); - continue; - } - if (UPDProtocol.checkResult(tmp.data(), false) != UDPResult.IAP_SUCCESS.id) { restartProgrammingDevice(); throw new UpdaterException("doFlash failed."); } - + updateProgressInfo(progressInfo, txBuffer.length); nIndex += txBuffer.length - updSendDataOffset; if (delay > 0) { @@ -289,12 +285,50 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay) return result; } - public ResponseResult programBootDescriptor(BootDescriptor bootDescriptor, int delay) + public void startProgressInfo() { + logger.info(" Done Speed Avg Min Max Time"); + // We need one newLine for the gui + ListTextAppenders.appendEvent(Level.INFO, System.lineSeparator()); + } + + public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { + if (progressInfo == null) { + return; + } + + progressInfo.update(bytesDone); + // console output + String logText = String.format("%s%s%s%s", + AnsiCursor.off(), + ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset().toString(), + progressInfo, + AnsiCursor.on()); + System.out.print(logText); + + // gui JTextPane output + logText = ansi().cursorUpLine().toString() + logText; // need this CursorUp in gui + ListTextAppenders.appendEvent(Level.INFO, logText); + } + + public void finalProgressInfo(ProgressInfo progressInfo) { + if (progressInfo == null) { + return; + } + + //todo this is stupid + // We need two CursorUp in the gui, because it appends to every logMessage System.lineSeparator() + ListTextAppenders.appendEvent(Level.INFO, ansi().cursorUpLine(1).toString()); + + // Now normal logging in console and gui + logger.info("{} {}", ansi().cursorToColumn(1).toString(), progressInfo); + } + + public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) throws UpdaterException, KNXLinkClosedException, InterruptedException, KNXTimeoutException { byte[] streamBootDescriptor = bootDescriptor.toStream(); // send new boot descriptor - ResponseResult flashResult = doFlash(streamBootDescriptor, getMaxUpdCommandRetry(), delay); + ResponseResult flashResult = doFlash(streamBootDescriptor, getMaxUpdCommandRetry(), delay, null); if (flashResult.written() != streamBootDescriptor.length) { throw new UpdaterException(String.format("Sending Boot descriptor (length %d) failed. Wrote %d", streamBootDescriptor.length, flashResult.written())); } @@ -316,7 +350,6 @@ public ResponseResult programBootDescriptor(BootDescriptor bootDescriptor, int d if (delay > 0) { Thread.sleep(delay); } - return programResult; } public void requestBootLoaderStatistic() diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index f15aabe6..92836ee1 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -99,13 +99,17 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start FlashDiff differ = new FlashDiff(); differ.generateDiff(img1, img2, (outputDiffStream, crc32) -> {}); long bytesToFlash = differ.getTotalBytesTransferred(); + ProgressInfo progressInfo = new ProgressInfo(bytesToFlash); + SpinningCursor.reset(); logger.info("Start sending differential data ({} bytes)", bytesToFlash); + dm.startProgressInfo(); + AtomicReference result = new AtomicReference<>(); differ = new FlashDiff(); differ.generateDiff(img1, img2, (outputDiffStream, crc32) -> { // process compressed page - logger.info("Sending new firmware ({} diff bytes)", (outputDiffStream.size())); + logger.debug("Sending new firmware ({} diff bytes)", (outputDiffStream.size())); byte[] buf = new byte[MAX_PAYLOAD]; int i = 0; while (i < outputDiffStream.size()) { @@ -122,18 +126,19 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start dm.restartProgrammingDevice(); throw new UpdaterException("Selfbus update failed."); } + dm.updateProgressInfo(progressInfo, txBuf.length); } // diff data of a single page transmitted // flash the page byte[] progPars = new byte[4]; Utils.longToStream(progPars, 0, (int) crc32); - System.out.println(); - logger.info("Program device next page diff, crc32 0x{}", String.format("%08X", crc32)); + logger.debug("Program device next page diff, crc32 0x{}", String.format("%08X", crc32)); result.set(dm.sendWithRetry(UPDCommand.PROGRAM_DECOMPRESSED_DATA, progPars, dm.getMaxUpdCommandRetry())); if (UPDProtocol.checkResult(result.get().data()) != UDPResult.IAP_SUCCESS.id) { throw new UpdaterException("Selfbus update failed."); } }); + dm.finalProgressInfo(progressInfo); result.get().setWritten(differ.getTotalBytesTransferred()); return result.get(); } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java index f101cd1b..11d380de 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java @@ -46,9 +46,13 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa logMessage += String.format(" with telegram delay of %dms", dataSendDelay); } logger.info(logMessage); + dm.startProgressInfo(); int nRead = 0; boolean repeat = false; + SpinningCursor.reset(); + + ProgressInfo progressInfo = new ProgressInfo(totalLength); while (fis.available() > 0) { if (!repeat) { nRead = fis.read(buffer); // Read up to size of buffer @@ -62,27 +66,9 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa System.arraycopy(buffer, 0, txBuffer, 0, nRead); // send data to the bootloader - long flashTimeStart = System.currentTimeMillis(); // time this run started - resultSendData = dm.doFlash(txBuffer, dm.getMaxUpdCommandRetry(), dataSendDelay); + resultSendData = dm.doFlash(txBuffer, dm.getMaxUpdCommandRetry(), dataSendDelay, progressInfo); resultTotal.addCounters(resultSendData); // keep track of static data - // logging of connection speed - long flashTimeDuration = System.currentTimeMillis() - flashTimeStart; - float bytesPerSecond = (float) resultSendData.written() / (flashTimeDuration / 1000f); - String col; - if (bytesPerSecond >= 50.0) { - col = ansi().fgBright(GREEN).toString(); - } else { - col = ansi().fgBright(YELLOW).toString(); - } - String percentageDone = String.format("%5.1f", (float) 100 * (resultTotal.written()) / totalLength); - String progressInfo = ansi().fgBright(GREEN).a(String.format("%s%% %s%6.2f B/s", percentageDone, col, bytesPerSecond)).reset().toString(); - // Check if printed Utils.PROGRESS_MARKER and progressInfo would exceed console width - int progressMarkerLength = dm.getBlockSize()/(dm.getMaxPayload() * Utils.PROGRESS_MARKER.length()); - if ((progressMarkerLength + progressInfo.length()) > Utils.getConsoleWidth()) { - System.out.println(); - } - // flash the previously sent data int crc32 = Utils.crc32Value(txBuffer); byte[] progPars = new byte[2 + 4 + 4]; @@ -90,20 +76,19 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa Utils.longToStream(progPars, 2, progAddress); Utils.longToStream(progPars, 6, crc32); - String programFlashInfo = String.format("%s 0x%04X-0x%04X", progressInfo, progAddress, progAddress + txBuffer.length - 1); - if (txBuffer.length != dm.getBlockSize()) - { - programFlashInfo = String.format("%s (%d bytes)", programFlashInfo, txBuffer.length); + String debugInfo = String.format("%s 0x%04X-0x%04X", progressInfo, progAddress, progAddress + txBuffer.length - 1); + if (txBuffer.length != dm.getBlockSize()) { + debugInfo = String.format("%s (%d bytes)", debugInfo, txBuffer.length); } - logger.info(programFlashInfo); - logger.trace("with crc32 {}", String.format("crc32 0x%08X", crc32)); + logger.debug("{} with crc32 {}", debugInfo, String.format("crc32 0x%08X", crc32)); resultProgramData = dm.sendWithRetry(UPDCommand.PROGRAM, progPars, dm.getMaxUpdCommandRetry()); long result = UPDProtocol.checkResult(resultProgramData.data()); if ((result == BYTECOUNT_RECEIVED_TOO_LOW.id) || (result == BYTECOUNT_RECEIVED_TOO_HIGH.id)) { repeat = true; - resultTotal.setWritten(resultTotal.written() - txBuffer.length); // do not count failed transfer + // do not count failed transfer + progressInfo.update(-txBuffer.length); } else if (result == IAP_COMPARE_ERROR.id) { throw new UpdaterException(String.format("ProgramData update failed. %s", @@ -122,6 +107,8 @@ else if (result == IAP_SUCCESS.id) { resultTotal.addCounters(resultProgramData); // keep track of statistic data } fis.close(); + dm.finalProgressInfo(progressInfo); + resultTotal.setWritten(progressInfo.getBytesDone()); return resultTotal; } } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 548dad02..f9ce693f 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -110,6 +110,7 @@ public static void main(final String[] args) { } catch (final Throwable t) { logger.error("parsing options ", t); } finally { + AnsiCursor.on(); logger.debug("main exit"); } } @@ -150,21 +151,6 @@ public void run() { } } - private static void printStatisticData(long flashTimeStart, ResponseResult result) { - // logging of some static data - long flashTimeDuration = System.currentTimeMillis() - flashTimeStart; - float bytesPerSecond = (float) result.written() / (flashTimeDuration / 1000f); - String col; - if (bytesPerSecond >= 50.0) { - col = ansi().fgBright(GREEN).toString(); - } else { - col = ansi().fgBright(RED).toString(); - } - String infoMsg = String.format("Wrote %d bytes from file to device in %tM:% {}", CliOptions.OPT_LONG_NO_FLASH, diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java index e5585fc6..25e4b851 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java @@ -1,6 +1,5 @@ package org.selfbus.updater; -import org.fusesource.jansi.AnsiConsole; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tuwien.auto.calimero.KNXException; @@ -20,8 +19,6 @@ */ public class Utils { private static final Logger logger = LoggerFactory.getLogger(Utils.class.getName()); - public static final String PROGRESS_MARKER = "."; //!< symbol to print progress to console - private static final int FALLBACK_CONSOLE_WIDTH = 80; public static long streamToLong(byte[] stream, int offset) { return ((stream[offset] & 0xFF)) | @@ -178,12 +175,4 @@ private static void closeTcpConnections() { c.close(); } } - - public static int getConsoleWidth() { - int jansiConsoleWidth = AnsiConsole.getTerminalWidth(); - if (jansiConsoleWidth != 0) { - return jansiConsoleWidth; - } - return FALLBACK_CONSOLE_WIDTH; - } } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java index 5ad56fc3..835e0927 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -1,21 +1,58 @@ package org.selfbus.updater.gui; +import org.selfbus.updater.AnsiCursor; + +import javax.swing.*; import javax.swing.text.*; +import java.awt.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ConColorsToStyledDoc { +import static org.fusesource.jansi.Ansi.ansi; + +public final class ConColorsToStyledDoc { + @SuppressWarnings("unused") + private ConColorsToStyledDoc() {} // avoids instance creation private static final Style stringStyle = new javax.swing.text.StyleContext().addStyle("testStyle", null); public static final java.awt.Color DefaultForegroundColor = java.awt.Color.white; public static final java.awt.Color DefaultBackgroundColor = java.awt.Color.black; - private static final String RegExAnsi = "\033\\[([;\\d]*)m"; // Regular expression to match ANSI escape sequences + private static final String AnsiEscape = "\033"; // ASCII escape character (ESC) + private static final String AnsiBracket = "["; // Ansi second character [ + + /** + * Regular expression for matching ANSI escape sequences. + *

+ * Includes sequences for text styles, colors and some cursor movements. + *

+ *

    + *
  • {@code ([;\\d*)[A-Hmsu]} - Matches a sequence of zero or more digits and semicolons + * followed by a character indicating the type of ANSI control or formatting action. + *
      + *
    • A-H - Cursor movement
    • + *
    • m - Set Graphics Mode (color, bold, underline, etc.)
    • + *
    • s - Save Cursor Position
    • + *
    • u - Restore Cursor Position
    • + *
    + *
  • + *
  • {@code (\\?25[hl])} - Cursor on/off
  • + *
+ *

+ *

+ * Great overview on ANSI escape codes and their usage: + * ANSI escape codes + *

+ */ + private static final String RegExAnsi = AnsiEscape + "\\" + AnsiBracket + "(([;\\d]*)[A-Hmsu]|(\\?25[hl]))"; + private static final String RegExAnsiCursor = AnsiEscape + "\\" + AnsiBracket + "(([;\\d]*)[A-Hsu])"; private static final String AnsiCodeSeparator = ";"; - private static void insertStyledString(String toInsert, String colorCode, StyledDocument document) throws BadLocationException { + private static void insertStyledString(String toInsert, String colorCode, JTextPane textPane) throws BadLocationException { Style newStyle = colorCodeToStyle(colorCode.split(AnsiCodeSeparator)); + StyledDocument document = (StyledDocument) textPane.getDocument(); document.insertString(document.getLength(), toInsert, newStyle); + textPane.setCaretPosition(document.getLength()); } /* @@ -23,28 +60,117 @@ private static void insertStyledString(String toInsert, String colorCode, Styled * z.B. wird ein String "\033[0;31m dieser Text wird rot sein,\033[44m jetzt auf blauem Hintergrund" übergeben * Dieser String wird entsprechend der Steuerbefehle in ein StyledDocument gefüllt */ - public static void Convert(String input, StyledDocument originDocument) throws BadLocationException { + public static void Convert(String input, JTextPane textPane) throws BadLocationException { Pattern pattern = Pattern.compile(RegExAnsi); Matcher matcher = pattern.matcher(input); String cleanedString = ""; - String colorCode = ""; + String ansiCode = ""; int index = 0; while (matcher.find()) { if (matcher.start() > index) { // possible text before the ANSI escape sequence cleanedString = input.substring(index, matcher.start()); - insertStyledString(cleanedString, colorCode, originDocument); + insertStyledString(cleanedString, ansiCode, textPane); + } + ansiCode = matcher.group(); + + if (processAnsiCursor(ansiCode, textPane)) { + index = matcher.end(); + ansiCode = ""; + continue; } - colorCode = matcher.group(1); + + ansiCode = ansiCode.replace(AnsiEscape + AnsiBracket, ""); index = matcher.end(); } if (index < input.length()) { // possible remaining text after the last ANSI escape sequence cleanedString = input.substring(index); - insertStyledString(cleanedString, colorCode, originDocument); + insertStyledString(cleanedString, ansiCode, textPane); + } + + // immer die letzte Zeile zeigen + textPane.setCaretPosition(textPane.getDocument().getLength()); + } + + /** + * Processes ANSI cursor control codes and updates the cursor position in the given {@link JTextPane}. + * + *

Warning: This method is partially implemented. Some cursor movement codes are not yet handled and will throw an exception if encountered.

* + *

When encountering an unsupported ANSI cursor movement code, an {@link IllegalStateException} is thrown.

+ * + * @param ansiCode the ANSI code to process + * @param textPane the {@link JTextPane} whose cursor position will be updated + * @return {@code true} if the ANSI code was successfully processed; {@code false} otherwise + * @throws BadLocationException if an error occurs when updating the cursor position + * @throws IllegalStateException if an not implemented or unexpected ANSI code is encountered + */ + private static boolean processAnsiCursor(String ansiCode, JTextPane textPane) throws BadLocationException { + if (ansiCode.equals(AnsiCursor.off()) || ansiCode.equals(AnsiCursor.on())) { + return true; // there is no visible cursor in the JTextPane + } + + if (ansiCode.equals(ansi().restoreCursorPosition().toString()) || + ansiCode.equals(ansi().saveCursorPosition().toString())) { + //todo implement save and restore cursor position + throw new IllegalStateException("Save/Restore cursor position not implemented: " + ansiCode); + } + + Pattern pattern = Pattern.compile(RegExAnsiCursor); + Matcher matcher = pattern.matcher(ansiCode); + String cursorMovement = ""; + while (matcher.find()) { + cursorMovement = matcher.group(1); + if (cursorMovement.isEmpty()) { + continue; + } + + Point moveCursor = getCursorPosition(textPane); + int cursorMovementCode = cursorMovement.charAt(cursorMovement.length() - 1); + int number; + if (cursorMovement.length() > 1) { + number = Integer.parseInt(cursorMovement.substring(0, cursorMovement.length() - 1)); + } + else { + number = 1; + } + + switch (cursorMovementCode) { + //todo implement remaining cursor movements + case 65: // A Move up by n rows + case 66: // B Move down by n rows + case 67: // C Move right by n columns + case 68: // D Move left by n columns + case 72: // H Move cursor to row n and column m + throw new IllegalStateException("Not implemented cursorMovement: " + cursorMovement); + + case 69: // E Move cursor to beginning of line and n lines down + moveCursor.x = 1; + moveCursor.y += number; + setCursorPosition(textPane, moveCursor); + return true; + + case 70: // F Move cursor to beginning of line and n lines up + moveCursor.x = 1; + moveCursor.y -= number; + setCursorPosition(textPane, moveCursor); + return true; + + case 71: // G Move cursor to column n + moveCursor.x = number; + setCursorPosition(textPane, moveCursor); + return true; + + default: + throw new IllegalStateException("Unexpected cursorMovement: " + cursorMovement); + } } + return false; } + /** + * Resets the styling attributes of the {@link #stringStyle} + */ private static void resetStringStyle() { StyleConstants.setForeground(ConColorsToStyledDoc.stringStyle, DefaultForegroundColor); StyleConstants.setBackground(ConColorsToStyledDoc.stringStyle, DefaultBackgroundColor); @@ -54,13 +180,28 @@ private static void resetStringStyle() { StyleConstants.setStrikeThrough(ConColorsToStyledDoc.stringStyle,false); } + /** + * Converts an array of color codes to a {@link Style} with the corresponding styles applied. + *

+ * This method parses ANSI color codes and applies the styles to the {@link #stringStyle}, such as + * setting text to bold, italic, underlined, strikethrough, and changing foreground and background colors. + *

+ * + * @param colorCodes an array of ANSI color codes as {@link String} values + * @return the {@link #stringStyle} object with the styles applied + * @throws IllegalStateException if an 256 fore/background color code (38 or 48) or an unexpected code value is encountered + */ private static Style colorCodeToStyle(String [] colorCodes) { for (String code : colorCodes) { - if (code.isEmpty()) { + if (code.isEmpty() || code.equals("m")) { resetStringStyle(); continue; } + if (code.charAt(code.length() - 1) == 'm') { + code = code.substring(0, code.length() - 1); // strip of 'm' + } + switch (Integer.parseInt(code)) { case 0: // Reset resetStringStyle(); @@ -243,10 +384,83 @@ private static Style colorCodeToStyle(String [] colorCodes) { return stringStyle; } - public static Style testColorCodeToStyle(String [] colorCodes) { - return colorCodeToStyle(colorCodes); + /** + * Returns the current virtual cursor position within the given {@link JTextPane} as a {@link Point}. + * The position is represented as (column, row) with 1-based indexing like in consoles. + * + * @param textPane the {@link JTextPane} from which to retrieve the cursor position + * @return a {@link Point} representing the cursor position, where the x-coordinate is the column + * number and the y-coordinate is the row number, both 1-based + */ + private static Point getCursorPosition(JTextPane textPane) { + int caretPosition = textPane.getCaretPosition(); + // Get the row at the caret position + Element docRoot = textPane.getStyledDocument().getDefaultRootElement(); + int row = docRoot.getElementIndex(caretPosition); + + // Get the start offset of the column + Element lineElement = docRoot.getElement(row); + int lineStartOffset = lineElement.getStartOffset(); + int column = caretPosition - lineStartOffset; + return new Point(column + 1, row + 1); // 1-based indexing for ANSI console + } + + /** + * Sets the virtual cursor position in the specified {@link JTextPane} to the given {@link Point}. + * The {@link Point} specifies the row and column (1-based index). + * + * @param textPane The {@link JTextPane} in which to set the cursor position. + * @param newPosition A {@link Point} where the x-coordinate represents the column + * and the y-coordinate represents the row. Both coordinates are 1-based. + * @throws BadLocationException If the specified position is invalid within the document. + */ + public static void setCursorPosition(JTextPane textPane, Point newPosition) throws BadLocationException { + StyledDocument doc = textPane.getStyledDocument(); + int rowIndex = Math.max(0, newPosition.y - 1); // rowIndex starts from 0 + int columnIndex = Math.max(0, newPosition.x - 1); // columnIndex starts from 0 + + // Get the line start offset + int lineStartOffset = doc.getDefaultRootElement().getElement(rowIndex).getStartOffset(); + + // Calculate the new caret position + int newCaretPosition = lineStartOffset + columnIndex; + + if (newCaretPosition < 0) { + newCaretPosition = 0; + } + + if (newCaretPosition > doc.getLength()) { + newCaretPosition = doc.getLength(); + } + + StyledDocument document = (StyledDocument) textPane.getDocument(); + int currentLength = document.getLength(); + // Set the caret position + if (newCaretPosition < currentLength) { + document.remove(newCaretPosition, currentLength - newCaretPosition); + } + + textPane.setCaretPosition(document.getLength()); + } + + /** + * This method is intended for unit tests only. + * It invokes the {@link #colorCodeToStyle} method with the provided color codes. + * + * @param colorCodes an array of color codes to be converted to styles. + * @warning This method is intended for unit tests only. + */ + public static void testColorCodeToStyle(String [] colorCodes) { + colorCodeToStyle(colorCodes); } + /** + * This method is intended for unit tests only. + * It returns the current {@link #stringStyle}. + * + * @return the current string style. + * @warning This method is intended for unit tests only. + */ public static Style testStringStyle() { return stringStyle; } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 5390047a..50916e81 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -3,16 +3,13 @@ import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; +import org.selfbus.updater.*; import tuwien.auto.calimero.KNXFormatException; import tuwien.auto.calimero.KNXIllegalArgumentException; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; import org.apache.commons.cli.ParseException; -import org.selfbus.updater.CliOptions; -import org.selfbus.updater.DiscoverKnxInterfaces; -import org.selfbus.updater.ToolInfo; -import org.selfbus.updater.Updater; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -399,7 +396,11 @@ public void startUpdaterGui() { new Thread(this::LoadKnxIpInterfacesAndFillComboBox).start(); - TextAppender.addLog4j2TextPaneAppender(this.jLoggingPane); + TextAppender textAppender = ListTextAppenders.searchAppender(); + if (textAppender != null) { + textAppender.setTextPane(this.jLoggingPane); + } + InitGuiElementsVisibility(); fillScenarios(); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/TextAppender.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/TextAppender.java index aa589e37..70b67aa5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/TextAppender.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/TextAppender.java @@ -4,53 +4,50 @@ import ch.qos.logback.core.AppenderBase; import javax.swing.*; -import javax.swing.text.StyledDocument; -import java.util.ArrayList; +import javax.swing.text.BadLocationException; import static javax.swing.SwingUtilities.invokeLater; public class TextAppender extends AppenderBase { + private JTextPane textPane; - private static final ArrayList textPanes = new ArrayList<>(); + @SuppressWarnings("unused") + public TextAppender() { + this.textPane = null; + } - // Add the target JTextPane to be populated and updated by the logging information. - public static void addLog4j2TextPaneAppender(final JTextPane textPane){ - TextAppender.textPanes.add(textPane); + private void updateTextPane(String message) throws BadLocationException { + if (getTextPane() == null) { + return; + } + ConColorsToStyledDoc.Convert(message, getTextPane()); } @Override protected void append(ILoggingEvent event) { - String message = event.getFormattedMessage(); - message = message + System.lineSeparator(); // jeden Eintrag als newline enden lassen - - String finalMessage = message; - - // Append formatted message to text area using the Thread. - try - { - invokeLater(() -> - { - for (JTextPane textPane : textPanes) - { - try - { - if (textPane != null) - { - StyledDocument document = (StyledDocument) textPane.getDocument(); - ConColorsToStyledDoc.Convert(finalMessage, document); - - // immer die letzte Zeile zeigen - textPane.setCaretPosition(textPane.getDocument().getLength()); - } - } catch (Throwable throwable) - { - throwable.printStackTrace(); - } - } - }); - } catch (IllegalStateException exception) - { - exception.printStackTrace(); + String eventMessage = event.getFormattedMessage(); + // Make sure, that every eventMessage ends with a line separator + if (eventMessage.lastIndexOf(System.lineSeparator()) != eventMessage.length() - System.lineSeparator().length()) { + eventMessage += System.lineSeparator(); } + + final String finalMessage = eventMessage; + invokeLater(() -> { + try { + // Append formatted message to text area using the Thread. + updateTextPane(finalMessage); + } + catch (Throwable e) { + throw new RuntimeException(e); + } + }); + } + + public JTextPane getTextPane() { + return textPane; + } + + public void setTextPane(JTextPane textPane) { + this.textPane = textPane; } } \ No newline at end of file diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java index 7eb24607..cb4d0d07 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java @@ -217,7 +217,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash } debug("\n"); - System.out.print("Page " + pages + ", "); + logger.debug("Page {}, ",pages); flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); outputDiffStream.clear(); pages++; @@ -241,7 +241,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash } debug("\n"); - System.out.print("Page " + pages + ", "); + logger.debug("Page {}, ", pages); flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); totalBytesTransferred = size; logger.debug("OK! Total diff stream length={} bytes", ansi().fgBright(GREEN).a(size).reset().toString()); @@ -250,6 +250,6 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash } protected void debug(String format, Object... args) { - //System.out.format(format, args); + logger.trace(String.format(format, args)); } } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java index 8ea8ab4c..2ae449b8 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java @@ -1,7 +1,5 @@ package org.selfbus.updater.upd; -import ch.qos.logback.classic.Level; -import org.selfbus.updater.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,9 +38,6 @@ public static long checkResult(byte[] result, boolean verbose) { } else { if (verbose) { logger.trace("done ({})", ansi().fgBright(GREEN).a(udpResult.id).reset().toString()); - } else { - System.out.printf(ansi().fgBright(GREEN).a(Utils.PROGRESS_MARKER).reset().toString()); // Success in green - logger.debug(Utils.PROGRESS_MARKER); } } return udpResult.id; diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java index 55798ff3..87fd01e1 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.selfbus.updater.gui.ConColorsToStyledDoc.*; +import javax.swing.*; import javax.swing.text.*; import java.awt.*; @@ -184,25 +185,112 @@ public void testExceptionOnExtendedColors() { } @Test - public void testConvert() { - StyledDocument document = new DefaultStyledDocument(); - try { - //todo add more testcases - String[][] testCases = { - {"\033[mreset_jansi\033[m", "reset_jansi"}, // reset like jansi it sends - {"\033[0mreset_standard\033[0m", "reset_standard"}, // Ansi default reset - {"normal_start\033[1;32mboldGreen\033[0mnormal_end", "normal_startboldGreennormal_end"}, - {"Device 1.1.32 reported \033[92m1\033[m second(s) for restarting", "Device 1.1.32 reported 1 second(s) for restarting"} - }; - - for (String[] underTest : testCases) { - document.remove(0, document.getLength()); - ConColorsToStyledDoc.Convert(underTest[0], document); - //todo check also the style and not only the text - assertEquals(underTest[1], document.getText(0, document.getLength())); - } - } catch (BadLocationException e) { - throw new RuntimeException(e); + public void testConvert() throws BadLocationException { + JTextPane textPane = new JTextPane(); + StyledDocument document = (StyledDocument) textPane.getDocument(); + //todo add more testcases + String[][] testCases = { + {"\" Done Speed Avg Min Max Time\"", "\" Done Speed Avg Min Max Time\""}, // no ansi + {"\033[mreset_jansi\033[m", "reset_jansi"}, // reset like jansi it sends + {"\033[0mreset_standard\033[0m", "reset_standard"}, // Ansi default reset + {"normal_start\033[1;32mboldGreen\033[0mnormal_end", "normal_startboldGreennormal_end"}, + {"Device 1.1.32 reported \033[92m1\033[m second(s) for restarting", "Device 1.1.32 reported 1 second(s) for restarting"} + }; + + for (String[] underTest : testCases) { + document.remove(0, document.getLength()); + ConColorsToStyledDoc.Convert(underTest[0], textPane); + //todo check also the style and not only the text + assertEquals(underTest[1], document.getText(0, document.getLength())); + } + } + + @Test + public void testCursor() throws BadLocationException { + JTextPane textPane = new JTextPane(); + StyledDocument document = (StyledDocument) textPane.getDocument(); + //todo add more testcases + String[][] testCases = { + {"\033[1G\033[92m|\033[m 0,1% \033[92m 86,7\033[m \033[92m 86,7\033[m 86,7 86,7 00:00", "| 0,1% 86,7 86,7 86,7 86,7 00:00"} + }; + for (String[] underTest : testCases) { + document.remove(0, document.getLength()); + ConColorsToStyledDoc.Convert(underTest[0], textPane); + assertEquals(underTest[1], document.getText(0, document.getLength())); + } + } + + @Test + public void testCursorOnOff() throws BadLocationException { + JTextPane textPane = new JTextPane(); + StyledDocument document = (StyledDocument) textPane.getDocument(); + //todo add more testcases + String[][] testCases = { + {"\033[?25h", ""}, // Cursor on + {"\033[?25l", ""}, // Cursor off + }; + for (String[] underTest : testCases) { + document.remove(0, document.getLength()); + ConColorsToStyledDoc.Convert(underTest[0], textPane); + assertEquals(underTest[1], document.getText(0, document.getLength())); + } + } + + @Test + public void testCursorSaveAndRestore() throws BadLocationException { + JTextPane textPane = new JTextPane(); + StyledDocument document = (StyledDocument) textPane.getDocument(); + String[] testCases = { + "\033[s", // Cursor save + "\033[u", // Cursor restore last position + }; + for (String underTest : testCases) { + document.remove(0, document.getLength()); + assertThrows(IllegalStateException.class, () -> { + ConColorsToStyledDoc.Convert(underTest, textPane); + }); + } + } + + @Test + public void testCursorMovement() throws BadLocationException { + JTextPane textPane = new JTextPane(); + StyledDocument document = (StyledDocument) textPane.getDocument(); + String[][] testCases = { + { // Cursor one line up (without parameter )and to column 1 + "testLine_1" + System.lineSeparator() + + "testLine_2" + System.lineSeparator() + + "testLine_3" + "\033[F" + + "test_end", + "testLine_1" + System.lineSeparator() + "test_end"}, + + { // Cursor one line up and to column 1 + "testLine_1" + System.lineSeparator() + + "testLine_2" + System.lineSeparator() + + "testLine_3" + "\033[1F" + + "test_end", + "testLine_1" + System.lineSeparator() + "test_end"}, + + { // Cursor two lines up and to column 1 + "testLine_1" + System.lineSeparator() + + "testLine_2" + System.lineSeparator() + + "testLine_3" + "\033[2F" + + "test_end", + "test_end"}, + + // cursor to column 1 + {"test_front\033[1Gtest_back", "test_back"}, + + // cursor to column 11 + {"test_front_erased\033[11G+test_back", "test_front+test_back"}, + + // cursor to column 1 + {"123456789" + System.lineSeparator() + "\033[1G", "123456789" + System.lineSeparator()}, + }; + for (String[] underTest : testCases) { + document.remove(0, document.getLength()); + ConColorsToStyledDoc.Convert(underTest[0], textPane); + assertEquals(underTest[1], document.getText(0, document.getLength())); } } } From 522b84ad002984d364ad64c9fe75d7eddc22ced4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 01:19:33 +0200 Subject: [PATCH 049/359] [Updater] Make `java-intelhex-parser` a dependecy To build it, we use https://jitpack.io so we can add it as our dependency, The build of `java-intelhex-parser` v1.0 fails, therefore the build is linked to commit https://github.com/j123b567/java-intelhex-parser/commit/9dec82355697dba028e8aa1832b8b9ee1b66d9e2 --- firmware_updater/updater/source/build.gradle | 18 +- .../updater/source/settings.gradle | 17 +- .../java/cz/jaybee/intelhex/DataListener.java | 50 ---- .../cz/jaybee/intelhex/IntelHexException.java | 42 ---- .../cz/jaybee/intelhex/MemoryRegions.java | 111 --------- .../main/java/cz/jaybee/intelhex/Parser.java | 233 ------------------ .../main/java/cz/jaybee/intelhex/Record.java | 60 ----- .../java/cz/jaybee/intelhex/RecordType.java | 71 ------ .../main/java/cz/jaybee/intelhex/Region.java | 111 --------- .../jaybee/intelhex/listeners/BinWriter.java | 93 ------- .../intelhex/listeners/RangeDetector.java | 63 ----- 11 files changed, 26 insertions(+), 843 deletions(-) delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/DataListener.java delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/IntelHexException.java delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/MemoryRegions.java delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Parser.java delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Record.java delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/RecordType.java delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Region.java delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/listeners/BinWriter.java delete mode 100644 firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/listeners/RangeDetector.java diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 079c9465..5b69e9fa 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -47,11 +47,6 @@ artifacts { archives fatJar } -repositories { - mavenCentral() - maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots' } -} - gradle.projectsEvaluated{ tasks.withType(JavaCompile){ options.compilerArgs << "-Xlint:unchecked" @@ -90,6 +85,10 @@ tasks.named('check') { dependsOn(testing.suites.integrationTest) } +// +// Repositories are now configured in settings.gradle, so we can include https://jitpack.io. +// We need it to add java-intelhex-parser as an dependency, instead of copy/pasting the source. +// dependencies { // calimero knx bus access library implementation 'com.github.calimero:calimero-core:2.6-rc1' @@ -114,4 +113,13 @@ dependencies { // GUI implementation files('libs/forms_rt.jar') + + // Intel hex parser + // commit 9dec823 of 2023/02/27 + // https://github.com/j123b567/java-intelhex-parser/commit/9dec82355697dba028e8aa1832b8b9ee1b66d9e2 + implementation 'com.github.j123b567:java-intelhex-parser:9dec823' + // To revert this, just delete "implementation 'com.github.j123b567:java-intelhex-parser:9dec823'" + // and c&p the code of + // ../cz/jaybee/intelhex to + // ../software-arm-lib/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex } \ No newline at end of file diff --git a/firmware_updater/updater/source/settings.gradle b/firmware_updater/updater/source/settings.gradle index ec38d8fa..2ae16b0a 100644 --- a/firmware_updater/updater/source/settings.gradle +++ b/firmware_updater/updater/source/settings.gradle @@ -1,8 +1,17 @@ rootProject.name = 'SB_updater' + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + mavenCentral() + maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots' } + maven { url 'https://jitpack.io' } // jitpack builds if needed java-intelhex-parser for us + } /* -sourceControl { - gitRepository("https://github.com/calimero-project/calimero-core.git") { - producesModule('com.github.calimero:calimero-core') + sourceControl { + gitRepository(uri("https://github.com/calimero-project/calimero-core.git")) { + producesModule('com.github.calimero:calimero-core') + } } +*/ } -*/ \ No newline at end of file diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/DataListener.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/DataListener.java deleted file mode 100644 index 70d3cd1e..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/DataListener.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex; - -/** - * Listener interface to parser events - * - * @author Jan Breuer - */ -public interface DataListener { - - /** - * Every time new data are read from file, this listener method is called - * with appropriate values. Multiple calls of this function may be done - * inside one memory regions but they will not overlap (if they don't - * overlap in original intelhex). - * - * @param address - * @param data - */ - public void data(long address, byte[] data); - - /** - * After eof is detected in the file, this listener method is called - */ - public void eof(); -} diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/IntelHexException.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/IntelHexException.java deleted file mode 100644 index 366db604..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/IntelHexException.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex; - -/** - * Custom exception to prevent using general Exception - * - * @author Jan Breuer - */ -@SuppressWarnings("serial") -public class IntelHexException extends Exception { - - public IntelHexException() { - } - - public IntelHexException(String message) { - super(message); - } -} diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/MemoryRegions.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/MemoryRegions.java deleted file mode 100644 index 91d9802d..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/MemoryRegions.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * Class to hold all memory address regions - * - * @author Jan Breuer - * @author riilabs - */ -public class MemoryRegions { - - private final List regions = new ArrayList<>(); - - public void add(long start, long length) { - Region prevRegion; - if (regions.size() > 0) { - prevRegion = regions.get(regions.size() - 1); - long nextAddress = prevRegion.getAddressStart() + prevRegion.getLength(); - if (nextAddress == start) { - prevRegion.incLength(length); - return; - } - } - regions.add(new Region(start, length)); - } - - public void compact() { - Collections.sort(regions); - - Iterator iter = regions.iterator(); - Region prev = null; - while (iter.hasNext()) { - Region curr = iter.next(); - if (prev == null) { - prev = curr; - } else { - // check for chaining - if (curr.getAddressStart() == (prev.getAddressStart() + prev.getLength())) { - prev.incLength(curr.getLength()); - iter.remove(); - } else { - prev = curr; - } - } - } - } - - public void clear() { - regions.clear(); - } - - public int size() { - return regions.size(); - } - - public Region get(int index) { - return regions.get(index); - } - - public Region getFullRangeRegion() { - long start = 0; - long length = 0; - if (!regions.isEmpty()) { - start = regions.get(0).getAddressStart(); - Region last = regions.get(regions.size() - 1); - length = last.getAddressStart() + last.getLength() - start; - } - - return new Region(start, length); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - - for (Region r : regions) { - sb.append(r).append("\r\n"); - } - - return sb.toString(); - } -} diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Parser.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Parser.java deleted file mode 100644 index 7ebe9801..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Parser.java +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex; - -import java.io.*; - -/** - * Main Intel HEX parser class - * - * @author Jan Breuer - * @author Kristian Sloth Lauszus - * @author riilabs - */ -public class Parser { - - private final BufferedReader reader; - private DataListener dataListener = null; - private static final int HEX = 16; - private boolean eof = false; - private int recordIdx = 0; - private long upperAddress = 0; - private long startAddress = 0; - - /** - * Constructor of the parser with reader - * - * @param reader - */ - public Parser(Reader reader) { - this.reader = (reader instanceof BufferedReader) ? (BufferedReader) reader : new BufferedReader(reader); - } - - /** - * Constructor of the parser with input stream - * - * @param stream - */ - public Parser(InputStream stream) { - this.reader = new BufferedReader(new InputStreamReader(stream)); - } - - /** - * Set data listener to parsing events (data and eof) - * - * @param listener - */ - public void setDataListener(DataListener listener) { - this.dataListener = listener; - } - - /** - * Parse one line of Intel HEX file - * - * @param string record - * @return parsed record - * @throws IntelHexException - */ - private Record parseRecord(String record) throws IntelHexException { - Record result = new Record(); - // check, if there wasn an accidential EOF record - if (eof) { - throw new IntelHexException("Data after eof (" + recordIdx + ")"); - } - - // every IntelHEX record must start with ":" - if (!record.startsWith(":")) { - throw new IntelHexException("Invalid Intel HEX record (" + recordIdx + ")"); - } - - int lineLength = record.length(); - byte[] hexRecord = new byte[lineLength / 2]; - - // sum of all bytes modulo 256 (including checksum) should be 0 - int sum = 0; - for (int i = 0; i < hexRecord.length; i++) { - String num = record.substring(2 * i + 1, 2 * i + 3); - hexRecord[i] = (byte) Integer.parseInt(num, HEX); - sum += hexRecord[i] & 0xff; - } - sum &= 0xff; - - if (sum != 0) { - throw new IntelHexException("Invalid checksum (" + recordIdx + ")"); - } - - // if the length field does not correspond with line length - result.length = hexRecord[0] & 0xFF; - if ((result.length + 5) != hexRecord.length) { - throw new IntelHexException("Invalid record length (" + recordIdx + ")"); - } - // length is OK, copy data - result.data = new byte[result.length]; - System.arraycopy(hexRecord, 4, result.data, 0, result.length); - - // build lower part of data address - result.address = ((hexRecord[1] & 0xFF) << 8) + (hexRecord[2] & 0xFF); - - // determine record type - result.type = RecordType.fromInt(hexRecord[3] & 0xFF); - if (result.type == RecordType.UNKNOWN) { - throw new IntelHexException("Unsupported record type " + (hexRecord[3] & 0xFF) + " (" + recordIdx + ")"); - } - - return result; - } - - /** - * Process parsed record, copute correct address, emit events - * - * @param record - * @throws IntelHexException - */ - private void processRecord(Record record) throws IntelHexException { - // build full address - long addr = record.address | upperAddress; - switch (record.type) { - case DATA: - if (dataListener != null) { - dataListener.data(addr, record.data); - } - break; - case EOF: - if (dataListener != null) { - dataListener.eof(); - } - eof = true; - break; - case EXT_LIN: - if (record.length == 2) { - upperAddress = ((record.data[0] & 0xFF) << 8) + (record.data[1] & 0xFF); - upperAddress <<= 16; // ELA is bits 16-31 of the segment base address (SBA), so shift left 16 bits - } else { - throw new IntelHexException("Invalid EXT_LIN record (" + recordIdx + ")"); - } - - break; - case EXT_SEG: - if (record.length == 2) { - upperAddress = ((record.data[0] & 0xFF) << 8) + (record.data[1] & 0xFF); - upperAddress <<= 4; // ESA is bits 4-19 of the segment base address (SBA), so shift left 4 bits - } else { - throw new IntelHexException("Invalid EXT_SEG record (" + recordIdx + ")"); - } - break; - case START_LIN: - if (record.length == 4) { - startAddress = 0; - for (byte c : record.data) { - startAddress = startAddress << 8; - startAddress |= (c & 0xFF); - } - } else { - throw new IntelHexException("Invalid START_LIN record at line #" + recordIdx + " " + record); - } - break; - case START_SEG: - if (record.length == 4) { - startAddress = 0; - for (byte c : record.data) { - startAddress = startAddress << 8; - startAddress |= (c & 0xFF); - } - } else { - throw new IntelHexException("Invalid START_SEG record at line #" + recordIdx + " " + record); - } - break; - case UNKNOWN: - break; - } - - } - - /** - * Return program start address/reset address. May not be at the beggining - * of the data. - * - * @return Start address - */ - public long getStartAddress() { - return startAddress; - } - - /** - * Main public method to start parsing of the input - * - * @throws IntelHexException - * @throws IOException - */ - public void parse() throws IntelHexException, IOException { - eof = false; - recordIdx = 1; - upperAddress = 0; - startAddress = 0; - String recordStr; - - while ((recordStr = reader.readLine()) != null) { - // Ignore if this is a blank line. - if (recordStr.isEmpty()) { - continue; - } - Record record = parseRecord(recordStr); - processRecord(record); - recordIdx++; - } - - if (!eof) { - throw new IntelHexException("No eof at the end of file"); - } - } -} diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Record.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Record.java deleted file mode 100644 index 6fd0dd42..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Record.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex; - -/** - * Class to hold one Intel HEX record - one line in the file - * @author Jan Breuer - */ -public class Record { - - public int length; - public int address; - public RecordType type; - public byte[] data; - - /** - * Convert the record to pretty string - * - * @return - */ - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - - sb.append(type); - sb.append(" @"); - sb.append(String.format("0x%04X", address)); - sb.append(" ["); - for (byte c : data) { - sb.append(String.format("0x%02X", c)); - sb.append(" "); - } - sb.setLength(sb.length() - 1); - sb.append("]"); - return sb.toString(); - } -} diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/RecordType.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/RecordType.java deleted file mode 100644 index 229b5837..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/RecordType.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex; - -/** - * Type of one record in Intel HEX file (type of line) - * - * @author Jan Breuer - */ -public enum RecordType { - - DATA(0x00), - EOF(0x01), - EXT_SEG(0x02), - START_SEG(0x03), - EXT_LIN(0x04), - START_LIN(0x05), - UNKNOWN(0xFF); - int id; - - RecordType(int id) { - this.id = id; - } - - /** - * Convert enum value to integer - * - * @return record type integer value - */ - public int toInt() { - return id; - } - - /** - * Convert integer value to enum value - * - * @param id record type integer value - * @return record type enum value - */ - public static RecordType fromInt(int id) { - for (RecordType d : RecordType.values()) { - if (d.id == id) { - return d; - } - } - return RecordType.UNKNOWN; - } -} diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Region.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Region.java deleted file mode 100644 index 0fa41989..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/Region.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex; - -/** - * One memory region - * - * @author Jan Breuer - */ -public class Region implements Comparable { - - private long addressStart; - private long addressEnd; - - public Region(long start, long length) { - this.addressStart = start; - this.addressEnd = start + length - 1; - } - - /** - * Get length of the region - * @return length of the region - */ - public long getLength() { - return addressEnd - addressStart + 1; - } - - /** - * Return last address in memory region - * @return last address in memory region - */ - public long getAddressEnd() { - return addressEnd; - } - - /** - * Set end address - * @param addressEnd - */ - public void setAddressEnd(long addressEnd) { - this.addressEnd = addressEnd; - } - - /** - * Get start address of the region - * @return start address of memory region - */ - public long getAddressStart() { - return addressStart; - } - - /** - * Set start address - * @param addressStart - */ - public void setAddressStart(long addressStart) { - this.addressStart = addressStart; - } - - /** - * Increment length of the region by value - * @param value - */ - void incLength(long value) { - addressEnd += value; - } - - @Override - public String toString() { - return String.format("0x%08x:0x%08x (%dB 0x%08X)", addressStart, addressEnd, getLength(), getLength()); - } - - /** - * Compare, if one region is after another region - * - * @param o the object to be compared. - * @return a negative integer, zero, or a positive integer as this object - * is less than, equal to, or greater than the specified object. - */ - @Override - public int compareTo(Region o) { - if (this.addressStart == o.addressStart) { - return Long.compare(this.addressEnd, o.addressEnd); - } else { - return Long.compare(this.addressStart, o.addressStart); - } - } -} diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/listeners/BinWriter.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/listeners/BinWriter.java deleted file mode 100644 index 870781dd..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/listeners/BinWriter.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex.listeners; - -import cz.jaybee.intelhex.DataListener; -import cz.jaybee.intelhex.MemoryRegions; -import cz.jaybee.intelhex.Region; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Binary file writer - * - * @author Jan Breuer - */ -public class BinWriter implements DataListener { - - private final Region outputRegion; - private final OutputStream destination; - private final byte[] buffer; - private final MemoryRegions regions; - private long maxAddress; - private final boolean minimize; - - public BinWriter(Region outputRegion, OutputStream destination, boolean minimize) { - this.outputRegion = outputRegion; - this.destination = destination; - this.minimize = minimize; - this.buffer = new byte[(int) (outputRegion.getLength())]; - Arrays.fill(buffer, (byte) 0xFF); - regions = new MemoryRegions(); - maxAddress = outputRegion.getAddressStart(); - } - - @Override - public void data(long address, byte[] data) { - regions.add(address, data.length); - - if ((address >= outputRegion.getAddressStart()) && (address <= outputRegion.getAddressEnd())) { - int length = data.length; - if ((address + length) > outputRegion.getAddressEnd()) { - length = (int) (outputRegion.getAddressEnd() - address + 1); - } - System.arraycopy(data, 0, buffer, (int) (address - outputRegion.getAddressStart()), length); - - if (maxAddress < (address + data.length -1)) { - maxAddress = address + data.length - 1; - } - } - } - - @Override - public void eof() { - try { - if (!minimize) { - maxAddress = outputRegion.getAddressEnd(); - } - destination.write(buffer, 0, (int)(maxAddress - outputRegion.getAddressStart() + 1)); - } catch (IOException ex) { - Logger.getLogger(BinWriter.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public MemoryRegions getMemoryRegions() { - return regions; - } -} diff --git a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/listeners/RangeDetector.java b/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/listeners/RangeDetector.java deleted file mode 100644 index 2e4c8d7b..00000000 --- a/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex/listeners/RangeDetector.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2015, Jan Breuer All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package cz.jaybee.intelhex.listeners; - -import cz.jaybee.intelhex.DataListener; -import cz.jaybee.intelhex.MemoryRegions; -import cz.jaybee.intelhex.Region; - -/** - * First pass listener to calculate data address range for further use - * - * @author riilabs - * @author Jan Breuer - */ -public class RangeDetector implements DataListener { - - private final MemoryRegions regions = new MemoryRegions(); - - @Override - public void data(long address, byte[] data) { - regions.add(address, data.length); - } - - @Override - public void eof() { - regions.compact(); - } - - public void reset() { - regions.clear(); - } - - public Region getFullRangeRegion() { - return regions.getFullRangeRegion(); - } - - public MemoryRegions getMemoryRegions() { - return regions; - } -} From 6ed88ce8cab4c4e55560c9a778e8391c171c8fcc Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 01:28:26 +0200 Subject: [PATCH 050/359] [Updater] Fix was ignored if ft12 or tpuart was set. --- .../java/org/selfbus/updater/CliOptions.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index a379e069..88171a94 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -515,13 +515,11 @@ private void parse(final String[] args) { } logger.debug("tpuart={}", tpuart); - if ((ft12.isEmpty()) && (tpuart.isEmpty())) { - // no ft12 or tpuart => get the - if (cmdLine.getArgs().length <= 0) { - throw new ParseException("No , ft12 or tpuart specified."); - } else { - knxInterface = Utils.parseHost(cmdLine.getArgs()[0]); - } + if (cmdLine.getArgs().length > 0) { + knxInterface = Utils.parseHost(cmdLine.getArgs()[0]); + } + else { + knxInterface = null; } logger.debug("knxInterface={}", knxInterface); @@ -558,9 +556,7 @@ private void parse(final String[] args) { } int interfacesSet = 0; - if (!(userPassword().isEmpty()) && !(devicePassword().isEmpty())) interfacesSet++; - if (tunnelingV2()) interfacesSet++; - if (tunnelingV1()) interfacesSet++; + if (knxInterface() != null) interfacesSet++; if (routing()) interfacesSet++; if (!ft12().isEmpty()) interfacesSet++; if (!tpuart().isEmpty()) interfacesSet++; @@ -568,6 +564,9 @@ private void parse(final String[] args) { if (interfacesSet > 1) { throw new ParseException(ansi().fg(RED).a("Only one bus interface can be used.").reset().toString()); } + else if (interfacesSet == 0) { + throw new ParseException(ansi().fg(RED).a("No bus interface specified.").reset().toString()); + } } catch (ParseException | KNXFormatException e) { StringBuilder cliParsed = new StringBuilder(); From 90242589d27209fd2f541dd8def4fca1a0293694 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 01:29:30 +0200 Subject: [PATCH 051/359] [Updater] Add more checks for IP-Secure and nat --- .../java/org/selfbus/updater/CliOptions.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 88171a94..e4c604e2 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -550,11 +550,40 @@ private void parse(final String[] args) { logger.info(ansi().fg(RED).a("--{} is set. --> switching to full flash mode").reset().toString(), OPT_LONG_ERASEFLASH); } + // nat only possible with tunneling v1 if (nat() && (!tunnelingV1())) { throw new ParseException(String.format(ansi().fg(RED).a("Option --%s can only be used together with --%s").reset().toString(), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1)); } + // nat not allowed with tunneling v2 + if (nat() && (tunnelingV2())) { + throw new ParseException(String.format(ansi().fg(RED).a("Option --%s can not be used together with --%s").reset().toString(), + OPT_LONG_NAT, OPT_LONG_TUNNEL_V2)); + } + + // check IP-secure configuration + if (!(userPassword().isEmpty()) || !(devicePassword().isEmpty())) { + if (knxInterface() == null) { + throw new ParseException(ansi().fg(RED).a("No IP-Interface specified for IP-secure").reset().toString()); + } + else if (!ft12().isEmpty()) { + throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_FT12)).reset().toString()); + } + else if (!tpuart().isEmpty()) { + throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_TPUART)).reset().toString()); + } + else if (nat()) { + throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_NAT)).reset().toString()); + } + else if (tunnelingV1()) { + throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_TUNNEL_V1)).reset().toString()); + } + else if (tunnelingV2()) { + throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_TUNNEL_V2)).reset().toString()); + } + } + int interfacesSet = 0; if (knxInterface() != null) interfacesSet++; if (routing()) interfacesSet++; From 6b38eed4282bf821d55ac1107799c9d4bbe2eb36 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 01:37:27 +0200 Subject: [PATCH 052/359] [Updater] Erase full flash only if new firmware is checked. This ensures that the new firmware is ok and can be flashed. Also it avoids console outputs like: ``` Current APP_VERSION: S_RM_H6 3.00 Deleting the entire flash except from the bootloader itself! File APP_VERSION : S_RM_H6 3.00 ``` --- .../src/main/java/org/selfbus/updater/Updater.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index f9ce693f..8f3f9554 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -258,11 +258,6 @@ public void run() { System.exit(0); } - if (cliOptions.eraseFullFlash()) { - logger.warn(ansi().fgBright(RED).a("Deleting the entire flash except from the bootloader itself!").reset().toString()); - dm.eraseFlash(); - } - // store new firmware bin file in cache directory String cacheFileName = FlashDiffMode.createCacheFileName(newFirmware.startAddress(), newFirmware.length(), newFirmware.crc32()); BinImage imageCache = BinImage.copyFromArray(newFirmware.getBinData(), newFirmware.startAddress()); @@ -284,6 +279,11 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd newFirmware.startAddress() - bootLoaderIdentity.getApplicationFirstAddress()); } + if (cliOptions.eraseFullFlash()) { + logger.warn(ansi().fgBright(RED).a("Deleting the entire flash except from the bootloader itself!").reset().toString()); + dm.eraseFlash(); + } + boolean diffMode = false; if (!(cliOptions.full())) { if (bootDescriptor.valid()) { From dc9ccbb1e3840f5e5af884084e94cdb470598087 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 02:31:30 +0200 Subject: [PATCH 053/359] [Updater] Fix javadoc --- .../java/org/selfbus/updater/bootloader/BootDescriptor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java index 10a2fb3f..424a793b 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java @@ -21,7 +21,8 @@ public class BootDescriptor { private BootDescriptor() {} /** - * Create a @ref BootDescriptor instance from given start/end address, crc32 and application pointer address + * Creates a {@link BootDescriptor} instance from the given start and end addresses, CRC32 checksum, + * and application version address. * @param startAddress start address of the application firmware * @param endAddress end address of the application firmware * @param crc32 crc32 checksum from start to end address From 37061c9f168f4ce5131ffccef085bed99a12f276 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 02:38:36 +0200 Subject: [PATCH 054/359] [Updater] Don't fake start or end address in `toString` This was not consistent. `toString` should return the actual values and not `INVALID_ADDRESS`, even if the BootDescriptor is not valid. It's still not 100% consistent because the long addresses are "cast" to short for smaller log output. Right now we are dealing with 16bit addresses, so this ok, but needs later attention. --- .../updater/bootloader/BootDescriptor.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java index 424a793b..df18cf9c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java @@ -78,22 +78,22 @@ public long length() { return endAddress() - startAddress() + 1; } + //todo get rid of this dirty hack for shorter console output + private short long2short(long a) { + return (short) (a & 0x0000FFFF); + } + public String toString() { String res; if (valid) { res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X", - ansi().fgBright(GREEN).a(" valid").reset().toString(), startAddress, endAddress, length(), crc32); + ansi().fgBright(GREEN).a(" valid").reset().toString(), long2short(startAddress), long2short(endAddress), length(), crc32); } else { res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X", - ansi().fg(RED).a("invalid").reset().toString(), INVALID_ADDRESS & 0x0000FFFF, - INVALID_ADDRESS & 0x0000FFFF, length(), crc32); - } - - if (appVersionAddress != INVALID_ADDRESS) - { - res += String.format(", APP_VERSION pointer: 0x%04X", appVersionAddress); + ansi().fg(RED).a("invalid").reset().toString(), long2short(startAddress), long2short(endAddress), length(), crc32); } + res += String.format(", APP_VERSION pointer: 0x%04X", long2short(appVersionAddress)); return res; } From 8005def768a4ea3b01970f9f064afe89ba4648ec Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 03:21:29 +0200 Subject: [PATCH 055/359] [Updater] Move appVersionAddress calculation to `BinImage` .bin files start at 0. To handle the appVersionAddress correctly, we always have to add or subtract the startAddress of the underlying .hex file. --- .../src/main/java/org/selfbus/updater/BinImage.java | 10 +++++----- .../src/main/java/org/selfbus/updater/Updater.java | 4 +--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java index c7bbfe6e..372804ed 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java @@ -145,16 +145,16 @@ public final boolean writeToBinFile(String fileName) { } public final String toString() { - return String.format("0x%04X-0x%04X, %05d byte(s), crc32 0x%08X", - startAddress, endAddress, binData.length, crc32()); + return String.format("0x%04X-0x%04X, %05d byte(s), crc32 0x%08X, APP_VERSION pointer: 0x%04X", + startAddress(), endAddress(), binData.length, crc32(), getAppVersionAddress()); } - public int getAppVersionAddress() { - return Bytes.indexOf(this.getBinData(), Mcu.APP_VER_PTR_MAGIC) + Mcu.APP_VER_PTR_MAGIC.length; + public long getAppVersionAddress() { + return startAddress() + Bytes.indexOf(this.getBinData(), Mcu.APP_VER_PTR_MAGIC) + Mcu.APP_VER_PTR_MAGIC.length; } public String getAppVersion() { - int appVersionAddress = getAppVersionAddress(); + int appVersionAddress = (int)(getAppVersionAddress() - startAddress()); if (appVersionAddress <= Mcu.VECTOR_TABLE_END || appVersionAddress >= (this.length() - Mcu.BL_ID_STRING_LENGTH)) { return ""; } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 8f3f9554..d33ffd07 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -341,9 +341,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd } BootDescriptor newBootDescriptor = new BootDescriptor(newFirmware.startAddress(), - newFirmware.endAddress(), - (int) newFirmware.crc32(), - newFirmware.startAddress() + newFirmware.getAppVersionAddress()); + newFirmware.endAddress(), (int) newFirmware.crc32(), newFirmware.getAppVersionAddress()); logger.info("Updating boot descriptor with {}", newBootDescriptor); dm.programBootDescriptor(newBootDescriptor, cliOptions.delay()); String deviceInfo = cliOptions.progDevice().toString(); From adb58666565fccc268cc54230777c40db344b6d2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 03:22:26 +0200 Subject: [PATCH 056/359] [Updater] Simplify `BootDescriptor.toString()` --- .../selfbus/updater/bootloader/BootDescriptor.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java index df18cf9c..af28e33e 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java @@ -85,15 +85,14 @@ private short long2short(long a) { public String toString() { String res; - if (valid) { - res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X", - ansi().fgBright(GREEN).a(" valid").reset().toString(), long2short(startAddress), long2short(endAddress), length(), crc32); + if (valid()) { + res = ansi().fgBright(GREEN).a(" valid").reset().toString(); } else { - res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X", - ansi().fg(RED).a("invalid").reset().toString(), long2short(startAddress), long2short(endAddress), length(), crc32); + res = ansi().fg(RED).a("invalid").reset().toString(); } - res += String.format(", APP_VERSION pointer: 0x%04X", long2short(appVersionAddress)); + res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X, APP_VERSION pointer: 0x%04X", + res, long2short(startAddress()), long2short(endAddress()), length(), crc32(), long2short(appVersionAddress())); return res; } From cdb75b57fb8402e6db256e3e3c40cb9268666739 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 04:01:51 +0200 Subject: [PATCH 057/359] [Updater] Use `OPT_LONG_DELAY` in warning --- .../source/src/main/java/org/selfbus/updater/CliOptions.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index e4c604e2..fb0f7aaf 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -464,7 +464,8 @@ private void parse(final String[] args) { if (cmdLine.hasOption(OPT_LONG_DELAY)) { delay = ((Number)cmdLine.getParsedOptionValue(OPT_LONG_DELAY)).intValue(); if ((delay < Updater.DELAY_MIN) || (delay > Updater.DELAY_MAX)) { - logger.warn(ansi().fg(RED).a("option --delay {} is invalid (min:{}, max:{}) => setting --delay {}").reset().toString(), + logger.warn(ansi().fg(RED).a( + String.format("option --%s {} is invalid (min:{}, max:{}) => set to {}", OPT_LONG_DELAY)).reset().toString(), delay, Updater.DELAY_MIN, Updater.DELAY_MAX, Updater.DELAY_DEFAULT); delay = Updater.DELAY_DEFAULT; // set to DELAY_DEFAULT in case of invalid waiting time } From d7431740b827595ee10d805a3c201c7bb2dbd070 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 04:05:57 +0200 Subject: [PATCH 058/359] [Updater] Specify the exact number of expected cli arguments --- .../java/org/selfbus/updater/CliOptions.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index fb0f7aaf..ce7ec98e 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -194,102 +194,102 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe Option fileName = Option.builder(OPT_SHORT_FILENAME).longOpt(OPT_LONG_FILENAME) .argName("filename") - .hasArg() + .numberOfArgs(1) .required(false) ///\todo .required(true) leads to an exception with --help or --version .type(String.class) .desc("Filename of hex file to program").build(); Option localhost = Option.builder(OPT_SHORT_LOCALHOST).longOpt(OPT_LONG_LOCALHOST) .argName("localhost") - .hasArg() + .numberOfArgs(1) .required(false) .type(String.class) .desc("local IP/host name").build(); Option localport = Option.builder(OPT_SHORT_LOCALPORT).longOpt(OPT_LONG_LOCALPORT) .argName("localport") - .hasArg() + .numberOfArgs(1) .required(false) .type(Number.class) .desc("local UDP port (default system assigned)").build(); Option port = Option.builder(OPT_SHORT_PORT).longOpt(OPT_LONG_PORT) .argName("port") - .hasArg() + .numberOfArgs(1) .required(false) .type(Number.class) .desc(String.format("UDP port on (default %d)", KNXnetIPConnection.DEFAULT_PORT)).build(); Option ft12 = Option.builder(OPT_SHORT_FT12).longOpt(OPT_LONG_FT12) .argName("COM-port") - .hasArg() + .numberOfArgs(1) .required(false) .desc("use FT1.2 serial communication").build(); Option tpuart = Option.builder(OPT_SHORT_TPUART).longOpt(OPT_LONG_TPUART) .argName("COM-port") - .hasArg() + .numberOfArgs(1) .required(false) .desc("use TPUART serial communication (experimental, needs serialcom or rxtx library in java.library.path)").build(); Option medium = Option.builder(OPT_SHORT_MEDIUM).longOpt(OPT_LONG_MEDIUM) .argName("tp1|rf") - .hasArg() + .numberOfArgs(1) .required(false) .type(TPSettings.class) .desc(String.format("KNX medium [tp1|rf] (default %s)", this.medium)).build(); ///\todo not all implemented missing [tp0|p110|p132] Option optProgDevice = Option.builder(OPT_SHORT_PROG_DEVICE).longOpt(OPT_LONG_PROG_DEVICE) .argName("x.x.x") - .hasArg() + .numberOfArgs(1) .required(false) .type(IndividualAddress.class) .desc(String.format("KNX device address in bootloader mode (default %s)", this.progDevice.toString())).build(); Option device = Option.builder(OPT_SHORT_DEVICE).longOpt(OPT_LONG_DEVICE) .argName("x.x.x") - .hasArg() + .numberOfArgs(1) .required(false) .type(IndividualAddress.class) .desc("KNX device address in normal operating mode (default none)").build(); Option ownPhysicalAddress = Option.builder(OPT_SHORT_OWN_ADDRESS).longOpt(OPT_LONG_OWN_ADDRESS) .argName("x.x.x") - .hasArg() + .numberOfArgs(1) .required(false) .type(IndividualAddress.class) .desc(String.format("own physical KNX address (default %s)", this.ownAddress.toString())).build(); Option uid = Option.builder(OPT_SHORT_UID).longOpt(OPT_LONG_UID) .argName("uid") - .hasArg() + .numberOfArgs(1) .required(false) .desc(String.format("send UID to unlock (default: request UID to unlock). Only the first %d bytes of UID are used", UPDProtocol.UID_LENGTH_USED)).build(); Option delay = Option.builder(null).longOpt(OPT_LONG_DELAY) .argName("ms") - .hasArg() + .numberOfArgs(1) .required(false) .type(Number.class) .desc(String.format("delay telegrams during data transmission to reduce bus load, valid 0-500ms, default %d", Updater.DELAY_MIN)).build(); Option logLevel = Option.builder(OPT_SHORT_LOGLEVEL).longOpt(OPT_LONG_LOGLEVEL) .argName("TRACE|DEBUG|INFO") - .hasArg() + .numberOfArgs(1) .required(false) .type(String.class) .desc(String.format("Logfile logging level [TRACE|DEBUG|INFO] (default %s)", this.logLevel.toString())).build(); Option userId = Option.builder(null).longOpt(OPT_LONG_USER_ID) .argName("id") - .hasArg() + .numberOfArgs(1) .required(false) .type(Number.class) .desc(String.format("KNX IP Secure tunneling user identifier (1..127) (default %d)", this.userId)).build(); Option userPasswd = Option.builder(null).longOpt(OPT_LONG_USER_PASSWORD) .argName("password") - .hasArg() + .numberOfArgs(1) .required(false) .type(Number.class) .desc("KNX IP Secure tunneling user password (Commissioning password/Inbetriebnahmepasswort), quotation marks (\") in password may not work").build(); Option devicePasswd = Option.builder(null).longOpt(OPT_LONG_DEVICE_PASSWORD) .argName("password") - .hasArg() + .numberOfArgs(1) .required(false) .type(Number.class) .desc("KNX IP Secure device authentication code (Authentication Code/Authentifizierungscode) quotation marks(\") in password may not work").build(); Option knxPriority = Option.builder(null).longOpt(OPT_LONG_PRIORITY) .argName("SYSTEM|URGENT|NORMAL|LOW") - .hasArg() + .numberOfArgs(1) .required(false) .type(String.class) .desc(String.format("KNX telegram priority (default %s)", this.priority.toString().toUpperCase())).build(); From 46bce53d07d4cb54bc11990f9b0d4a0e768689fb Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 15:46:00 +0200 Subject: [PATCH 059/359] [Updater] Fix frame icon not loaded while running without an IDE `getResourceAsStream` works always, while `getResource` only works running under an IDE like IDEA. This commit also adds loading of multiple resolutions for the frame icon. --- .../java/org/selfbus/updater/gui/GuiMain.java | 39 ++++++++++++++---- .../{ => frame_images}/selfbus_logo_16x16.png | Bin 2 files changed, 30 insertions(+), 9 deletions(-) rename firmware_updater/updater/source/src/main/resources/{ => frame_images}/selfbus_logo_16x16.png (100%) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 50916e81..1ad91fdc 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -24,6 +24,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.*; @@ -462,21 +463,41 @@ private void fillTelegramPriorityComboBox() { comboBoxKnxTelegramPriority.addItem("SYSTEM"); } + private void setFrameImages() { + String resourceTemplate = "/frame_images/selfbus_logo_%sx%s.png"; + String[] resolutions = {"16"}; // "24", "32", "48", "256"}; //todo enable also these resolutions + List frameImageList = new ArrayList<>(); + + for (String resolution : resolutions) { + String resourceName = String.format(resourceTemplate, resolution, resolution); + InputStream imageStream = this.getClass().getResourceAsStream(resourceName); + if (imageStream == null) { + logger.error("getResourceAsStream({}) failed", resourceName); + continue; + } + + try { + Image image = ImageIO.read(imageStream); + frameImageList.add(image); + logger.debug("Added {} to frameImageList", resourceName); + } + catch (IOException e) { + logger.error("Could not add {} to frameImageList {}", resourceName, Arrays.toString(e.getStackTrace())); + } + } + + if (!frameImageList.isEmpty()) { + this.setIconImages(frameImageList); + } + } + private void createUIComponents() { // TODO: place custom component creation code here comboBoxIpGateways = new JComboBox(); comboBoxMedium = new JComboBox(); comboBoxScenario = new JComboBox(); comboBoxKnxTelegramPriority = new JComboBox(); - - String logoResourceName = "/selfbus_logo_16x16.png"; - try { - Image frameIcon = ImageIO.read(Objects.requireNonNull(this.getClass().getResource(logoResourceName))); - this.setIconImage(frameIcon); - } - catch (IOException e) { - logger.debug("Could not load '{}' {}", logoResourceName, e.toString()); - } + this.setFrameImages(); } public static class ComboItem { diff --git a/firmware_updater/updater/source/src/main/resources/selfbus_logo_16x16.png b/firmware_updater/updater/source/src/main/resources/frame_images/selfbus_logo_16x16.png similarity index 100% rename from firmware_updater/updater/source/src/main/resources/selfbus_logo_16x16.png rename to firmware_updater/updater/source/src/main/resources/frame_images/selfbus_logo_16x16.png From 22af09baa5de8af1a0b12e6eeb2e6a99bf8940c5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 16:00:38 +0200 Subject: [PATCH 060/359] [Updater] Rename package to `org.selfbus.updater.mode.differential.*` The old `org.selfbus.updater.tests.flashdiff.*` was quite confusing, as the differential mode is not really a test. --- .../org/selfbus/updater/FlashDiffMode.java | 2 +- .../differential}/Decompressor.java | 258 ++++----- .../differential}/DecompressorListener.java | 10 +- .../differential}/FlashDiff.java | 510 +++++++++--------- .../differential}/FlashPage.java | 36 +- .../differential}/FlashProgrammer.java | 28 +- .../differential}/OldWindow.java | 40 +- .../differential}/FlashDiffTests.java | 125 +++-- .../differential}/FlashDiffUtils.java | 74 +-- 9 files changed, 540 insertions(+), 543 deletions(-) rename firmware_updater/updater/source/src/main/java/org/selfbus/updater/{tests/flashdiff => mode/differential}/Decompressor.java (96%) rename firmware_updater/updater/source/src/main/java/org/selfbus/updater/{tests/flashdiff => mode/differential}/DecompressorListener.java (66%) rename firmware_updater/updater/source/src/main/java/org/selfbus/updater/{tests/flashdiff => mode/differential}/FlashDiff.java (97%) rename firmware_updater/updater/source/src/main/java/org/selfbus/updater/{tests/flashdiff => mode/differential}/FlashPage.java (86%) rename firmware_updater/updater/source/src/main/java/org/selfbus/updater/{tests/flashdiff => mode/differential}/FlashProgrammer.java (89%) rename firmware_updater/updater/source/src/main/java/org/selfbus/updater/{tests/flashdiff => mode/differential}/OldWindow.java (91%) rename firmware_updater/updater/source/src/test/java/org/selfbus/updater/{tests => mode/differential}/FlashDiffTests.java (93%) rename firmware_updater/updater/source/src/test/java/org/selfbus/updater/{tests => mode/differential}/FlashDiffUtils.java (93%) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 92836ee1..33951b5b 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -2,7 +2,7 @@ import net.harawata.appdirs.AppDirsFactory; import org.selfbus.updater.bootloader.BootDescriptor; -import org.selfbus.updater.tests.flashdiff.FlashDiff; +import org.selfbus.updater.mode.differential.FlashDiff; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; import org.selfbus.updater.upd.UPDProtocol; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/Decompressor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java similarity index 96% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/Decompressor.java rename to firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java index 4a442821..621393fd 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/Decompressor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java @@ -1,129 +1,129 @@ -package org.selfbus.updater.tests.flashdiff; - -import org.selfbus.updater.BinImage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; - -/** - * Apply diff stream and produce one page to be flashed - * (based on diff stream, original ROM content, and RAM buffer to store some latest ROM pages already flashed) - */ -public class Decompressor { - private final static Logger logger = LoggerFactory.getLogger(Decompressor.class.getName()); - private enum State { - EXPECT_COMMAND_BYTE, - EXPECT_COMMAND_PARAMS, - EXPECT_RAW_DATA - } - - private final byte[] cmdBuffer = new byte[5]; - private int expectedCmdLength = 0; - private int cmdBufferLength = 0; - private final byte[] scratchpad = new byte[FlashPage.PAGE_SIZE]; - private int scratchpadIndex; - private int rawLength; - private State state = State.EXPECT_COMMAND_BYTE; - private final DecompressorListener listener; - private final BinImage rom; - private final OldWindow oldPagesRam = new OldWindow(); - - public Decompressor(BinImage rom, DecompressorListener listener) { - this.rom = rom; - this.listener = listener; - } - - private int getLength() { - if ((cmdBuffer[0] & FlashDiff.FLAG_LONG) == FlashDiff.FLAG_LONG) { - return ((cmdBuffer[0] & 0b111111) << 8) | (cmdBuffer[1] & 0xff); - } else { - return (cmdBuffer[0] & 0b111111); - } - } - - private boolean isCopyFromRam() { - if ((cmdBuffer[0] & FlashDiff.FLAG_LONG) == FlashDiff.FLAG_LONG) { - return (cmdBuffer[2] & FlashDiff.ADDR_FROM_RAM) == FlashDiff.ADDR_FROM_RAM; - } else { - return (cmdBuffer[1] & FlashDiff.ADDR_FROM_RAM) == FlashDiff.ADDR_FROM_RAM; - } - } - - private int getCopyAddress() { - if ((cmdBuffer[0] & FlashDiff.FLAG_LONG) == FlashDiff.FLAG_LONG) { - return ((cmdBuffer[2] & 0b1111111) << 16) | ((cmdBuffer[3] & 0xff) << 8) | (cmdBuffer[4] & 0xff); - } else { - return ((cmdBuffer[1] & 0b1111111) << 16) | ((cmdBuffer[2] & 0xff) << 8) | (cmdBuffer[3] & 0xff); - } - } - - private void resetStateMachine() { - state = State.EXPECT_COMMAND_BYTE; - } - - public void pageCompleted() { - listener.flashPage(oldPagesRam, new FlashPage(scratchpad, 0)); - scratchpadIndex = 0; - Arrays.fill(scratchpad, (byte)0); // only get the last (incomplete) page padded with 0 for unit tests, otherwise not relevant - } - - public void putByte(byte data) { - logger.trace("Decompressor processing new byte {}, state={}", (data & 0xff), state); - switch (state) { - case EXPECT_COMMAND_BYTE: - cmdBuffer[0] = data; - cmdBufferLength = 1; - expectedCmdLength = 1; - if ((data & FlashDiff.CMD_COPY) == FlashDiff.CMD_COPY) { - expectedCmdLength += 3; // 3 more bytes of source address - } - if ((data & FlashDiff.FLAG_LONG) == FlashDiff.FLAG_LONG) { - expectedCmdLength += 1; // 1 more byte for longer length - } - if (expectedCmdLength > 1) { - state = State.EXPECT_COMMAND_PARAMS; - } else { - state = State.EXPECT_RAW_DATA; - rawLength = 0; - } - break; - case EXPECT_COMMAND_PARAMS: - cmdBuffer[cmdBufferLength++] = data; - if (cmdBufferLength >= expectedCmdLength) { - // we have all params of the command - if ((cmdBuffer[0] & FlashDiff.CMD_COPY) == FlashDiff.CMD_COPY) { - // perform copy - if (isCopyFromRam()) { - logger.trace("COPY FROM RAM index={} length={} from addr={}", scratchpadIndex, getLength(), getCopyAddress()); - System.arraycopy(oldPagesRam.getOldBinData(), getCopyAddress(), scratchpad, scratchpadIndex, getLength()); - } else { - logger.trace("COPY FROM ROM index={} length={} from addr={}", scratchpadIndex, getLength(), getCopyAddress()); - logger.trace("{}", rom.getBinData()[getCopyAddress()] & 0xff); - logger.trace("{}", rom.getBinData()[getCopyAddress()+1] & 0xff); - logger.trace("{}", rom.getBinData()[getCopyAddress()+2] & 0xff); - System.arraycopy(rom.getBinData(), getCopyAddress(), scratchpad, scratchpadIndex, getLength()); - } - scratchpadIndex += getLength(); - // and finish command - resetStateMachine(); - } else { - // next, read raw data - state = State.EXPECT_RAW_DATA; - rawLength = 0; - } - } // else expect more params of the command - break; - case EXPECT_RAW_DATA: - // store data read to scratchpad - scratchpad[scratchpadIndex++] = data; - rawLength++; - if (rawLength >= getLength()) { - // we have all RAW data, reset state machine - resetStateMachine(); - } - } - } - - -} +package org.selfbus.updater.mode.differential; + +import org.selfbus.updater.BinImage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; + +/** + * Apply diff stream and produce one page to be flashed + * (based on diff stream, original ROM content, and RAM buffer to store some latest ROM pages already flashed) + */ +public class Decompressor { + private final static Logger logger = LoggerFactory.getLogger(Decompressor.class.getName()); + private enum State { + EXPECT_COMMAND_BYTE, + EXPECT_COMMAND_PARAMS, + EXPECT_RAW_DATA + } + + private final byte[] cmdBuffer = new byte[5]; + private int expectedCmdLength = 0; + private int cmdBufferLength = 0; + private final byte[] scratchpad = new byte[FlashPage.PAGE_SIZE]; + private int scratchpadIndex; + private int rawLength; + private State state = State.EXPECT_COMMAND_BYTE; + private final DecompressorListener listener; + private final BinImage rom; + private final OldWindow oldPagesRam = new OldWindow(); + + public Decompressor(BinImage rom, DecompressorListener listener) { + this.rom = rom; + this.listener = listener; + } + + private int getLength() { + if ((cmdBuffer[0] & FlashDiff.FLAG_LONG) == FlashDiff.FLAG_LONG) { + return ((cmdBuffer[0] & 0b111111) << 8) | (cmdBuffer[1] & 0xff); + } else { + return (cmdBuffer[0] & 0b111111); + } + } + + private boolean isCopyFromRam() { + if ((cmdBuffer[0] & FlashDiff.FLAG_LONG) == FlashDiff.FLAG_LONG) { + return (cmdBuffer[2] & FlashDiff.ADDR_FROM_RAM) == FlashDiff.ADDR_FROM_RAM; + } else { + return (cmdBuffer[1] & FlashDiff.ADDR_FROM_RAM) == FlashDiff.ADDR_FROM_RAM; + } + } + + private int getCopyAddress() { + if ((cmdBuffer[0] & FlashDiff.FLAG_LONG) == FlashDiff.FLAG_LONG) { + return ((cmdBuffer[2] & 0b1111111) << 16) | ((cmdBuffer[3] & 0xff) << 8) | (cmdBuffer[4] & 0xff); + } else { + return ((cmdBuffer[1] & 0b1111111) << 16) | ((cmdBuffer[2] & 0xff) << 8) | (cmdBuffer[3] & 0xff); + } + } + + private void resetStateMachine() { + state = State.EXPECT_COMMAND_BYTE; + } + + public void pageCompleted() { + listener.flashPage(oldPagesRam, new FlashPage(scratchpad, 0)); + scratchpadIndex = 0; + Arrays.fill(scratchpad, (byte)0); // only get the last (incomplete) page padded with 0 for unit tests, otherwise not relevant + } + + public void putByte(byte data) { + logger.trace("Decompressor processing new byte {}, state={}", (data & 0xff), state); + switch (state) { + case EXPECT_COMMAND_BYTE: + cmdBuffer[0] = data; + cmdBufferLength = 1; + expectedCmdLength = 1; + if ((data & FlashDiff.CMD_COPY) == FlashDiff.CMD_COPY) { + expectedCmdLength += 3; // 3 more bytes of source address + } + if ((data & FlashDiff.FLAG_LONG) == FlashDiff.FLAG_LONG) { + expectedCmdLength += 1; // 1 more byte for longer length + } + if (expectedCmdLength > 1) { + state = State.EXPECT_COMMAND_PARAMS; + } else { + state = State.EXPECT_RAW_DATA; + rawLength = 0; + } + break; + case EXPECT_COMMAND_PARAMS: + cmdBuffer[cmdBufferLength++] = data; + if (cmdBufferLength >= expectedCmdLength) { + // we have all params of the command + if ((cmdBuffer[0] & FlashDiff.CMD_COPY) == FlashDiff.CMD_COPY) { + // perform copy + if (isCopyFromRam()) { + logger.trace("COPY FROM RAM index={} length={} from addr={}", scratchpadIndex, getLength(), getCopyAddress()); + System.arraycopy(oldPagesRam.getOldBinData(), getCopyAddress(), scratchpad, scratchpadIndex, getLength()); + } else { + logger.trace("COPY FROM ROM index={} length={} from addr={}", scratchpadIndex, getLength(), getCopyAddress()); + logger.trace("{}", rom.getBinData()[getCopyAddress()] & 0xff); + logger.trace("{}", rom.getBinData()[getCopyAddress()+1] & 0xff); + logger.trace("{}", rom.getBinData()[getCopyAddress()+2] & 0xff); + System.arraycopy(rom.getBinData(), getCopyAddress(), scratchpad, scratchpadIndex, getLength()); + } + scratchpadIndex += getLength(); + // and finish command + resetStateMachine(); + } else { + // next, read raw data + state = State.EXPECT_RAW_DATA; + rawLength = 0; + } + } // else expect more params of the command + break; + case EXPECT_RAW_DATA: + // store data read to scratchpad + scratchpad[scratchpadIndex++] = data; + rawLength++; + if (rawLength >= getLength()) { + // we have all RAW data, reset state machine + resetStateMachine(); + } + } + } + + +} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/DecompressorListener.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/DecompressorListener.java similarity index 66% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/DecompressorListener.java rename to firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/DecompressorListener.java index e0f08685..491d74d0 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/DecompressorListener.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/DecompressorListener.java @@ -1,5 +1,5 @@ -package org.selfbus.updater.tests.flashdiff; - -public interface DecompressorListener { - void flashPage(OldWindow oldPagesRam, FlashPage page); -} +package org.selfbus.updater.mode.differential; + +public interface DecompressorListener { + void flashPage(OldWindow oldPagesRam, FlashPage page); +} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java similarity index 97% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java rename to firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index cb4d0d07..bbbc9a8d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -1,255 +1,255 @@ -package org.selfbus.updater.tests.flashdiff; - -import org.selfbus.updater.BinImage; -import org.selfbus.updater.UpdaterException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.KNXRemoteException; -import tuwien.auto.calimero.KNXTimeoutException; -import tuwien.auto.calimero.link.KNXLinkClosedException; -import tuwien.auto.calimero.mgmt.KNXDisconnectException; - -import java.util.ArrayList; -import java.util.List; -import java.util.zip.CRC32; - -import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; - -public class FlashDiff { - private final static Logger logger = LoggerFactory.getLogger(FlashDiff.class.getName()); - private static final int MINIMUM_PATTERN_LENGTH = 6; // less that this is not efficient (metadata would be larger than data) - private static final int MAX_COPY_LENGTH = 2048-1; // 2^12 = 8 bits + 6 bits (remaining in CMD byte). Needs to match flash PAGE_SIZE? - private static final int MAX_LENGTH_SHORT = 64-1; // 2^6 = 6 bits (remaining in CMD byte) - - public static final int CMD_RAW = 0; - public static final int CMD_COPY = 0b10000000; - public static final int FLAG_SHORT = 0; - public static final int FLAG_LONG = 0b01000000; - public static final int ADDR_FROM_ROM = 0; - public static final int ADDR_FROM_RAM = 0b10000000; - - private int totalBytesTransferred = 0; - - enum SourceType { - FORWARD_ROM, - BACKWARD_RAM, - } - - static class SearchResult { - int offset; - int length; - SourceType sourceType = SourceType.FORWARD_ROM; - - public SearchResult(int logestCandidateSrcOffset, int logestCandidateLength) { - this.offset = logestCandidateSrcOffset; - this.length = logestCandidateLength; - } - - @Override - public String toString() { - return "SearchResult{" + - "offset=" + offset + - ", length=" + length + - ", sourceType=" + sourceType + - '}'; - } - } - - public SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOffset, int oldDataMinimumAddr, int maxLength) { - // search as long as possible ar2[beginOffset..n] bytes (pattern) common with ar1[s..t], where n and t are up to length-1 of appropriate arrays and s is unknown - int logestCandidateSrcOffset = 0; - int logestCandidateLength = 0; - for (int i = oldDataMinimumAddr; i < ar1.length; i++) { - int j = 0; - while ((patternOffset + j < ar2.length) - && (i + j < ar1.length) - && (ar1[i + j] == ar2[patternOffset + j]) - && j < maxLength - ) { - j++; - } - if (j > logestCandidateLength) { - // found better (or first) solution - logestCandidateLength = j; - logestCandidateSrcOffset = i; - } - } - if (logestCandidateLength > 0) { - int firstDstPage = patternOffset/FlashPage.PAGE_SIZE; - int lastDstPage = (patternOffset+logestCandidateLength-1)/FlashPage.PAGE_SIZE; - if (lastDstPage != firstDstPage) { - // truncate to a single destination page - logestCandidateLength = (firstDstPage + 1) * FlashPage.PAGE_SIZE - patternOffset; - } - if (logestCandidateLength >= MINIMUM_PATTERN_LENGTH) { - logger.trace("{}", ar1[logestCandidateSrcOffset] & 0xff); - logger.trace("{}", ar1[logestCandidateSrcOffset + 1] & 0xff); - logger.trace("{}", ar1[logestCandidateSrcOffset + 2] & 0xff); - } - return new SearchResult(logestCandidateSrcOffset, logestCandidateLength); - } else { - return new SearchResult(0, 0); - } - } - - private int possiblyFinishRawBuffer(List rawBuffer, List outputDiffStream) { - int outSize; - if (!rawBuffer.isEmpty()) { - byte cmdByte = CMD_RAW; - if (rawBuffer.size() <= MAX_LENGTH_SHORT) { - debug("RAW BUFFER SHORT size=" + rawBuffer.size()); - cmdByte = (byte)(cmdByte | FLAG_SHORT); - cmdByte = (byte)(cmdByte | (rawBuffer.size() & 0b111111)); // command + 6 low bits of the length - outputDiffStream.add(cmdByte); - outputDiffStream.addAll(rawBuffer); - outSize = 1 + rawBuffer.size(); // 1 byte = cmd with short length included - rawBuffer.clear(); - return outSize; - } else { - debug("RAW BUFFER LONG size=" + rawBuffer.size()); - cmdByte = (byte)(cmdByte | FLAG_LONG); - cmdByte = (byte)(cmdByte | ((rawBuffer.size() >> 8) & 0b111111)); // command + 6 high bits of the length - byte lengthLowByte = (byte)(rawBuffer.size() & 0xff); // 8 low bits of the length - outputDiffStream.add(cmdByte); - outputDiffStream.add(lengthLowByte); - outputDiffStream.addAll(rawBuffer); - outSize = 2 + rawBuffer.size(); // 2 bytes = cmd (6bits of length) + length (8bits of length) - rawBuffer.clear(); - return outSize; - } - } else { - return 0; - } - } - - public int getTotalBytesTransferred() { - return totalBytesTransferred; - } - - public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flashProgrammer) - throws InterruptedException, KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, UpdaterException { - // TODO check if old image can be truncated for smaller new image, first test was fine - //BinImage img1 = new BinImage(img1Orig); // make copy to keep img1Orig untouched, this fails if arraycopy at 203, when new image is larger than old - BinImage img1 = new BinImage(img1Orig,img2.getBinData().length); // make copy to keep img1Orig untouched, ensure old bin buffer is same size as new bin file - - List outputDiffStream = new ArrayList<>(); - List rawBuffer = new ArrayList<>(); - OldWindow w = new OldWindow(); - CRC32 crc32Block = new CRC32(); - int i = 0; - int size = 0; - int pages = 0; - totalBytesTransferred = 0; - while (i < img2.getBinData().length) { - SearchResult rBackwardRamWindow = letLongestCommonBytes(w.getOldBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); - rBackwardRamWindow.sourceType = SourceType.BACKWARD_RAM; - //SearchResult rBackwardRamWindow = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0); // in case we would have two flash banks, ie. full old image available - int currentPage = i / FlashPage.PAGE_SIZE; - int firstAddressInThisPage = currentPage * FlashPage.PAGE_SIZE; - SearchResult rForwardOldFlash = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); - rForwardOldFlash.sourceType = SourceType.FORWARD_ROM; - // which result is better, from FORWARD ROM or BACKWARD RAM? - SearchResult bestResult = (rForwardOldFlash.length > rBackwardRamWindow.length) ? rForwardOldFlash : rBackwardRamWindow; - if (bestResult.length >= MINIMUM_PATTERN_LENGTH) { - size += possiblyFinishRawBuffer(rawBuffer, outputDiffStream); - logger.trace("{} bestResult={}", String.format("%08x ", i), bestResult); - i += bestResult.length; - size += 1; - byte cmdByte = (byte)CMD_COPY; - if (bestResult.length <= MAX_LENGTH_SHORT) { - cmdByte = (byte)(cmdByte | FLAG_SHORT | (bestResult.length & 0b111111)); // command + 6 bits of the length - debug("@ b=%02X i=%d CMD_COPY", (cmdByte & 0xff), i); - outputDiffStream.add(cmdByte); - } else { - cmdByte = (byte)(cmdByte | FLAG_LONG | ((bestResult.length >> 8) & 0b111111)); // command + 6 bits from high byte of the length - debug("@ b=%02X i=%d CMD_COPY FLAG_LONG", (cmdByte & 0xff), i); - byte lengthLowByte = (byte)(bestResult.length & 0xff); // 8 low bits of the length - outputDiffStream.add(cmdByte); - size += 1; - outputDiffStream.add(lengthLowByte); - } - // 3 bytes are enough to address ROM or RAM buffer, the highest bit indicates ROM or RAM source - byte addr1 = (byte)(bestResult.offset & 0xff); // low byte - byte addr2 = (byte)((bestResult.offset >> 8) & 0xff); // middle byte - byte addr3 = (byte)((bestResult.offset >> 16) & 0xff); // high byte - int addrFromFlag = bestResult.sourceType == SourceType.BACKWARD_RAM ? ADDR_FROM_RAM : ADDR_FROM_ROM; - if (bestResult.sourceType == SourceType.BACKWARD_RAM) { - debug(" DO COPY FROM RAM l=%d sa=%08X", bestResult.length, bestResult.offset); - } else { - debug(" DO COPY FROM ROM l=%d sa=%08X", bestResult.length, bestResult.offset); - } - byte[] srcData = bestResult.sourceType == SourceType.BACKWARD_RAM ? w.getOldBinData() : img1.getBinData(); - for (int k = 0; k < bestResult.length; k++) { - if (k % 16 == 0) { - debug("\n "); - } - debug("%02X ", (srcData[bestResult.offset + k] & 0xff)); - } - size += 3; - addr3 = (byte)(addr3 | addrFromFlag); - outputDiffStream.add(addr3); - outputDiffStream.add(addr2); - outputDiffStream.add(addr1); - debug("\n"); - } - else { - logger.trace("{} RAW: {}", String.format("%08x", i), String.format("%02x", img2.getBinData()[i])); - - debug("@ b=%02X i=%d raw", (img2.getBinData()[i] & 0xff), i); - rawBuffer.add(img2.getBinData()[i]); - i++; - debug("\n"); - } - if (i%FlashPage.PAGE_SIZE == 0) { - // passed to new page - size += possiblyFinishRawBuffer(rawBuffer, outputDiffStream); - crc32Block.reset(); - crc32Block.update(img2.getBinData(), i - FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); - //p = new FlashPage(img1.getBinData(), i); - - debug("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE); - for (int k = pages*FlashPage.PAGE_SIZE; (k < (pages+1)*FlashPage.PAGE_SIZE && k < img2.getBinData().length); k++) { - if (k % 16 == 0) { - debug("\n "); - } - debug(String.format("%02X ", (img2.getBinData()[k] & 0xff))); - } - debug("\n"); - - logger.debug("Page {}, ",pages); - flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); - outputDiffStream.clear(); - pages++; - // emulate we have loaded the original page from ROM to RAM and written new page to ROM - w.fillNextPage(img1.getBinData(), i - FlashPage.PAGE_SIZE); // backup old data from ROM to RAM - System.arraycopy(img2.getBinData(), i - FlashPage.PAGE_SIZE, img1.getBinData(), i - FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); // emulatae write of new data to ROM - } - } - size += possiblyFinishRawBuffer(rawBuffer, outputDiffStream); - if (!outputDiffStream.isEmpty()) { - crc32Block.reset(); - crc32Block.update(img2.getBinData(), img2.getBinData().length - (img2.getBinData().length % FlashPage.PAGE_SIZE), img2.getBinData().length % FlashPage.PAGE_SIZE); - - debug("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE); - debug("\n "); - for (int k = pages*FlashPage.PAGE_SIZE; (k < (pages+1)*FlashPage.PAGE_SIZE && k < img2.getBinData().length); k++) { - debug("%02X ", (img2.getBinData()[k] & 0xff)); - if (k % 16 == 0) { - debug("\n "); - } - } - debug("\n"); - - logger.debug("Page {}, ", pages); - flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); - totalBytesTransferred = size; - logger.debug("OK! Total diff stream length={} bytes", ansi().fgBright(GREEN).a(size).reset().toString()); - } - //dumpSideBySide(img1, img2); - } - - protected void debug(String format, Object... args) { - logger.trace(String.format(format, args)); - } -} +package org.selfbus.updater.mode.differential; + +import org.selfbus.updater.BinImage; +import org.selfbus.updater.UpdaterException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tuwien.auto.calimero.KNXRemoteException; +import tuwien.auto.calimero.KNXTimeoutException; +import tuwien.auto.calimero.link.KNXLinkClosedException; +import tuwien.auto.calimero.mgmt.KNXDisconnectException; + +import java.util.ArrayList; +import java.util.List; +import java.util.zip.CRC32; + +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; + +public class FlashDiff { + private final static Logger logger = LoggerFactory.getLogger(FlashDiff.class.getName()); + private static final int MINIMUM_PATTERN_LENGTH = 6; // less that this is not efficient (metadata would be larger than data) + private static final int MAX_COPY_LENGTH = 2048-1; // 2^12 = 8 bits + 6 bits (remaining in CMD byte). Needs to match flash PAGE_SIZE? + private static final int MAX_LENGTH_SHORT = 64-1; // 2^6 = 6 bits (remaining in CMD byte) + + public static final int CMD_RAW = 0; + public static final int CMD_COPY = 0b10000000; + public static final int FLAG_SHORT = 0; + public static final int FLAG_LONG = 0b01000000; + public static final int ADDR_FROM_ROM = 0; + public static final int ADDR_FROM_RAM = 0b10000000; + + private int totalBytesTransferred = 0; + + enum SourceType { + FORWARD_ROM, + BACKWARD_RAM, + } + + static class SearchResult { + int offset; + int length; + SourceType sourceType = SourceType.FORWARD_ROM; + + public SearchResult(int logestCandidateSrcOffset, int logestCandidateLength) { + this.offset = logestCandidateSrcOffset; + this.length = logestCandidateLength; + } + + @Override + public String toString() { + return "SearchResult{" + + "offset=" + offset + + ", length=" + length + + ", sourceType=" + sourceType + + '}'; + } + } + + public SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOffset, int oldDataMinimumAddr, int maxLength) { + // search as long as possible ar2[beginOffset..n] bytes (pattern) common with ar1[s..t], where n and t are up to length-1 of appropriate arrays and s is unknown + int logestCandidateSrcOffset = 0; + int logestCandidateLength = 0; + for (int i = oldDataMinimumAddr; i < ar1.length; i++) { + int j = 0; + while ((patternOffset + j < ar2.length) + && (i + j < ar1.length) + && (ar1[i + j] == ar2[patternOffset + j]) + && j < maxLength + ) { + j++; + } + if (j > logestCandidateLength) { + // found better (or first) solution + logestCandidateLength = j; + logestCandidateSrcOffset = i; + } + } + if (logestCandidateLength > 0) { + int firstDstPage = patternOffset/FlashPage.PAGE_SIZE; + int lastDstPage = (patternOffset+logestCandidateLength-1)/FlashPage.PAGE_SIZE; + if (lastDstPage != firstDstPage) { + // truncate to a single destination page + logestCandidateLength = (firstDstPage + 1) * FlashPage.PAGE_SIZE - patternOffset; + } + if (logestCandidateLength >= MINIMUM_PATTERN_LENGTH) { + logger.trace("{}", ar1[logestCandidateSrcOffset] & 0xff); + logger.trace("{}", ar1[logestCandidateSrcOffset + 1] & 0xff); + logger.trace("{}", ar1[logestCandidateSrcOffset + 2] & 0xff); + } + return new SearchResult(logestCandidateSrcOffset, logestCandidateLength); + } else { + return new SearchResult(0, 0); + } + } + + private int possiblyFinishRawBuffer(List rawBuffer, List outputDiffStream) { + int outSize; + if (!rawBuffer.isEmpty()) { + byte cmdByte = CMD_RAW; + if (rawBuffer.size() <= MAX_LENGTH_SHORT) { + debug("RAW BUFFER SHORT size=" + rawBuffer.size()); + cmdByte = (byte)(cmdByte | FLAG_SHORT); + cmdByte = (byte)(cmdByte | (rawBuffer.size() & 0b111111)); // command + 6 low bits of the length + outputDiffStream.add(cmdByte); + outputDiffStream.addAll(rawBuffer); + outSize = 1 + rawBuffer.size(); // 1 byte = cmd with short length included + rawBuffer.clear(); + return outSize; + } else { + debug("RAW BUFFER LONG size=" + rawBuffer.size()); + cmdByte = (byte)(cmdByte | FLAG_LONG); + cmdByte = (byte)(cmdByte | ((rawBuffer.size() >> 8) & 0b111111)); // command + 6 high bits of the length + byte lengthLowByte = (byte)(rawBuffer.size() & 0xff); // 8 low bits of the length + outputDiffStream.add(cmdByte); + outputDiffStream.add(lengthLowByte); + outputDiffStream.addAll(rawBuffer); + outSize = 2 + rawBuffer.size(); // 2 bytes = cmd (6bits of length) + length (8bits of length) + rawBuffer.clear(); + return outSize; + } + } else { + return 0; + } + } + + public int getTotalBytesTransferred() { + return totalBytesTransferred; + } + + public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flashProgrammer) + throws InterruptedException, KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, UpdaterException { + // TODO check if old image can be truncated for smaller new image, first test was fine + //BinImage img1 = new BinImage(img1Orig); // make copy to keep img1Orig untouched, this fails if arraycopy at 203, when new image is larger than old + BinImage img1 = new BinImage(img1Orig,img2.getBinData().length); // make copy to keep img1Orig untouched, ensure old bin buffer is same size as new bin file + + List outputDiffStream = new ArrayList<>(); + List rawBuffer = new ArrayList<>(); + OldWindow w = new OldWindow(); + CRC32 crc32Block = new CRC32(); + int i = 0; + int size = 0; + int pages = 0; + totalBytesTransferred = 0; + while (i < img2.getBinData().length) { + SearchResult rBackwardRamWindow = letLongestCommonBytes(w.getOldBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); + rBackwardRamWindow.sourceType = SourceType.BACKWARD_RAM; + //SearchResult rBackwardRamWindow = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0); // in case we would have two flash banks, ie. full old image available + int currentPage = i / FlashPage.PAGE_SIZE; + int firstAddressInThisPage = currentPage * FlashPage.PAGE_SIZE; + SearchResult rForwardOldFlash = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); + rForwardOldFlash.sourceType = SourceType.FORWARD_ROM; + // which result is better, from FORWARD ROM or BACKWARD RAM? + SearchResult bestResult = (rForwardOldFlash.length > rBackwardRamWindow.length) ? rForwardOldFlash : rBackwardRamWindow; + if (bestResult.length >= MINIMUM_PATTERN_LENGTH) { + size += possiblyFinishRawBuffer(rawBuffer, outputDiffStream); + logger.trace("{} bestResult={}", String.format("%08x ", i), bestResult); + i += bestResult.length; + size += 1; + byte cmdByte = (byte)CMD_COPY; + if (bestResult.length <= MAX_LENGTH_SHORT) { + cmdByte = (byte)(cmdByte | FLAG_SHORT | (bestResult.length & 0b111111)); // command + 6 bits of the length + debug("@ b=%02X i=%d CMD_COPY", (cmdByte & 0xff), i); + outputDiffStream.add(cmdByte); + } else { + cmdByte = (byte)(cmdByte | FLAG_LONG | ((bestResult.length >> 8) & 0b111111)); // command + 6 bits from high byte of the length + debug("@ b=%02X i=%d CMD_COPY FLAG_LONG", (cmdByte & 0xff), i); + byte lengthLowByte = (byte)(bestResult.length & 0xff); // 8 low bits of the length + outputDiffStream.add(cmdByte); + size += 1; + outputDiffStream.add(lengthLowByte); + } + // 3 bytes are enough to address ROM or RAM buffer, the highest bit indicates ROM or RAM source + byte addr1 = (byte)(bestResult.offset & 0xff); // low byte + byte addr2 = (byte)((bestResult.offset >> 8) & 0xff); // middle byte + byte addr3 = (byte)((bestResult.offset >> 16) & 0xff); // high byte + int addrFromFlag = bestResult.sourceType == SourceType.BACKWARD_RAM ? ADDR_FROM_RAM : ADDR_FROM_ROM; + if (bestResult.sourceType == SourceType.BACKWARD_RAM) { + debug(" DO COPY FROM RAM l=%d sa=%08X", bestResult.length, bestResult.offset); + } else { + debug(" DO COPY FROM ROM l=%d sa=%08X", bestResult.length, bestResult.offset); + } + byte[] srcData = bestResult.sourceType == SourceType.BACKWARD_RAM ? w.getOldBinData() : img1.getBinData(); + for (int k = 0; k < bestResult.length; k++) { + if (k % 16 == 0) { + debug("\n "); + } + debug("%02X ", (srcData[bestResult.offset + k] & 0xff)); + } + size += 3; + addr3 = (byte)(addr3 | addrFromFlag); + outputDiffStream.add(addr3); + outputDiffStream.add(addr2); + outputDiffStream.add(addr1); + debug("\n"); + } + else { + logger.trace("{} RAW: {}", String.format("%08x", i), String.format("%02x", img2.getBinData()[i])); + + debug("@ b=%02X i=%d raw", (img2.getBinData()[i] & 0xff), i); + rawBuffer.add(img2.getBinData()[i]); + i++; + debug("\n"); + } + if (i%FlashPage.PAGE_SIZE == 0) { + // passed to new page + size += possiblyFinishRawBuffer(rawBuffer, outputDiffStream); + crc32Block.reset(); + crc32Block.update(img2.getBinData(), i - FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); + //p = new FlashPage(img1.getBinData(), i); + + debug("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE); + for (int k = pages*FlashPage.PAGE_SIZE; (k < (pages+1)*FlashPage.PAGE_SIZE && k < img2.getBinData().length); k++) { + if (k % 16 == 0) { + debug("\n "); + } + debug(String.format("%02X ", (img2.getBinData()[k] & 0xff))); + } + debug("\n"); + + logger.debug("Page {}, ",pages); + flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); + outputDiffStream.clear(); + pages++; + // emulate we have loaded the original page from ROM to RAM and written new page to ROM + w.fillNextPage(img1.getBinData(), i - FlashPage.PAGE_SIZE); // backup old data from ROM to RAM + System.arraycopy(img2.getBinData(), i - FlashPage.PAGE_SIZE, img1.getBinData(), i - FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); // emulatae write of new data to ROM + } + } + size += possiblyFinishRawBuffer(rawBuffer, outputDiffStream); + if (!outputDiffStream.isEmpty()) { + crc32Block.reset(); + crc32Block.update(img2.getBinData(), img2.getBinData().length - (img2.getBinData().length % FlashPage.PAGE_SIZE), img2.getBinData().length % FlashPage.PAGE_SIZE); + + debug("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE); + debug("\n "); + for (int k = pages*FlashPage.PAGE_SIZE; (k < (pages+1)*FlashPage.PAGE_SIZE && k < img2.getBinData().length); k++) { + debug("%02X ", (img2.getBinData()[k] & 0xff)); + if (k % 16 == 0) { + debug("\n "); + } + } + debug("\n"); + + logger.debug("Page {}, ", pages); + flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); + totalBytesTransferred = size; + logger.debug("OK! Total diff stream length={} bytes", ansi().fgBright(GREEN).a(size).reset().toString()); + } + //dumpSideBySide(img1, img2); + } + + protected void debug(String format, Object... args) { + logger.trace(String.format(format, args)); + } +} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashPage.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java similarity index 86% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashPage.java rename to firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java index ce42a945..c6b10385 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashPage.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java @@ -1,18 +1,18 @@ -package org.selfbus.updater.tests.flashdiff; - -import org.selfbus.updater.Mcu; - -import java.util.Arrays; - -public class FlashPage { - public static final int PAGE_SIZE = Mcu.FLASH_PAGE_SIZE; - private byte[] old; // old content before patching - - public FlashPage(byte[] fromByteArray, int begin) { - old = Arrays.copyOfRange(fromByteArray, begin, begin + PAGE_SIZE); - } - - public byte[] getOldBinData() { - return old; - } -} +package org.selfbus.updater.mode.differential; + +import org.selfbus.updater.Mcu; + +import java.util.Arrays; + +public class FlashPage { + public static final int PAGE_SIZE = Mcu.FLASH_PAGE_SIZE; + private byte[] old; // old content before patching + + public FlashPage(byte[] fromByteArray, int begin) { + old = Arrays.copyOfRange(fromByteArray, begin, begin + PAGE_SIZE); + } + + public byte[] getOldBinData() { + return old; + } +} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashProgrammer.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java similarity index 89% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashProgrammer.java rename to firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java index dc6e4e57..6998044c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/FlashProgrammer.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java @@ -1,14 +1,14 @@ -package org.selfbus.updater.tests.flashdiff; - -import org.selfbus.updater.UpdaterException; -import tuwien.auto.calimero.KNXRemoteException; -import tuwien.auto.calimero.KNXTimeoutException; -import tuwien.auto.calimero.link.KNXLinkClosedException; -import tuwien.auto.calimero.mgmt.KNXDisconnectException; - -import java.util.List; - -public interface FlashProgrammer { - - void sendCompressedPage(List outputDiffStream, long crc32) throws InterruptedException, KNXTimeoutException, KNXLinkClosedException, KNXRemoteException, KNXDisconnectException, UpdaterException; -} +package org.selfbus.updater.mode.differential; + +import org.selfbus.updater.UpdaterException; +import tuwien.auto.calimero.KNXRemoteException; +import tuwien.auto.calimero.KNXTimeoutException; +import tuwien.auto.calimero.link.KNXLinkClosedException; +import tuwien.auto.calimero.mgmt.KNXDisconnectException; + +import java.util.List; + +public interface FlashProgrammer { + + void sendCompressedPage(List outputDiffStream, long crc32) throws InterruptedException, KNXTimeoutException, KNXLinkClosedException, KNXRemoteException, KNXDisconnectException, UpdaterException; +} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/OldWindow.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java similarity index 91% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/OldWindow.java rename to firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java index eaa2f11a..22deafd5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/tests/flashdiff/OldWindow.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java @@ -1,20 +1,20 @@ -package org.selfbus.updater.tests.flashdiff; - -public class OldWindow { - public static final int PAGES = 2; - private byte[] old = new byte[PAGES * FlashPage.PAGE_SIZE]; - - public OldWindow() { - } - - public byte[] getOldBinData() { - return old; - } - - public void fillNextPage(byte[] binData, int pageBaseAddress) { - // shift old data one page back, will leave one "empty" page at the end - System.arraycopy( old, FlashPage.PAGE_SIZE, old, 0, old.length - FlashPage.PAGE_SIZE ); - // fill the last (current) page (may not be while page when EOF) at the end - System.arraycopy( binData, pageBaseAddress, old, old.length - FlashPage.PAGE_SIZE , Math.min(FlashPage.PAGE_SIZE, binData.length - pageBaseAddress) ); - } -} +package org.selfbus.updater.mode.differential; + +public class OldWindow { + public static final int PAGES = 2; + private byte[] old = new byte[PAGES * FlashPage.PAGE_SIZE]; + + public OldWindow() { + } + + public byte[] getOldBinData() { + return old; + } + + public void fillNextPage(byte[] binData, int pageBaseAddress) { + // shift old data one page back, will leave one "empty" page at the end + System.arraycopy( old, FlashPage.PAGE_SIZE, old, 0, old.length - FlashPage.PAGE_SIZE ); + // fill the last (current) page (may not be while page when EOF) at the end + System.arraycopy( binData, pageBaseAddress, old, old.length - FlashPage.PAGE_SIZE , Math.min(FlashPage.PAGE_SIZE, binData.length - pageBaseAddress) ); + } +} diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java similarity index 93% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java rename to firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java index 7c641f25..6f548210 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffTests.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java @@ -1,64 +1,61 @@ -package org.selfbus.updater.tests; - -import org.junit.jupiter.api.Test; -import org.selfbus.updater.BinImage; -import org.selfbus.updater.UpdaterException; -import org.selfbus.updater.tests.flashdiff.Decompressor; -import org.selfbus.updater.tests.flashdiff.FlashDiff; -import org.selfbus.updater.tests.flashdiff.FlashPage; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicInteger; - -public class FlashDiffTests { - private void performTest(BinImage img1, BinImage img2) { - int pages1 = (int) Math.ceil(img2.getBinData().length / (double) FlashPage.PAGE_SIZE); - int pages2 = (int) Math.ceil(img2.getBinData().length / (double)FlashPage.PAGE_SIZE); - int pagesMax = Math.max(pages1, pages2); - BinImage rom = new BinImage(img1, pagesMax * FlashPage.PAGE_SIZE); - BinImage img2wholePages = new BinImage(img2, pagesMax * FlashPage.PAGE_SIZE); - FlashDiff differ = new FlashDiff(); - AtomicInteger pageNumber = new AtomicInteger(); - Decompressor decompressor = new Decompressor(rom, (oldPagesRam, page) -> { - // backup page to be flashed to RAM - oldPagesRam.fillNextPage(rom.getBinData(), pageNumber.get() * FlashPage.PAGE_SIZE); - // process decompressed page - System.out.println("Page decompressed"); - // emulate ROM page flash (rom is our ROM in the MCU) - System.arraycopy(page.getOldBinData(), 0, rom.getBinData(), pageNumber.get() * FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); - FlashDiffUtils.dumpSideBySide(img2wholePages.getBinData(), rom.getBinData(), pageNumber.get() * FlashPage.PAGE_SIZE); - pageNumber.incrementAndGet(); - //System.exit(1); - }); - // TODO -// differ.generateDiff(img1, img2, outputDiffStream -> { -// // process compressed page -// for (byte b : outputDiffStream) { -// decompressor.putByte(b); -// } -// decompressor.pageCompleted(); -// }); - } - - @Test - public void testDiff() throws URISyntaxException { - // test of upgrade from old version to newer (longer) - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - URI uri1 = Objects.requireNonNull(contextClassLoader.getResource("updater.ino.slto.v1.hex")).toURI(); - URI uri2 = Objects.requireNonNull(contextClassLoader.getResource("updater.ino.slto.v2.hex")).toURI(); - BinImage img1 = BinImage.readFromHex(uri1.getPath()); - BinImage img2 = BinImage.readFromHex(uri2.getPath()); - performTest(img1, img2); - } - - @Test - public void testDiff2() throws URISyntaxException, UpdaterException { - // test of new firmware into empty MCU - URI uri2 = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("updater.ino.slto.v2.hex")).toURI(); - BinImage img2 = BinImage.readFromHex(uri2.getPath()); - BinImage img1 = BinImage.dummyFilled(img2.getBinData().length, 0xff); - performTest(img1, img2); - } -} +package org.selfbus.updater.mode.differential; + +import org.junit.jupiter.api.Test; +import org.selfbus.updater.BinImage; +import org.selfbus.updater.UpdaterException; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; + +public class FlashDiffTests { + private void performTest(BinImage img1, BinImage img2) { + int pages1 = (int) Math.ceil(img2.getBinData().length / (double) FlashPage.PAGE_SIZE); + int pages2 = (int) Math.ceil(img2.getBinData().length / (double)FlashPage.PAGE_SIZE); + int pagesMax = Math.max(pages1, pages2); + BinImage rom = new BinImage(img1, pagesMax * FlashPage.PAGE_SIZE); + BinImage img2wholePages = new BinImage(img2, pagesMax * FlashPage.PAGE_SIZE); + FlashDiff differ = new FlashDiff(); + AtomicInteger pageNumber = new AtomicInteger(); + Decompressor decompressor = new Decompressor(rom, (oldPagesRam, page) -> { + // backup page to be flashed to RAM + oldPagesRam.fillNextPage(rom.getBinData(), pageNumber.get() * FlashPage.PAGE_SIZE); + // process decompressed page + System.out.println("Page decompressed"); + // emulate ROM page flash (rom is our ROM in the MCU) + System.arraycopy(page.getOldBinData(), 0, rom.getBinData(), pageNumber.get() * FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); + FlashDiffUtils.dumpSideBySide(img2wholePages.getBinData(), rom.getBinData(), pageNumber.get() * FlashPage.PAGE_SIZE); + pageNumber.incrementAndGet(); + //System.exit(1); + }); + // TODO +// differ.generateDiff(img1, img2, outputDiffStream -> { +// // process compressed page +// for (byte b : outputDiffStream) { +// decompressor.putByte(b); +// } +// decompressor.pageCompleted(); +// }); + } + + @Test + public void testDiff() throws URISyntaxException { + // test of upgrade from old version to newer (longer) + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + URI uri1 = Objects.requireNonNull(contextClassLoader.getResource("updater.ino.slto.v1.hex")).toURI(); + URI uri2 = Objects.requireNonNull(contextClassLoader.getResource("updater.ino.slto.v2.hex")).toURI(); + BinImage img1 = BinImage.readFromHex(uri1.getPath()); + BinImage img2 = BinImage.readFromHex(uri2.getPath()); + performTest(img1, img2); + } + + @Test + public void testDiff2() throws URISyntaxException, UpdaterException { + // test of new firmware into empty MCU + URI uri2 = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("updater.ino.slto.v2.hex")).toURI(); + BinImage img2 = BinImage.readFromHex(uri2.getPath()); + BinImage img1 = BinImage.dummyFilled(img2.getBinData().length, 0xff); + performTest(img1, img2); + } +} diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffUtils.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java similarity index 93% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffUtils.java rename to firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java index 17d5d2a0..c3446f9d 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/tests/FlashDiffUtils.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java @@ -1,37 +1,37 @@ -package org.selfbus.updater.tests; - -import static org.junit.jupiter.api.Assertions.fail; -import org.selfbus.updater.BinImage; - -public class FlashDiffUtils { - - public static boolean isEqual(byte[] a1, byte[] a2, int begin, int size) { - for(int i=begin;i Date: Thu, 8 Aug 2024 17:09:21 +0200 Subject: [PATCH 061/359] [Updater] Fix use UTF-8 file format --- .../resources/GuiTranslation_de.properties | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties index 6fd5b588..935ca721 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties @@ -6,63 +6,63 @@ scenario=Szenario uid=UID startFlash=Starte Flashvorgang language=Sprache -newDevice=neues Gerät -appDevice=Gerät mit Applikation +newDevice=neues Gerät +appDevice=Gerät mit Applikation allOptions=alle Optionen -newDeviceHint=Es wurde auf ein Selfbus Gerät der Bootloader geflasht
\ -und das Gerät wurde in den programmierbaren Modus versetzt. -appDeviceHint=Das Gerät wurde bereits mit einer Applikation
\ -über den Bootloader geflasht und soll nun aktualisiert werden.
\ +newDeviceHint=Es wurde auf ein Selfbus Gerät der Bootloader geflasht
\ +und das Gerät wurde in den programmierbaren Modus versetzt. +appDeviceHint=Das Gerät wurde bereits mit einer Applikation
\ +über den Bootloader geflasht und soll nun aktualisiert werden.
\ Dazu muss die physikalische Adresse und das Applikationsprogramm
\ in der ETS geschrieben worden sein. eraseCompleteFlashHint=MIT VORSICHT VERWENDEN! Es wird der komplette
\ -Speicher gelöscht, inklusive der physikalischen KNX Adresse
\ +Speicher gelöscht, inklusive der physikalischen KNX Adresse
\ und allen Einstellungen. Nur der Bootloader bleibt bestehen. noFlashHint=Flashen der Firmware deaktivieren -uidHint=die UID des Selfbus Gerätes +uidHint=die UID des Selfbus Gerätes fileNameHint=Der Dateipfad kann manuell bearbeitet werden
\ -(Enter zum bestätigen) +(Enter zum bestätigen) ipAddrHint=IP Adresse der KNX Schnittstelle portHint=TCP/UDP Portnummer der KNX Schnittstelle (default 3671) stopFlash=Stoppe Flashvorgang medium=Medium serial=Seriell tpuart=TpUart -knxDeviceAddr=Geräteadresse -knxProgDeviceAddr=Geräteadresse im Bootloader +knxDeviceAddr=Geräteadresse +knxProgDeviceAddr=Geräteadresse im Bootloader knxOwnAddress=eigene KNX Adresse port=Port useNat=NAT -knxMessageDelay=Verzögerung [ms] +knxMessageDelay=Verzögerung [ms] knxTimeout=Timeout -eraseFlash=Lösche kompletten Flash -noFlash=keine Daten übertragen -requestUid=Erfasse UID von Gerät -requestUidHint=Die UID wird von einem Selfbus Gerät abgefragt
\ -und angezeigt. Das Selfbus Gerät muss sich
\ +eraseFlash=Lösche kompletten Flash +noFlash=keine Daten übertragen +requestUid=Erfasse UID von Gerät +requestUidHint=Die UID wird von einem Selfbus Gerät abgefragt
\ +und angezeigt. Das Selfbus Gerät muss sich
\ im programmierbaren Modus im Bootloader befinden UpdaterSettings=Updater KnxBusSettings=KNX Bus KnxGatewayConnectionSettings=KNX Schnittstelle advancedSettings=Erweiterte Einstellungen knxSecureUserPwd=Benutzer Passwort -messagePriority=KNX Telegramm Priorität +messagePriority=KNX Telegramm Priorität knxSecureUser=KNX Secure Benutzer knxSecureUserHint=KNX IP Secure tunneling Benutzer Identifikator
\ (1..127) (default 1) knxSecureUserPwdHint=\ KNX IP Secure tunneling Benutzer Passwort
\ -(Inbetriebnahmepasswort), Anführungszeichen
\ -(") im Passwort könnten nicht funktionieren\ +(Inbetriebnahmepasswort), Anführungszeichen
\ +(") im Passwort könnten nicht funktionieren\ -knxSecureDevicePwd=Gerätepasswort +knxSecureDevicePwd=Gerätepasswort knxSecureDevicePwdHint=\ -KNX IP Secure Geräte Authentifizierung Code
\ -(Authentifizierungscode), Anführungszeichen
\ -(") im Passwort könnten nicht funktionieren\ +KNX IP Secure Geräte Authentifizierung Code
\ +(Authentifizierungscode), Anführungszeichen
\ +(") im Passwort könnten nicht funktionieren\ reloadKnxIpGateways=Gateways erneut laden -selectInterface=wähle Schnittstelle +selectInterface=wähle Schnittstelle diffFlash=differenzieller Flashvorgang -diffFlashHint=Es werden nur veränderte Teile der Software
\ -übertragen [experimentell] \ No newline at end of file +diffFlashHint=Es werden nur veränderte Teile der Software
\ +übertragen [experimentell] \ No newline at end of file From 2713a5774b6a99f99d836f01f31ffe3739780c9f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 17:21:07 +0200 Subject: [PATCH 062/359] [Updater] Simplify translations and start/stop/uid button actions - Replace all `ResourceBundle bundle` with new field `GuiMain.guiTranslation` - Since Updater v1.20 uid can be queried without `--fileName` - Reduce usage of `try catch`, otherwise we may miss some important exceptions --- .../java/org/selfbus/updater/gui/GuiMain.java | 112 +++++++----------- .../main/resources/GuiTranslation.properties | 3 +- .../resources/GuiTranslation_de.properties | 3 +- .../resources/GuiTranslation_en.properties | 3 +- 4 files changed, 47 insertions(+), 74 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 1ad91fdc..9f470bd5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -93,6 +93,7 @@ public class GuiMain extends JFrame { private JScrollPane mainScrollPane; private JButton reloadGatewaysButton; + private final ResourceBundle guiTranslation; private CliOptions cliOptions; private Thread updaterThread; public static GuiMain guiMainInstance; @@ -102,6 +103,7 @@ public class GuiMain extends JFrame { public GuiMain() { $$$setupUI$$$(); + guiTranslation = ResourceBundle.getBundle("GuiTranslation"); buttonLoadFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -125,45 +127,36 @@ public void actionPerformed(ActionEvent e) { buttonStartStopFlash.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { + String stopFlash = guiTranslation.getString("stopFlash"); + String startFlash = guiTranslation.getString("startFlash"); + + if (Objects.equals(buttonStartStopFlash.getText(), stopFlash)) { + if (updaterThread != null) { + updaterThread.interrupt(); + buttonStartStopFlash.setText(startFlash); + logger.info(guiTranslation.getString("logMessageCanceledFlashing")); + return; + } + } - ResourceBundle bundle = ResourceBundle.getBundle("GuiTranslation"); - - if (Objects.equals(buttonStartStopFlash.getText(), bundle.getString("startFlash"))) { - jLoggingPane.setText(""); - - updaterThread = new Thread() { - public void run() { - try { - setCliOptions(); - } catch (KNXFormatException | ParseException ex) { - throw new RuntimeException(ex); - } - - try { - final Updater d = new Updater(cliOptions); - - //final ShutdownHandler sh = new ShutdownHandler().register(); - d.run(); - SwingUtilities.invokeLater(() -> { - guiMainInstance.buttonStartStopFlash.setText(bundle.getString("startFlash")); - }); - //sh.unregister(); - } catch (final Throwable t) { - logger.error("parsing options ", t); - } finally { - logger.info("\n\n"); - logger.debug("main exit"); - } + jLoggingPane.setText(""); + updaterThread = new Thread() { + public void run() { + try { + setCliOptions(); + } catch (KNXFormatException | ParseException ex) { + throw new RuntimeException(ex); } - }; - updaterThread.start(); - - buttonStartStopFlash.setText(bundle.getString("stopFlash")); - } else { - updaterThread.interrupt(); //.stop() is deprecated - buttonStartStopFlash.setText(bundle.getString("startFlash")); - } + final Updater d = new Updater(cliOptions); + d.run(); + SwingUtilities.invokeLater(() -> { + guiMainInstance.buttonStartStopFlash.setText(startFlash); + }); + } + }; + updaterThread.start(); + buttonStartStopFlash.setText(stopFlash); } }); @@ -173,33 +166,15 @@ public void actionPerformed(ActionEvent e) { updaterThread = new Thread(() -> { try { - // für den bestehenden Updater wird zwingend ein Dateiname benötigt - if (textFieldFileName.getText().isEmpty()) { - textFieldFileName.setText("dummy.hex"); - } setCliOptions(); - if (Objects.equals(textFieldFileName.getText(), "dummy.hex")) { - textFieldFileName.setText(""); - } } catch (KNXFormatException | ParseException ex) { throw new RuntimeException(ex); } - - try { - final Updater upd = new Updater(cliOptions); - - String uid = upd.requestUid(); - - SwingUtilities.invokeLater(() -> { - guiMainInstance.textFieldUid.setText(uid); - }); - - } catch (final Throwable t) { - logger.error("parsing options ", t); - } finally { - logger.info("\n\n"); - logger.debug("main exit"); - } + final Updater upd = new Updater(cliOptions); + String uid = upd.requestUid(); + SwingUtilities.invokeLater(() -> { + guiMainInstance.textFieldUid.setText(uid); + }); }); updaterThread.start(); } @@ -428,8 +403,7 @@ private void LoadKnxIpInterfacesAndFillComboBox() { comboBoxIpGateways.removeAllItems(); - ResourceBundle bundle = ResourceBundle.getBundle("GuiTranslation"); - comboBoxIpGateways.addItem(new CalimeroSearchComboItem(bundle.getString("selectInterface"), null)); + comboBoxIpGateways.addItem(new CalimeroSearchComboItem(guiTranslation.getString("selectInterface"), null)); DiscoverKnxInterfaces.getAllInterfaces().forEach(r -> comboBoxIpGateways.addItem(new CalimeroSearchComboItem(r.response().getDevice().getName() + @@ -441,12 +415,9 @@ private void LoadKnxIpInterfacesAndFillComboBox() { private void fillScenarios() { if (comboBoxScenario == null) return; - - ResourceBundle bundle = ResourceBundle.getBundle("GuiTranslation"); - - comboBoxScenario.addItem(new ComboItem(bundle.getString(GuiObjsVisOpts.NEWDEV.getVisOption()), GuiObjsVisOpts.NEWDEV)); - comboBoxScenario.addItem(new ComboItem(bundle.getString(GuiObjsVisOpts.APPDEV.getVisOption()), GuiObjsVisOpts.APPDEV)); - comboBoxScenario.addItem(new ComboItem(bundle.getString(GuiObjsVisOpts.REQUID.getVisOption()), GuiObjsVisOpts.REQUID)); + comboBoxScenario.addItem(new ComboItem(guiTranslation.getString(GuiObjsVisOpts.NEWDEV.getVisOption()), GuiObjsVisOpts.NEWDEV)); + comboBoxScenario.addItem(new ComboItem(guiTranslation.getString(GuiObjsVisOpts.APPDEV.getVisOption()), GuiObjsVisOpts.APPDEV)); + comboBoxScenario.addItem(new ComboItem(guiTranslation.getString(GuiObjsVisOpts.REQUID.getVisOption()), GuiObjsVisOpts.REQUID)); } private void fillMediumComboBox() { @@ -634,19 +605,18 @@ private void InitGuiElementsVisibility() { } private void setGuiElementsVisibility() { - ResourceBundle bundle = ResourceBundle.getBundle("GuiTranslation"); final GuiObjsVisOpts selectedScenario = (GuiObjsVisOpts) ((ComboItem) Objects.requireNonNull(comboBoxScenario.getSelectedItem())).getValue(); switch (selectedScenario) { case NEWDEV: - labelScenarioHint.setText(bundle.getString("newDeviceHint")); + labelScenarioHint.setText(guiTranslation.getString("newDeviceHint")); break; case APPDEV: - labelScenarioHint.setText(bundle.getString("appDeviceHint")); + labelScenarioHint.setText(guiTranslation.getString("appDeviceHint")); break; case REQUID: - labelScenarioHint.setText(bundle.getString("requestUidHint")); + labelScenarioHint.setText(guiTranslation.getString("requestUidHint")); break; } diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties index de2feb82..828ce52f 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties @@ -61,4 +61,5 @@ in password may not work\ reloadKnxIpGateways=reload gateways selectInterface=select Interface diffFlash=differential Flash -diffFlashHint=flash only changed parts of software
\\ [experimental] \ No newline at end of file +diffFlashHint=flash only changed parts of software
\\ [experimental] +logMessageCanceledFlashing=Flash process canceled. \ No newline at end of file diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties index 935ca721..fcab99b9 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties @@ -65,4 +65,5 @@ reloadKnxIpGateways=Gateways erneut laden selectInterface=wähle Schnittstelle diffFlash=differenzieller Flashvorgang diffFlashHint=Es werden nur veränderte Teile der Software
\ -übertragen [experimentell] \ No newline at end of file +übertragen [experimentell] +logMessageCanceledFlashing=Flashvorgang wurde abgebrochen. \ No newline at end of file diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties index d93661fd..96e8a5b6 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties @@ -62,4 +62,5 @@ reloadKnxIpGateways=reload gateways selectInterface=select Interface diffFlash=differential Flash diffFlashHint=flash only changed parts of software
\ -[experimental] \ No newline at end of file +[experimental] +logMessageCanceledFlashing=Flash process canceled. \ No newline at end of file From ba3b8cab886253e5a80a3c4b1099a4c8bba2cff3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 17:46:22 +0200 Subject: [PATCH 063/359] [Updater] Move `Settings for GUI development` to `README.md` --- firmware_updater/updater/README.md | 19 +++++++++++++++---- .../source/Settings for GUI development.txt | 7 ------- 2 files changed, 15 insertions(+), 11 deletions(-) delete mode 100644 firmware_updater/updater/source/Settings for GUI development.txt diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index e264f0f2..51f42903 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -93,10 +93,21 @@ Manual specification of parameters if the App-Version pointer is not found/integ ``` java -jar SB_updater-x.xx-all.jar -fileName "in16-bim112.hex" -appVersionPtr 0x3263 -uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 -nat ``` -## Used IDE's: -IntelliJ IDEA Community 2023.3.4 (Build -> Build Artifacts)
-eclipse project is currently not maintained -## gradle: +## Development + +### IDEs: +- [IntelliJ IDEA (Community Edition)](https://www.jetbrains.com/idea/download) +- Eclipse project is currently not maintained + +### IntelliJ IDEA Settings for Updater GUI development: +Change these in [**Settings Dialog**](https://www.jetbrains.com/help/idea/settings-preferences-dialog.html) (Menu File->Settings): +- Editor->[GUI Designer](https://www.jetbrains.com/help/idea/gui-designer.html)->Generate GUI into: Java source code +- [Plugins](https://www.jetbrains.com/help/idea/plugins-settings.html)->install "Resource Bundle Editor" [(howto)](https://www.jetbrains.com/help/idea/resource-bundle.html#open-bundle-editor) +- Build, Execution, Deployment->[Build Tools](https://www.jetbrains.com/help/idea/settings-build-tools.html)->Gradle->Build and run using: Intellij IDEA +- Build, Execution, Deployment->[Build Tools](https://www.jetbrains.com/help/idea/settings-build-tools.html)->Gradle->Run tests using: Intellij IDEA + + +### gradle: update [gradle wrapper](source/gradle/wrapper) to the newest version: ``` gradlew wrapper diff --git a/firmware_updater/updater/source/Settings for GUI development.txt b/firmware_updater/updater/source/Settings for GUI development.txt deleted file mode 100644 index ce099e4c..00000000 --- a/firmware_updater/updater/source/Settings for GUI development.txt +++ /dev/null @@ -1,7 +0,0 @@ -Intellij IDEA - -File -> Settings -> Editor -> GUI Designer -> Generate GUI into: Java source code -File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Build and run using: Intellij IDEA -File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Run tests using: Intellij IDEA - -Plugings -> install "Resource Bundle Editor" (start with highlight resources->Resource Bundle and press F4) \ No newline at end of file From 30c3ab2552ba53ab307d24d3fc279ca525689190 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 18:04:31 +0200 Subject: [PATCH 064/359] [Updater] Fix missing `knxSecureUser` EN translation --- .../updater/source/src/main/resources/GuiTranslation.properties | 2 +- .../source/src/main/resources/GuiTranslation_de.properties | 2 +- .../source/src/main/resources/GuiTranslation_en.properties | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties index 828ce52f..393bc682 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties @@ -43,7 +43,7 @@ KnxBusSettings=KNX bus settings UpdaterSettings=Updater settings advancedSettings=Advanced settings messagePriority=KNX telegram priority -knxSecureUser=KNX Secure User +knxSecureUser=KNX IP-Secure User knxSecureUserHint=KNX IP Secure tunneling user identifier
\ (1..127) (default 1) knxSecureUserPwd=User password diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties index fcab99b9..a88fcd46 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties @@ -47,7 +47,7 @@ KnxGatewayConnectionSettings=KNX Schnittstelle advancedSettings=Erweiterte Einstellungen knxSecureUserPwd=Benutzer Passwort messagePriority=KNX Telegramm Priorität -knxSecureUser=KNX Secure Benutzer +knxSecureUser=KNX IP-Secure Benutzer knxSecureUserHint=KNX IP Secure tunneling Benutzer Identifikator
\ (1..127) (default 1) knxSecureUserPwdHint=\ diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties index 96e8a5b6..94ffe20d 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties @@ -45,6 +45,7 @@ KnxGatewayConnectionSettings=KNX gateway connection settings advancedSettings=Advanced settings knxSecureUserPwd=User password messagePriority=KNX telegram priority +knxSecureUser=KNX IP-Secure User knxSecureUserHint=KNX IP Secure tunneling user identifier
\ (1..127) (default 1) knxSecureUserPwdHint=\ From da4008452c0fecc0cd108f3485aa0fdb529938ac Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 18:19:26 +0200 Subject: [PATCH 065/359] [Updater] Add `log` folder to `.gitignore` --- firmware_updater/updater/source/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware_updater/updater/source/.gitignore b/firmware_updater/updater/source/.gitignore index 3f874f51..8d3bf67a 100644 --- a/firmware_updater/updater/source/.gitignore +++ b/firmware_updater/updater/source/.gitignore @@ -1,4 +1,5 @@ .idea .gradle build +log out From dd7477f233c1a32bcfb8753fc7e17d4d8c89ee8c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 18:25:02 +0200 Subject: [PATCH 066/359] [Updater] Move `KNXNetworkLink` logging to `openLink()` This also avoids the IDEA project error `similar log messages` --- .../src/main/java/org/selfbus/updater/SBKNXLink.java | 10 ++++++++-- .../src/main/java/org/selfbus/updater/Updater.java | 2 -- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index c2759fe3..bda1f106 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -93,8 +93,14 @@ public void setCliOptions(CliOptions cliOptions) { * @throws KNXException on problems on link creation * @throws InterruptedException on interrupted thread */ - public KNXNetworkLink openLink() throws KNXException, - InterruptedException, UpdaterException { + public KNXNetworkLink openLink() throws KNXException, InterruptedException, UpdaterException { + KNXNetworkLink newLink = doOpenLink(); + logger.info("KNX connection: {}", newLink); + return newLink; + } + + private KNXNetworkLink doOpenLink() throws KNXException, + InterruptedException { final KNXMediumSettings medium = getMedium(cliOptions.medium(), cliOptions.ownAddress()); logger.debug("Creating KNX network link {}", medium); if (!cliOptions.ft12().isEmpty()) { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index d33ffd07..220feef4 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -207,7 +207,6 @@ public void run() { DeviceManagement dm = new DeviceManagement(link, cliOptions.progDevice(), cliOptions.priority()); - logger.info("KNX connection: {}", link); logger.debug("Telegram priority: {}", cliOptions.priority()); //for option --device restart the device in bootloader mode @@ -381,7 +380,6 @@ public String requestUid(){ try { link = this.sbKNXLink.openLink(); DeviceManagement dm = new DeviceManagement(link, cliOptions.progDevice(), cliOptions.priority()); - logger.info("KNX connection: {}", link); //for option --device restart the device in bootloader mode if (cliOptions.device() != null) { // phys. knx address of the device in normal operation From d8da70d55a010c471159155281a5ade9f3945596 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 18:37:05 +0200 Subject: [PATCH 067/359] [Updater] Fix `expected` and `actual` mixup in `assertEquals` --- .../test/java/org/selfbus/updater/UtilsTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java index 6913b68b..d6845697 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java @@ -34,7 +34,7 @@ public void testStreamToLong() { long expectedLong = l + (k << 8) + (j << 16) + (i << 24); long result; result = Utils.streamToLong(testStream, 0); - assertEquals(result, expectedLong); + assertEquals(expectedLong, result); } } } @@ -58,7 +58,7 @@ public void testLongToStream() { for (int i = 0; i < underTest.length; i++) { Utils.longToStream(testStream, 0, underTest[i]); expectedLong = Utils.streamToLong(testStream, 0); - assertEquals(underTest[i], expectedLong); + assertEquals(expectedLong, underTest[i]); } } @@ -81,26 +81,26 @@ public void testShortenPath() { // test windows path testFileName = "C:\\Users\\user\\AppData\\Local\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin"; testFileName = Utils.shortenPath(testFileName, thresholdToTest + 1); - assertEquals(testFileName, "C:\\...\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin"); + assertEquals("C:\\...\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin", testFileName); // test linux path testFileName = Utils.shortenPath("/home/username/.cache/Selfbus-Updater/0.61/image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertEquals(testFileName, "/.../.cache/Selfbus-Updater/0.61/image-0x7000-34624-0xe8b27ade.bin"); + assertEquals("/.../.cache/Selfbus-Updater/0.61/image-0x7000-34624-0xe8b27ade.bin", testFileName); // test UNC path testFileName = Utils.shortenPath("\\\\share\\funstuff\\running\\from\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertEquals(testFileName, "\\\\...\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin"); + assertEquals("\\\\...\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin", testFileName); // test http testFileName = Utils.shortenPath("http://tester.com/a1/b2/c3/d4/image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertEquals(testFileName, "http://.../b2/c3/d4/image-0x7000-34624-0xe8b27ade.bin"); + assertEquals("http://.../b2/c3/d4/image-0x7000-34624-0xe8b27ade.bin", testFileName); // test ftp testFileName = Utils.shortenPath("ftp://tester.com/a1/b2/c3/d4/e5/image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertEquals(testFileName, "ftp://.../c3/d4/e5/image-0x7000-34624-0xe8b27ade.bin"); + assertEquals("ftp://.../c3/d4/e5/image-0x7000-34624-0xe8b27ade.bin", testFileName); // test nothing to shorten testFileName = Utils.shortenPath("C:\\Local\\Selfbus\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin", thresholdToTest); - assertEquals(testFileName, testFileName); + assertEquals("C:\\...\\Selfbus-Updater\\Cache\\0.61\\image-0x7000-34624-0xe8b27ade.bin", testFileName); } } \ No newline at end of file From 1c74e4a961854d21500a2824222bd1598650eeb4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 18:39:34 +0200 Subject: [PATCH 068/359] [Updater] Delete empty tests --- .../java/org/selfbus/updater/ToolInfoTest.java | 18 ------------------ .../java/org/selfbus/updater/UtilsTest.java | 12 ------------ 2 files changed, 30 deletions(-) delete mode 100644 firmware_updater/updater/source/src/test/java/org/selfbus/updater/ToolInfoTest.java diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/ToolInfoTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/ToolInfoTest.java deleted file mode 100644 index d0b25100..00000000 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/ToolInfoTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.selfbus.updater; - -import org.junit.jupiter.api.Test; - -public class ToolInfoTest { - - @Test - public void testGetVersion() { - } - - @Test - public void testGetAuthor() { - } - - @Test - public void testGetTool() { - } -} \ No newline at end of file diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java index d6845697..4b013ca8 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java @@ -62,18 +62,6 @@ public void testLongToStream() { } } - @Test - public void testByteArrayToHex() { - } - - @Test - public void testParseHost() { - } - - @Test - public void testFileExists() { - } - @Test public void testShortenPath() { int thresholdToTest = 4; From 02312b69762ad7ceac5a1f5bb6e52b5564797f61 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 18:46:31 +0200 Subject: [PATCH 069/359] [Updater] Fix double `;;` --- .../source/src/main/java/org/selfbus/updater/ProgressInfo.java | 2 +- .../test/java/org/selfbus/updater/BootloaderIdentityTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java index 03f6a2a0..4e126ef5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java @@ -26,7 +26,7 @@ public ProgressInfo(long totalByteCount) { totalBytes = totalByteCount; bytesDone = 0; startTimeMs = System.currentTimeMillis(); - lastUpdateTimeMs = startTimeMs;; + lastUpdateTimeMs = startTimeMs; } public void update(long byteCount) { diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java index e0210889..f5a9eb02 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.Test; import org.selfbus.updater.bootloader.BootloaderIdentity; -import static org.junit.jupiter.api.Assertions.*;; +import static org.junit.jupiter.api.Assertions.*; public class BootloaderIdentityTest { From fece57faffb7b88faa4bb4eb2e9e4ee3565069ec Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 18:51:40 +0200 Subject: [PATCH 070/359] [Updater] Fields can be `final` --- .../source/src/main/java/org/selfbus/updater/BinImage.java | 2 +- .../java/org/selfbus/updater/mode/differential/FlashPage.java | 2 +- .../java/org/selfbus/updater/mode/differential/OldWindow.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java index 372804ed..e0e1f6d8 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java @@ -22,7 +22,7 @@ public class BinImage { private final static Logger logger = LoggerFactory.getLogger(BinImage.class.getName()); - private byte[] binData; + private final byte[] binData; private final long startAddress; private final long endAddress; private long crc32; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java index c6b10385..87630ddd 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java @@ -6,7 +6,7 @@ public class FlashPage { public static final int PAGE_SIZE = Mcu.FLASH_PAGE_SIZE; - private byte[] old; // old content before patching + private final byte[] old; // old content before patching public FlashPage(byte[] fromByteArray, int begin) { old = Arrays.copyOfRange(fromByteArray, begin, begin + PAGE_SIZE); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java index 22deafd5..a3cce585 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java @@ -2,7 +2,7 @@ public class OldWindow { public static final int PAGES = 2; - private byte[] old = new byte[PAGES * FlashPage.PAGE_SIZE]; + private final byte[] old = new byte[PAGES * FlashPage.PAGE_SIZE]; public OldWindow() { } From 58cf150219eabd739b1077b08fc7b37cdb86b7df Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 19:00:42 +0200 Subject: [PATCH 071/359] [Updater] Invert 'if' statement --- .../updater/mode/differential/FlashDiff.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index bbbc9a8d..8b421fff 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -95,32 +95,32 @@ public SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOff private int possiblyFinishRawBuffer(List rawBuffer, List outputDiffStream) { int outSize; - if (!rawBuffer.isEmpty()) { - byte cmdByte = CMD_RAW; - if (rawBuffer.size() <= MAX_LENGTH_SHORT) { - debug("RAW BUFFER SHORT size=" + rawBuffer.size()); - cmdByte = (byte)(cmdByte | FLAG_SHORT); - cmdByte = (byte)(cmdByte | (rawBuffer.size() & 0b111111)); // command + 6 low bits of the length - outputDiffStream.add(cmdByte); - outputDiffStream.addAll(rawBuffer); - outSize = 1 + rawBuffer.size(); // 1 byte = cmd with short length included - rawBuffer.clear(); - return outSize; - } else { - debug("RAW BUFFER LONG size=" + rawBuffer.size()); - cmdByte = (byte)(cmdByte | FLAG_LONG); - cmdByte = (byte)(cmdByte | ((rawBuffer.size() >> 8) & 0b111111)); // command + 6 high bits of the length - byte lengthLowByte = (byte)(rawBuffer.size() & 0xff); // 8 low bits of the length - outputDiffStream.add(cmdByte); - outputDiffStream.add(lengthLowByte); - outputDiffStream.addAll(rawBuffer); - outSize = 2 + rawBuffer.size(); // 2 bytes = cmd (6bits of length) + length (8bits of length) - rawBuffer.clear(); - return outSize; - } - } else { + if (rawBuffer.isEmpty()) { return 0; } + + byte cmdByte = CMD_RAW; + if (rawBuffer.size() <= MAX_LENGTH_SHORT) { + debug("RAW BUFFER SHORT size=" + rawBuffer.size()); + cmdByte = (byte)(cmdByte | FLAG_SHORT); + cmdByte = (byte)(cmdByte | (rawBuffer.size() & 0b111111)); // command + 6 low bits of the length + outputDiffStream.add(cmdByte); + outputDiffStream.addAll(rawBuffer); + outSize = 1 + rawBuffer.size(); // 1 byte = cmd with short length included + rawBuffer.clear(); + return outSize; + } else { + debug("RAW BUFFER LONG size=" + rawBuffer.size()); + cmdByte = (byte)(cmdByte | FLAG_LONG); + cmdByte = (byte)(cmdByte | ((rawBuffer.size() >> 8) & 0b111111)); // command + 6 high bits of the length + byte lengthLowByte = (byte)(rawBuffer.size() & 0xff); // 8 low bits of the length + outputDiffStream.add(cmdByte); + outputDiffStream.add(lengthLowByte); + outputDiffStream.addAll(rawBuffer); + outSize = 2 + rawBuffer.size(); // 2 bytes = cmd (6bits of length) + length (8bits of length) + rawBuffer.clear(); + return outSize; + } } public int getTotalBytesTransferred() { From 92a93defa16aacebea9b7b6ceb916f5d92fb5359 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 19:09:46 +0200 Subject: [PATCH 072/359] [Updater] Simplify `if` by moving same code outside of it --- .../updater/mode/differential/FlashDiff.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index 8b421fff..2b0efb3f 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -94,33 +94,32 @@ public SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOff } private int possiblyFinishRawBuffer(List rawBuffer, List outputDiffStream) { - int outSize; if (rawBuffer.isEmpty()) { return 0; } - byte cmdByte = CMD_RAW; + int outSize; + byte cmdByte; if (rawBuffer.size() <= MAX_LENGTH_SHORT) { debug("RAW BUFFER SHORT size=" + rawBuffer.size()); - cmdByte = (byte)(cmdByte | FLAG_SHORT); + cmdByte = (byte)(CMD_RAW | FLAG_SHORT); cmdByte = (byte)(cmdByte | (rawBuffer.size() & 0b111111)); // command + 6 low bits of the length outputDiffStream.add(cmdByte); - outputDiffStream.addAll(rawBuffer); - outSize = 1 + rawBuffer.size(); // 1 byte = cmd with short length included - rawBuffer.clear(); - return outSize; - } else { + outSize = 1; // 1 byte = cmd with short length included + } + else { debug("RAW BUFFER LONG size=" + rawBuffer.size()); - cmdByte = (byte)(cmdByte | FLAG_LONG); + cmdByte = (byte)(CMD_RAW | FLAG_LONG); cmdByte = (byte)(cmdByte | ((rawBuffer.size() >> 8) & 0b111111)); // command + 6 high bits of the length byte lengthLowByte = (byte)(rawBuffer.size() & 0xff); // 8 low bits of the length outputDiffStream.add(cmdByte); outputDiffStream.add(lengthLowByte); - outputDiffStream.addAll(rawBuffer); - outSize = 2 + rawBuffer.size(); // 2 bytes = cmd (6bits of length) + length (8bits of length) - rawBuffer.clear(); - return outSize; + outSize = 2; // 2 bytes = cmd (6bits of length) + length (8bits of length) } + outputDiffStream.addAll(rawBuffer); + outSize += rawBuffer.size(); + rawBuffer.clear(); + return outSize; } public int getTotalBytesTransferred() { From daf30890e1e30e044cdc2e9149b2827a7beb7de7 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 19:46:47 +0200 Subject: [PATCH 073/359] [Updater] Fix differential mode is not an error --- .../source/src/main/java/org/selfbus/updater/Updater.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 220feef4..aa6026dd 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -310,7 +310,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd ResponseResult resultTotal; logger.info(ansi().bg(GREEN).fg(BLACK).a("Starting to send new firmware now:").reset().toString()); if (diffMode && FlashDiffMode.isInitialized()) { - logger.error(ansi().fgBright(RED).a("Differential mode is EXPERIMENTAL -> Use with caution.").reset().toString()); + logger.warn(ansi().fgBright(RED).a("Differential mode is EXPERIMENTAL -> Use with caution.").reset().toString()); resultTotal = FlashDiffMode.doDifferentialFlash(dm, newFirmware.startAddress(), newFirmware.getBinData()); } else { From 43a7a5afa7db5a7623b9d71d2063263bbac2a610 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 20:11:19 +0200 Subject: [PATCH 074/359] [Updater] Use `logger.trace` in `FlashDiff` --- .../updater/mode/differential/FlashDiff.java | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index 2b0efb3f..76ecf17c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -83,9 +83,8 @@ public SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOff logestCandidateLength = (firstDstPage + 1) * FlashPage.PAGE_SIZE - patternOffset; } if (logestCandidateLength >= MINIMUM_PATTERN_LENGTH) { - logger.trace("{}", ar1[logestCandidateSrcOffset] & 0xff); - logger.trace("{}", ar1[logestCandidateSrcOffset + 1] & 0xff); - logger.trace("{}", ar1[logestCandidateSrcOffset + 2] & 0xff); + logger.trace("{} {} {}", ar1[logestCandidateSrcOffset] & 0xff, ar1[logestCandidateSrcOffset + 1] & 0xff, + ar1[logestCandidateSrcOffset + 2] & 0xff); } return new SearchResult(logestCandidateSrcOffset, logestCandidateLength); } else { @@ -101,14 +100,14 @@ private int possiblyFinishRawBuffer(List rawBuffer, List outputDiffS int outSize; byte cmdByte; if (rawBuffer.size() <= MAX_LENGTH_SHORT) { - debug("RAW BUFFER SHORT size=" + rawBuffer.size()); + logger.trace("RAW BUFFER SHORT size={}", rawBuffer.size()); cmdByte = (byte)(CMD_RAW | FLAG_SHORT); cmdByte = (byte)(cmdByte | (rawBuffer.size() & 0b111111)); // command + 6 low bits of the length outputDiffStream.add(cmdByte); outSize = 1; // 1 byte = cmd with short length included } else { - debug("RAW BUFFER LONG size=" + rawBuffer.size()); + logger.trace("RAW BUFFER LONG size={}", rawBuffer.size()); cmdByte = (byte)(CMD_RAW | FLAG_LONG); cmdByte = (byte)(cmdByte | ((rawBuffer.size() >> 8) & 0b111111)); // command + 6 high bits of the length byte lengthLowByte = (byte)(rawBuffer.size() & 0xff); // 8 low bits of the length @@ -158,11 +157,11 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash byte cmdByte = (byte)CMD_COPY; if (bestResult.length <= MAX_LENGTH_SHORT) { cmdByte = (byte)(cmdByte | FLAG_SHORT | (bestResult.length & 0b111111)); // command + 6 bits of the length - debug("@ b=%02X i=%d CMD_COPY", (cmdByte & 0xff), i); + logger.trace(String.format("@ b=%02X i=%d CMD_COPY", (cmdByte & 0xff), i)); outputDiffStream.add(cmdByte); } else { cmdByte = (byte)(cmdByte | FLAG_LONG | ((bestResult.length >> 8) & 0b111111)); // command + 6 bits from high byte of the length - debug("@ b=%02X i=%d CMD_COPY FLAG_LONG", (cmdByte & 0xff), i); + logger.trace(String.format("@ b=%02X i=%d CMD_COPY FLAG_LONG", (cmdByte & 0xff), i)); byte lengthLowByte = (byte)(bestResult.length & 0xff); // 8 low bits of the length outputDiffStream.add(cmdByte); size += 1; @@ -173,32 +172,35 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash byte addr2 = (byte)((bestResult.offset >> 8) & 0xff); // middle byte byte addr3 = (byte)((bestResult.offset >> 16) & 0xff); // high byte int addrFromFlag = bestResult.sourceType == SourceType.BACKWARD_RAM ? ADDR_FROM_RAM : ADDR_FROM_ROM; + String location; if (bestResult.sourceType == SourceType.BACKWARD_RAM) { - debug(" DO COPY FROM RAM l=%d sa=%08X", bestResult.length, bestResult.offset); + location = "RAM"; } else { - debug(" DO COPY FROM ROM l=%d sa=%08X", bestResult.length, bestResult.offset); + location = "ROM"; } + logger.trace(String.format("DO COPY FROM %s l=%d sa=%08X",location , bestResult.length, bestResult.offset)); byte[] srcData = bestResult.sourceType == SourceType.BACKWARD_RAM ? w.getOldBinData() : img1.getBinData(); + StringBuilder traceMessage = new StringBuilder(); for (int k = 0; k < bestResult.length; k++) { if (k % 16 == 0) { - debug("\n "); + logger.trace(traceMessage.toString()); + traceMessage = new StringBuilder(); } - debug("%02X ", (srcData[bestResult.offset + k] & 0xff)); + traceMessage.append(String.format("%02X ", (srcData[bestResult.offset + k] & 0xff))); } + logger.trace(traceMessage.toString()); + size += 3; addr3 = (byte)(addr3 | addrFromFlag); outputDiffStream.add(addr3); outputDiffStream.add(addr2); outputDiffStream.add(addr1); - debug("\n"); } else { - logger.trace("{} RAW: {}", String.format("%08x", i), String.format("%02x", img2.getBinData()[i])); - - debug("@ b=%02X i=%d raw", (img2.getBinData()[i] & 0xff), i); + logger.trace(String.format("%08x RAW: %02x@ b=%02X i=%d raw", i, img2.getBinData()[i], + (img2.getBinData()[i] & 0xff), i)); rawBuffer.add(img2.getBinData()[i]); i++; - debug("\n"); } if (i%FlashPage.PAGE_SIZE == 0) { // passed to new page @@ -207,16 +209,18 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash crc32Block.update(img2.getBinData(), i - FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); //p = new FlashPage(img1.getBinData(), i); - debug("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE); + logger.trace(String.format("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE)); + StringBuilder traceMessage = new StringBuilder(); for (int k = pages*FlashPage.PAGE_SIZE; (k < (pages+1)*FlashPage.PAGE_SIZE && k < img2.getBinData().length); k++) { if (k % 16 == 0) { - debug("\n "); + logger.trace(traceMessage.toString()); + traceMessage = new StringBuilder(); } - debug(String.format("%02X ", (img2.getBinData()[k] & 0xff))); + traceMessage.append(String.format("%02X ", (img2.getBinData()[k] & 0xff))); } - debug("\n"); + logger.trace(traceMessage.toString()); - logger.debug("Page {}, ",pages); + logger.trace("Page {}, ",pages); flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); outputDiffStream.clear(); pages++; @@ -230,25 +234,22 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash crc32Block.reset(); crc32Block.update(img2.getBinData(), img2.getBinData().length - (img2.getBinData().length % FlashPage.PAGE_SIZE), img2.getBinData().length % FlashPage.PAGE_SIZE); - debug("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE); - debug("\n "); + logger.trace(String.format("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE)); + StringBuilder traceMessage = new StringBuilder(); for (int k = pages*FlashPage.PAGE_SIZE; (k < (pages+1)*FlashPage.PAGE_SIZE && k < img2.getBinData().length); k++) { - debug("%02X ", (img2.getBinData()[k] & 0xff)); + traceMessage.append(String.format("%02X ", (img2.getBinData()[k] & 0xff))); if (k % 16 == 0) { - debug("\n "); + logger.trace(traceMessage.toString()); + traceMessage = new StringBuilder(); } } - debug("\n"); + logger.trace(traceMessage.toString()); - logger.debug("Page {}, ", pages); + logger.trace("Page {}, ", pages); flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); totalBytesTransferred = size; - logger.debug("OK! Total diff stream length={} bytes", ansi().fgBright(GREEN).a(size).reset().toString()); + logger.trace("OK! Total diff stream length={} bytes", ansi().fgBright(GREEN).a(size).reset().toString()); } //dumpSideBySide(img1, img2); } - - protected void debug(String format, Object... args) { - logger.trace(String.format(format, args)); - } } From 835d4edeaee2bd62aa892288d89af4e076898c08 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 20:17:49 +0200 Subject: [PATCH 075/359] [Updater] For now comment out unused differential code --- .../java/org/selfbus/updater/mode/differential/FlashDiff.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index 76ecf17c..1285ce57 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -143,8 +143,8 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash SearchResult rBackwardRamWindow = letLongestCommonBytes(w.getOldBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); rBackwardRamWindow.sourceType = SourceType.BACKWARD_RAM; //SearchResult rBackwardRamWindow = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0); // in case we would have two flash banks, ie. full old image available - int currentPage = i / FlashPage.PAGE_SIZE; - int firstAddressInThisPage = currentPage * FlashPage.PAGE_SIZE; + //int currentPage = i / FlashPage.PAGE_SIZE; + //int firstAddressInThisPage = currentPage * FlashPage.PAGE_SIZE; SearchResult rForwardOldFlash = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); rForwardOldFlash.sourceType = SourceType.FORWARD_ROM; // which result is better, from FORWARD ROM or BACKWARD RAM? From 505da938f779f20d983b322ce86e991a4589c750 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 20:19:01 +0200 Subject: [PATCH 076/359] [Updater] Make `letLongestCommonBytes` private --- .../java/org/selfbus/updater/mode/differential/FlashDiff.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index 1285ce57..653ca683 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -56,7 +56,7 @@ public String toString() { } } - public SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOffset, int oldDataMinimumAddr, int maxLength) { + private SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOffset, int oldDataMinimumAddr, int maxLength) { // search as long as possible ar2[beginOffset..n] bytes (pattern) common with ar1[s..t], where n and t are up to length-1 of appropriate arrays and s is unknown int logestCandidateSrcOffset = 0; int logestCandidateLength = 0; From bc2f40c2fa6d26bdf205a87172a75b3ddf529013 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 20:22:41 +0200 Subject: [PATCH 077/359] [Updater] Fields can be `final` --- .../source/src/main/java/org/selfbus/updater/CliOptions.java | 2 +- .../java/org/selfbus/updater/mode/differential/FlashDiff.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index ce7ec98e..e8212dc9 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -119,7 +119,7 @@ public class CliOptions { private final Options cliOptions = new Options(); // define parser CommandLine cmdLine; - HelpFormatter helper = new HelpFormatter(); + final HelpFormatter helper = new HelpFormatter(); private final String helpHeader; private final String helpFooter; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index 653ca683..bb3562d9 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -37,8 +37,8 @@ enum SourceType { } static class SearchResult { - int offset; - int length; + final int offset; + final int length; SourceType sourceType = SourceType.FORWARD_ROM; public SearchResult(int logestCandidateSrcOffset, int logestCandidateLength) { From eb4cc472c8e9614929205922d9da5f28e5e78f46 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 20:23:09 +0200 Subject: [PATCH 078/359] [Updater] Inline return value --- .../main/java/org/selfbus/updater/SBManagementClientImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java index 4c834479..84ad3d8a 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java @@ -68,7 +68,6 @@ public byte[] sendUpdateData(final Destination dst, final int cmd, final byte[] } send = DataUnitBuilder.createAPDU(apciWrite, asdu); - final byte[] response = this.sendWait(dst, getPriority(), send, apciResponse, 2, MAX_ASDU_LENGTH, responseTimeout()); - return response; + return this.sendWait(dst, getPriority(), send, apciResponse, 2, MAX_ASDU_LENGTH, responseTimeout()); } } From ff8f040113fb257b6a5de22eafd47f679ac42ea8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 20:25:37 +0200 Subject: [PATCH 079/359] [Updater] Delete unused return value --- .../source/src/main/java/org/selfbus/updater/BinImage.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java index e0e1f6d8..7c8ba779 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java @@ -125,23 +125,21 @@ private void calculateCrc32() { this.crc32 = crc32File.getValue(); } - public final boolean writeToBinFile(String fileName) { + public final void writeToBinFile(String fileName) { File binFile = new File(fileName); if (!binFile.getParentFile().exists()) { if (!binFile.getParentFile().mkdirs()) { logger.warn(ansi().fg(RED).a("Could not create bin-file directory {}").reset().toString(), binFile.getParentFile().toString()); } - return false; + return; } try (FileOutputStream fos = new FileOutputStream(binFile)) { fos.write(binData); - return true; } catch (IOException e) { logger.warn(ansi().fg(RED).a("Could not write bin-file {}").reset().toString(), binFile.getPath()); } - return false; } public final String toString() { From ba545e7a77b2195dc50a7776cc3948555751ace5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 20:43:30 +0200 Subject: [PATCH 080/359] [Updater] Cleanup exceptions never thrown and unused imports --- .../java/org/selfbus/updater/CliOptions.java | 3 +-- .../org/selfbus/updater/DeviceManagement.java | 15 +++++++-------- .../selfbus/updater/DiscoverKnxInterfaces.java | 1 - .../org/selfbus/updater/FlashFullMode.java | 6 ++---- .../java/org/selfbus/updater/SBKNXLink.java | 2 +- .../updater/SBManagementClientImpl.java | 2 -- .../main/java/org/selfbus/updater/Updater.java | 10 ++-------- .../main/java/org/selfbus/updater/Utils.java | 4 +--- .../updater/bootloader/BootDescriptor.java | 6 ++---- .../java/org/selfbus/updater/gui/GuiMain.java | 18 ++++-------------- .../updater/mode/differential/FlashDiff.java | 4 +--- .../mode/differential/FlashProgrammer.java | 4 +--- 12 files changed, 22 insertions(+), 53 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index e8212dc9..143234c1 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -166,8 +166,7 @@ public class CliOptions { public CliOptions(final String[] args, String helpApplicationName, String helpHeader, String helpFooter, - IndividualAddress progDevice, IndividualAddress ownAddress) - throws ParseException, KNXFormatException { + IndividualAddress progDevice, IndividualAddress ownAddress) { this.helpApplicationName = helpApplicationName; this.helpHeader = helpHeader; this.helpFooter = helpFooter; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index 2b72bbb2..3669cc55 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -86,8 +86,7 @@ private void waitRestartTime(int restartTimeSeconds) { * * @param device the IndividualAddress of the device to restart */ - public void restartDeviceToBootloader(IndividualAddress device) - throws KNXLinkClosedException { + public void restartDeviceToBootloader(IndividualAddress device) { int restartProcessTime = Mcu.DEFAULT_RESTART_TIME_SECONDS; try (Destination dest = this.mc.createDestination(device, true, false, false)) { logger.info("Restarting device {} into bootloader", device); @@ -103,7 +102,7 @@ public void restartDeviceToBootloader(IndividualAddress device) } public byte[] requestUIDFromDevice() - throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { logger.info("Requesting UID from {}", progDestination.getAddress()); byte[] result = sendWithRetry(UPDCommand.REQUEST_UID, new byte[0], getMaxUpdCommandRetry()).data(); if (result[COMMAND_POSITION] != UPDCommand.RESPONSE_UID.id) { @@ -127,7 +126,7 @@ public byte[] requestUIDFromDevice() } public BootloaderIdentity requestBootloaderIdentity() - throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { logger.debug("Requesting Bootloader Identity"); byte[] telegram = new byte[2]; @@ -166,7 +165,7 @@ public BootloaderIdentity requestBootloaderIdentity() } public BootDescriptor requestBootDescriptor() - throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { logger.debug("Requesting Boot Descriptor"); byte[] result = sendWithRetry(UPDCommand.REQUEST_BOOT_DESC, new byte[0], getMaxUpdCommandRetry()).data(); if (result[COMMAND_POSITION] != UPDCommand.RESPONSE_BOOT_DESC.id) { @@ -182,7 +181,7 @@ public BootDescriptor requestBootDescriptor() } public String requestAppVersionString() - throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { + throws UpdaterException { byte[] result = sendWithRetry(UPDCommand.APP_VERSION_REQUEST, new byte[0], getMaxUpdCommandRetry()).data(); if (result[COMMAND_POSITION] != UPDCommand.APP_VERSION_RESPONSE.id){ UPDProtocol.checkResult(result); @@ -192,7 +191,7 @@ public String requestAppVersionString() } public void unlockDeviceWithUID(byte[] uid) - throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { logger.info("Unlocking device {} with UID {}", progDestination.getAddress(), Utils.byteArrayToHex(uid)); byte[] result = sendWithRetry(UPDCommand.UNLOCK_DEVICE, uid, getMaxUpdCommandRetry()).data(); if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS.id) { @@ -353,7 +352,7 @@ public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) } public void requestBootLoaderStatistic() - throws KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, InterruptedException, UpdaterException { + throws UpdaterException { logger.debug("Requesting Bootloader statistic"); byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); if (result[COMMAND_POSITION] == UPDCommand.RESPONSE_STATISTIC.id) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DiscoverKnxInterfaces.java index 49f229fd..58a09d0f 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -5,7 +5,6 @@ import java.time.Duration; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class DiscoverKnxInterfaces { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java index 11d380de..a0241d08 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java @@ -4,10 +4,8 @@ import org.selfbus.updater.upd.UPDProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.KNXRemoteException; import tuwien.auto.calimero.KNXTimeoutException; import tuwien.auto.calimero.link.KNXLinkClosedException; -import tuwien.auto.calimero.mgmt.KNXDisconnectException; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -26,8 +24,8 @@ public class FlashFullMode { */ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmware, int dataSendDelay, boolean eraseFirmwareRange, boolean logStatistics) - throws IOException, KNXDisconnectException, KNXTimeoutException, KNXLinkClosedException, - InterruptedException, UpdaterException, KNXRemoteException { + throws IOException, KNXTimeoutException, KNXLinkClosedException, + InterruptedException, UpdaterException { ResponseResult resultSendData, resultProgramData; ResponseResult resultTotal = new ResponseResult(); // collect so static data diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index bda1f106..b08e7191 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -93,7 +93,7 @@ public void setCliOptions(CliOptions cliOptions) { * @throws KNXException on problems on link creation * @throws InterruptedException on interrupted thread */ - public KNXNetworkLink openLink() throws KNXException, InterruptedException, UpdaterException { + public KNXNetworkLink openLink() throws KNXException, InterruptedException { KNXNetworkLink newLink = doOpenLink(); logger.info("KNX connection: {}", newLink); return newLink; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java index 84ad3d8a..276a8353 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java @@ -12,12 +12,10 @@ import tuwien.auto.calimero.mgmt.KNXDisconnectException; import tuwien.auto.calimero.mgmt.ManagementClientImpl; -import java.util.List; import java.util.Optional; import java.util.function.BiFunction; import static org.selfbus.updater.Mcu.MAX_ASDU_LENGTH; -import static org.selfbus.updater.Mcu.MAX_PAYLOAD; /** * Extends the calimero-core class {@link ManagementClientImpl} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index aa6026dd..0163049e 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -3,7 +3,6 @@ import org.fusesource.jansi.AnsiConsole; import org.selfbus.updater.bootloader.BootloaderStatistic; import tuwien.auto.calimero.*; -import org.apache.commons.cli.ParseException; import org.selfbus.updater.bootloader.BootDescriptor; import org.selfbus.updater.bootloader.BootloaderIdentity; import org.selfbus.updater.bootloader.BootloaderUpdater; @@ -12,7 +11,6 @@ import org.slf4j.LoggerFactory; import tuwien.auto.calimero.link.KNXNetworkLink; -import java.io.FileNotFoundException; import java.io.IOException; import static org.fusesource.jansi.Ansi.*; @@ -64,10 +62,8 @@ public class Updater implements Runnable { * list with options * @throws KNXIllegalArgumentException * on unknown/invalid options - * @throws ParseException - * on invalid command line parameters */ - public Updater(final String[] args) throws ParseException, KNXFormatException { + public Updater(final String[] args) { logger.debug(ToolInfo.getFullInfo()); logger.debug(Settings.getLibraryHeader(false)); logger.info(ansi().fgBright(GREEN).bold().a( @@ -84,7 +80,7 @@ public Updater(final String[] args) throws ParseException, KNXFormatException { "Selfbus KNX-Firmware update tool options", "", PHYS_ADDRESS_BOOTLOADER, PHYS_ADDRESS_OWN); this.sbKNXLink = new SBKNXLink(); this.sbKNXLink.setCliOptions(cliOptions); - } catch (final KNXIllegalArgumentException | KNXFormatException | ParseException e) { + } catch (final KNXIllegalArgumentException e) { throw e; } catch (final RuntimeException e) { throw new KNXIllegalArgumentException(e.getMessage(), e); @@ -362,8 +358,6 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd } catch (final InterruptedException e) { canceled = true; Thread.currentThread().interrupt(); - } catch (FileNotFoundException e) { - logger.error("FileNotFoundException ", e); } catch (IOException e) { logger.error("IOException ", e); } catch (Throwable e) { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java index 25e4b851..7be907d0 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java @@ -2,7 +2,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.KNXException; import tuwien.auto.calimero.KNXIllegalArgumentException; import tuwien.auto.calimero.knxnetip.TcpConnection; @@ -154,8 +153,7 @@ public static int crc32Value(byte[] buffer) { private static final Map tcpConnectionPool = new HashMap<>(); private static boolean registeredTcpShutdownHook; - static synchronized TcpConnection tcpConnection(final InetSocketAddress local, final InetSocketAddress server) - throws KNXException { + static synchronized TcpConnection tcpConnection(final InetSocketAddress local, final InetSocketAddress server) { if (!registeredTcpShutdownHook) { Runtime.getRuntime().addShutdownHook(new Thread(Utils::closeTcpConnections)); registeredTcpShutdownHook = true; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java index af28e33e..2cb87a2c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java @@ -1,6 +1,5 @@ package org.selfbus.updater.bootloader; -import org.selfbus.updater.UpdaterException; import org.selfbus.updater.Utils; import static org.fusesource.jansi.Ansi.*; @@ -27,9 +26,8 @@ private BootDescriptor() {} * @param endAddress end address of the application firmware * @param crc32 crc32 checksum from start to end address * @param appVersionAddress AppVersionPointer address - * @throws UpdaterException Exception in case of invalid start or end address */ - public BootDescriptor(long startAddress, long endAddress, int crc32, long appVersionAddress) throws UpdaterException { + public BootDescriptor(long startAddress, long endAddress, int crc32, long appVersionAddress) { this.startAddress = startAddress; this.endAddress = endAddress; this.crc32 = crc32; @@ -39,7 +37,7 @@ public BootDescriptor(long startAddress, long endAddress, int crc32, long appVer } - public static BootDescriptor fromArray(byte[] parse) throws UpdaterException { + public static BootDescriptor fromArray(byte[] parse) { long startAddr = (parse[0] & 0xFF) + ((parse[1] & 0xFF) << 8) + ((parse[2] & 0xFF) << 16) + diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 9f470bd5..2502d1fa 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -4,12 +4,10 @@ import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; import org.selfbus.updater.*; -import tuwien.auto.calimero.KNXFormatException; import tuwien.auto.calimero.KNXIllegalArgumentException; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; -import org.apache.commons.cli.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -142,11 +140,7 @@ public void actionPerformed(ActionEvent e) { jLoggingPane.setText(""); updaterThread = new Thread() { public void run() { - try { - setCliOptions(); - } catch (KNXFormatException | ParseException ex) { - throw new RuntimeException(ex); - } + setCliOptions(); final Updater d = new Updater(cliOptions); d.run(); @@ -165,11 +159,7 @@ public void run() { public void actionPerformed(ActionEvent e) { updaterThread = new Thread(() -> { - try { - setCliOptions(); - } catch (KNXFormatException | ParseException ex) { - throw new RuntimeException(ex); - } + setCliOptions(); final Updater upd = new Updater(cliOptions); String uid = upd.requestUid(); SwingUtilities.invokeLater(() -> { @@ -304,7 +294,7 @@ private String cliParameterToString(ArrayList list) { return result.toString().trim(); } - private void setCliOptions() throws KNXFormatException, ParseException { + private void setCliOptions() { ArrayList argsList = new ArrayList<>(); argsList.add(textBoxKnxGatewayIpAddr.getText()); @@ -346,7 +336,7 @@ private void setCliOptions() throws KNXFormatException, ParseException { // read in user-supplied command line options this.cliOptions = new CliOptions(args, updaterFileName, "Selfbus KNX-Firmware update tool options", "", Updater.PHYS_ADDRESS_BOOTLOADER, Updater.PHYS_ADDRESS_OWN); - } catch (final KNXIllegalArgumentException | KNXFormatException | ParseException e) { + } catch (final KNXIllegalArgumentException e) { throw e; } catch (final RuntimeException e) { throw new KNXIllegalArgumentException(e.getMessage(), e); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index bb3562d9..10e26bba 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -4,10 +4,8 @@ import org.selfbus.updater.UpdaterException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.KNXRemoteException; import tuwien.auto.calimero.KNXTimeoutException; import tuwien.auto.calimero.link.KNXLinkClosedException; -import tuwien.auto.calimero.mgmt.KNXDisconnectException; import java.util.ArrayList; import java.util.List; @@ -126,7 +124,7 @@ public int getTotalBytesTransferred() { } public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flashProgrammer) - throws InterruptedException, KNXTimeoutException, KNXLinkClosedException, KNXDisconnectException, KNXRemoteException, UpdaterException { + throws InterruptedException, KNXTimeoutException, KNXLinkClosedException, UpdaterException { // TODO check if old image can be truncated for smaller new image, first test was fine //BinImage img1 = new BinImage(img1Orig); // make copy to keep img1Orig untouched, this fails if arraycopy at 203, when new image is larger than old BinImage img1 = new BinImage(img1Orig,img2.getBinData().length); // make copy to keep img1Orig untouched, ensure old bin buffer is same size as new bin file diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java index 6998044c..b49b4ed1 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java @@ -1,14 +1,12 @@ package org.selfbus.updater.mode.differential; import org.selfbus.updater.UpdaterException; -import tuwien.auto.calimero.KNXRemoteException; import tuwien.auto.calimero.KNXTimeoutException; import tuwien.auto.calimero.link.KNXLinkClosedException; -import tuwien.auto.calimero.mgmt.KNXDisconnectException; import java.util.List; public interface FlashProgrammer { - void sendCompressedPage(List outputDiffStream, long crc32) throws InterruptedException, KNXTimeoutException, KNXLinkClosedException, KNXRemoteException, KNXDisconnectException, UpdaterException; + void sendCompressedPage(List outputDiffStream, long crc32) throws InterruptedException, KNXTimeoutException, KNXLinkClosedException, UpdaterException; } From c0e5691a06982f8bb3acf7f915cecaa363963186 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 21:03:13 +0200 Subject: [PATCH 081/359] [Updater] Fix typos --- .../updater/source/src/main/java/org/selfbus/updater/Mcu.java | 2 +- .../org/selfbus/updater/bootloader/BootloaderUpdater.java | 1 + .../java/org/selfbus/updater/gui/ConColorsToStyledDoc.java | 1 + .../java/org/selfbus/updater/mode/differential/FlashDiff.java | 4 ++-- .../src/main/java/org/selfbus/updater/upd/UDPResult.java | 2 +- .../src/main/java/org/selfbus/updater/upd/UPDCommand.java | 2 +- .../source/src/main/resources/GuiTranslation_de.properties | 2 +- .../org/selfbus/updater/gui/ConColorsToStyledDocTests.java | 1 + .../org/selfbus/updater/mode/differential/FlashDiffTests.java | 2 ++ 9 files changed, 11 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Mcu.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Mcu.java index e7d23233..25929f1d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Mcu.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Mcu.java @@ -6,7 +6,7 @@ * Basic information about the bootloader's MCU */ public final class Mcu { - /** Maximum length a asdu can be in a standard frame */ + /** Maximum length an asdu can be in a standard frame */ public static final int MAX_ASDU_LENGTH = 14; /** Maximum data payload one APCI_USERMSG_MANUFACTURER_0/APCI_USERMSG_MANUFACTURER_6 can handle */ public static final int MAX_PAYLOAD = MAX_ASDU_LENGTH - 1; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderUpdater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderUpdater.java index 11df7bd6..bd5f6e3a 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderUpdater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderUpdater.java @@ -2,6 +2,7 @@ public final class BootloaderUpdater { /** Bootloader Updater appVersion identity string from app_main.cpp of bootloaderupdater */ + @SuppressWarnings("SpellCheckingInspection") public static final String BOOTLOADER_UPDATER_ID_STRING = "SBblu"; /** Maximum restart and processing time in milliseconds to diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java index 835e0927..82aa2f2e 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -44,6 +44,7 @@ private ConColorsToStyledDoc() {} // avoids instance creation * ANSI escape codes *

*/ + @SuppressWarnings("SpellCheckingInspection") private static final String RegExAnsi = AnsiEscape + "\\" + AnsiBracket + "(([;\\d]*)[A-Hmsu]|(\\?25[hl]))"; private static final String RegExAnsiCursor = AnsiEscape + "\\" + AnsiBracket + "(([;\\d]*)[A-Hsu])"; private static final String AnsiCodeSeparator = ";"; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index 10e26bba..e5514f14 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -140,7 +140,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash while (i < img2.getBinData().length) { SearchResult rBackwardRamWindow = letLongestCommonBytes(w.getOldBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); rBackwardRamWindow.sourceType = SourceType.BACKWARD_RAM; - //SearchResult rBackwardRamWindow = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0); // in case we would have two flash banks, ie. full old image available + //SearchResult rBackwardRamWindow = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0); // in case we would have two flash banks, i.e. full old image available //int currentPage = i / FlashPage.PAGE_SIZE; //int firstAddressInThisPage = currentPage * FlashPage.PAGE_SIZE; SearchResult rForwardOldFlash = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); @@ -224,7 +224,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash pages++; // emulate we have loaded the original page from ROM to RAM and written new page to ROM w.fillNextPage(img1.getBinData(), i - FlashPage.PAGE_SIZE); // backup old data from ROM to RAM - System.arraycopy(img2.getBinData(), i - FlashPage.PAGE_SIZE, img1.getBinData(), i - FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); // emulatae write of new data to ROM + System.arraycopy(img2.getBinData(), i - FlashPage.PAGE_SIZE, img1.getBinData(), i - FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); // emulate write of new data to ROM } } size += possiblyFinishRawBuffer(rawBuffer, outputDiffStream); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java index d300840f..2b034a6c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java @@ -99,7 +99,7 @@ public enum UDPResult { * Create a UDPResult instance from given id, description and error state * @param id ID of the UDPResult * @param description Description of the UDPResult - * @param isError Set to true, if the UPDResult represents a error, otherwise set to false + * @param isError Set to true, if the UPDResult represents an error, otherwise set to false */ UDPResult(byte id, String description, boolean isError) { this.id = id; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java index 91bd856f..505bb055 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java @@ -23,7 +23,7 @@ public enum UPDCommand { REQUEST_STATISTIC((byte)0xdf, "STATISTIC_REQUEST"), //!< Return some statistic data for the active connection RESPONSE_STATISTIC((byte)0xde, "STATISTIC_RESPONSE"), //!< Response for @ref UPD_STATISTIC_RESPONSE containing the statistic data SEND_LAST_ERROR((byte)0xdc, "SEND_LAST_ERROR"), //!< Response containing the last error - UNLOCK_DEVICE((byte)0xbf, "UNLOCK_DEVICE"), //!< Unlock the device for operations, which are only allowed on a unlocked device + UNLOCK_DEVICE((byte)0xbf, "UNLOCK_DEVICE"), //!< Unlock the device for operations, which are only allowed on an unlocked device REQUEST_UID((byte)0xbe, "REQUEST_UID"), //!< Return the 12 byte shorten UID (GUID) of the mcu @note device must be unlocked RESPONSE_UID((byte)0xbd, "RESPONSE_UID"), //!< Response for @ref REQUEST_UID containing the first 12 bytes of the UID APP_VERSION_REQUEST((byte)0xbc, "APP_VERSION_REQUEST"), //!< Return address of AppVersion string diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties index a88fcd46..312e22c7 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties @@ -22,7 +22,7 @@ noFlashHint=Flashen der Firmware deaktivieren uidHint=die UID des Selfbus Gerätes fileNameHint=Der Dateipfad kann manuell bearbeitet werden
\ (Enter zum bestätigen) -ipAddrHint=IP Adresse der KNX Schnittstelle +ipAddrHint=IP-Adresse der KNX Schnittstelle portHint=TCP/UDP Portnummer der KNX Schnittstelle (default 3671) stopFlash=Stoppe Flashvorgang medium=Medium diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java index 87fd01e1..09810a5c 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java @@ -189,6 +189,7 @@ public void testConvert() throws BadLocationException { JTextPane textPane = new JTextPane(); StyledDocument document = (StyledDocument) textPane.getDocument(); //todo add more testcases + @SuppressWarnings("SpellCheckingInspection") String[][] testCases = { {"\" Done Speed Avg Min Max Time\"", "\" Done Speed Avg Min Max Time\""}, // no ansi {"\033[mreset_jansi\033[m", "reset_jansi"}, // reset like jansi it sends diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java index 6f548210..49e24fe8 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java @@ -39,6 +39,7 @@ private void performTest(BinImage img1, BinImage img2) { // }); } + @SuppressWarnings("SpellCheckingInspection") @Test public void testDiff() throws URISyntaxException { // test of upgrade from old version to newer (longer) @@ -50,6 +51,7 @@ public void testDiff() throws URISyntaxException { performTest(img1, img2); } + @SuppressWarnings("SpellCheckingInspection") @Test public void testDiff2() throws URISyntaxException, UpdaterException { // test of new firmware into empty MCU From 27fea585fc0327f0a0f4d7611bdc1f03be980927 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 21:37:44 +0200 Subject: [PATCH 082/359] [Updater] Use /* */ for commented out code --- .../updater/mode/differential/FlashDiff.java | 8 +++++--- .../mode/differential/FlashDiffTests.java | 16 +++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index e5514f14..2c3efd3d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -140,9 +140,11 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash while (i < img2.getBinData().length) { SearchResult rBackwardRamWindow = letLongestCommonBytes(w.getOldBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); rBackwardRamWindow.sourceType = SourceType.BACKWARD_RAM; - //SearchResult rBackwardRamWindow = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0); // in case we would have two flash banks, i.e. full old image available - //int currentPage = i / FlashPage.PAGE_SIZE; - //int firstAddressInThisPage = currentPage * FlashPage.PAGE_SIZE; + /* + SearchResult rBackwardRamWindow = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0); // in case we would have two flash banks, i.e. full old image available + int currentPage = i / FlashPage.PAGE_SIZE; + int firstAddressInThisPage = currentPage * FlashPage.PAGE_SIZE; + */ SearchResult rForwardOldFlash = letLongestCommonBytes(img1.getBinData(), img2.getBinData(), i, 0, MAX_COPY_LENGTH); rForwardOldFlash.sourceType = SourceType.FORWARD_ROM; // which result is better, from FORWARD ROM or BACKWARD RAM? diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java index 49e24fe8..b31662e3 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java @@ -30,13 +30,15 @@ private void performTest(BinImage img1, BinImage img2) { //System.exit(1); }); // TODO -// differ.generateDiff(img1, img2, outputDiffStream -> { -// // process compressed page -// for (byte b : outputDiffStream) { -// decompressor.putByte(b); -// } -// decompressor.pageCompleted(); -// }); + /* + differ.generateDiff(img1, img2, outputDiffStream -> { + // process compressed page + for (byte b : outputDiffStream) { + decompressor.putByte(b); + } + decompressor.pageCompleted(); + }); + */ } @SuppressWarnings("SpellCheckingInspection") From e84638f1c7e2447c77ef551e8831e5f257c131e6 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 21:40:15 +0200 Subject: [PATCH 083/359] [Updater] Cleanup exceptions never thrown and unused imports --- .../src/main/java/org/selfbus/updater/FlashDiffMode.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 33951b5b..5b87d6bb 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -8,10 +8,8 @@ import org.selfbus.updater.upd.UPDProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.KNXRemoteException; import tuwien.auto.calimero.KNXTimeoutException; import tuwien.auto.calimero.link.KNXLinkClosedException; -import tuwien.auto.calimero.mgmt.KNXDisconnectException; import java.io.File; import java.util.Arrays; @@ -82,7 +80,7 @@ public static boolean isInitialized() { // Differential update routine public static ResponseResult doDifferentialFlash(DeviceManagement dm, long startAddress, byte[] binData) - throws KNXDisconnectException, KNXTimeoutException, KNXRemoteException, KNXLinkClosedException, InterruptedException, UpdaterException { + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { ///\todo add connection reset and sending again on failure, like in full flash mode if (!isInitialized()) { throw new UpdaterException("Differential mode not initialized!"); From 4a062a03cb85e893165620458ba1afc26eaee2b2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 21:40:58 +0200 Subject: [PATCH 084/359] [Updater] `logger` at the moment not used in `SBManagementClientImpl` --- .../java/org/selfbus/updater/SBManagementClientImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java index 276a8353..6d88bbfe 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java @@ -1,7 +1,5 @@ package org.selfbus.updater; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import tuwien.auto.calimero.DataUnitBuilder; import tuwien.auto.calimero.IndividualAddress; import tuwien.auto.calimero.KNXInvalidResponseException; @@ -28,12 +26,12 @@ public class SBManagementClientImpl extends ManagementClientImpl { private static final int USERMSG_MANUFACTURER_6_RESPONSE = 0x2FE; private static final int apciWrite = USERMSG_MANUFACTURER_0_WRITE; private static final int apciResponse = USERMSG_MANUFACTURER_6_RESPONSE; - private final Logger logger; + //private final Logger logger; // uncomment when needed public SBManagementClientImpl(KNXNetworkLink link) throws KNXLinkClosedException { super(link); - logger = LoggerFactory.getLogger(SBManagementClientImpl.class.getName() + " " + link.getName()); + //logger = LoggerFactory.getLogger(SBManagementClientImpl.class.getName() + " " + link.getName()); } private byte[] prepareAsdu(final int cmd, final byte[] data) { From 0e9e2e3bc5e8dc95431b1048ba946afdba3c46c5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 21:59:43 +0200 Subject: [PATCH 085/359] [Updater] Default constructor is private, so it can't be used. Thats why @SuppressWarnings("unused") --- .../main/java/org/selfbus/updater/bootloader/BootDescriptor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java index 2cb87a2c..3e82c5ad 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java @@ -17,6 +17,7 @@ public class BootDescriptor { private long appVersionAddress; private boolean valid; + @SuppressWarnings("unused") private BootDescriptor() {} /** From df993b87d44e9b4317a16a0c3b2ccbeb431f5803 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 22:48:26 +0200 Subject: [PATCH 086/359] [Updater] Fix make sure to enable cursor on exit --- .../source/src/main/java/org/selfbus/updater/Updater.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 0163049e..164f1c6a 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -106,7 +106,7 @@ public static void main(final String[] args) { } catch (final Throwable t) { logger.error("parsing options ", t); } finally { - AnsiCursor.on(); + System.out.print(AnsiCursor.on()); // make sure we enable the cursor logger.debug("main exit"); } } From a718bc0d05a6ac4f9e8b55e3b9cd371a9d9f537c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 23:08:29 +0200 Subject: [PATCH 087/359] [Updater] Use enhanced for loop --- .../source/src/test/java/org/selfbus/updater/UtilsTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java index 4b013ca8..efb6dcec 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java @@ -55,10 +55,10 @@ public void testLongToStream() { long expectedLong; byte[] testStream = new byte[]{0, 0, 0, 0}; - for (int i = 0; i < underTest.length; i++) { - Utils.longToStream(testStream, 0, underTest[i]); + for (long l : underTest) { + Utils.longToStream(testStream, 0, l); expectedLong = Utils.streamToLong(testStream, 0); - assertEquals(expectedLong, underTest[i]); + assertEquals(expectedLong, l); } } From 51e4b15ad41b9515722ed16308588ec4fce11ced Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 23:45:22 +0200 Subject: [PATCH 088/359] [Updater] Make `BootloaderIdentity` a `record` --- .../org/selfbus/updater/DeviceManagement.java | 4 +- .../java/org/selfbus/updater/Updater.java | 10 +-- .../bootloader/BootloaderIdentity.java | 66 ++++--------------- 3 files changed, 21 insertions(+), 59 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index 3669cc55..a1cb0753 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -152,8 +152,8 @@ public BootloaderIdentity requestBootloaderIdentity() BootloaderIdentity bl = BootloaderIdentity.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); logger.info("Device Bootloader: {}", ansi().fgBright(YELLOW).a(bl).reset().toString()); - boolean versionsMatch = (bl.getVersionMajor() > ToolInfo.minMajorVersionBootloader()) || - ((bl.getVersionMajor() == ToolInfo.minMajorVersionBootloader()) && (bl.getVersionMinor() >= ToolInfo.minMinorVersionBootloader())); + boolean versionsMatch = (bl.versionMajor() > ToolInfo.minMajorVersionBootloader()) || + ((bl.versionMajor() == ToolInfo.minMajorVersionBootloader()) && (bl.versionMinor() >= ToolInfo.minMinorVersionBootloader())); if (!versionsMatch) { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 164f1c6a..75262edf 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -261,17 +261,17 @@ public void run() { logger.info("File APP_VERSION : {}", ansi().fgBright(GREEN).a(newFirmware.getAppVersion()).reset().toString()); // Check if FW image has correct offset for MCUs bootloader size - if (newFirmware.startAddress() < bootLoaderIdentity.getApplicationFirstAddress()) { + if (newFirmware.startAddress() < bootLoaderIdentity.applicationFirstAddress()) { logger.error(ansi().fgBright(RED).a(" Error! The specified firmware image would overwrite parts of the bootloader. Check FW offset setting in the linker!").reset().toString()); - logger.error(ansi().fgBright(RED).a(" Firmware needs to start at or beyond 0x{}").reset().toString(), String.format("%04X", bootLoaderIdentity.getApplicationFirstAddress())); + logger.error(ansi().fgBright(RED).a(" Firmware needs to start at or beyond 0x{}").reset().toString(), String.format("%04X", bootLoaderIdentity.applicationFirstAddress())); throw new UpdaterException("Firmware offset not correct!"); } - else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAddress()) { + else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddress()) { logger.debug(ansi().fgBright(GREEN).a(" Firmware starts directly beyond bootloader.").reset().toString()); } else { logger.debug(ansi().fgBright(YELLOW).a(" Info: There are {} bytes of unused flash between bootloader and firmware.").reset().toString(), - newFirmware.startAddress() - bootLoaderIdentity.getApplicationFirstAddress()); + newFirmware.startAddress() - bootLoaderIdentity.applicationFirstAddress()); } if (cliOptions.eraseFullFlash()) { @@ -289,7 +289,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.getApplicationFirstAdd } } - if ((bootLoaderIdentity.getVersionMajor()) <= 1 && (bootLoaderIdentity.getVersionMinor() < 20)) { + if ((bootLoaderIdentity.versionMajor()) <= 1 && (bootLoaderIdentity.versionMinor() < 20)) { dm.setProtocolVersion(UDPProtocolVersion.UDP_V0); } else { diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java index 7a541306..f87b29ed 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java @@ -3,17 +3,16 @@ import org.selfbus.updater.Utils; /** - * Holds Bootloader identity information - * see software-arm-lib/Bus-Updater/src/update.cpp (method updRequestBootloaderIdentity) for more information. + * Holds Bootloader identity information + *

+ * see software-arm-lib/Bus-Updater/src/update.cpp (method updRequestBootloaderIdentity) for more information. */ -public class BootloaderIdentity { - private final long versionMajor; - private final long versionMinor; - private final long versionSBLibMajor; - private final long versionSBLibMinor; - private final long features; - private final long applicationFirstAddress; - +public record BootloaderIdentity(long versionMajor, + long versionMinor, + long versionSBLibMajor, + long versionSBLibMinor, + long features, + long applicationFirstAddress) { private String hexVersionToString(long versionMajor, long versionMinor) { byte high = (byte) (versionMinor >> 4); byte low = (byte) (versionMinor & 0x0f); @@ -38,63 +37,26 @@ private String hexVersionToString(long versionMajor, long versionMinor) { return String.format("%d.%s%s", versionMajor, highPart, lowPart); } - public BootloaderIdentity(long versionMajor, long versionMinor, long versionSBLibMajor, long versionSBLibMinor, long features, long applicationFirstAddress) { - this.versionSBLibMajor = versionSBLibMajor; - this.versionSBLibMinor = versionSBLibMinor; - this.features = features; - this.applicationFirstAddress = applicationFirstAddress; - this.versionMajor = versionMajor; - this.versionMinor = versionMinor; - } - public static BootloaderIdentity fromArray(byte[] parse) { long vMajor = parse[0] & 0xff; long vMinor = parse[1] & 0xff; long features = Utils.streamToShort(parse, 2); long versionSBLibMajor = parse[4] & 0xff; - long versionSBLibMinor= parse[5] & 0xff; + long versionSBLibMinor = parse[5] & 0xff; long applicationFirstAddress = Utils.streamToLong(parse, 6); - return new BootloaderIdentity(vMajor, vMinor, versionSBLibMajor, versionSBLibMinor, features, applicationFirstAddress ); + return new BootloaderIdentity(vMajor, vMinor, versionSBLibMajor, versionSBLibMinor, features, applicationFirstAddress); } public String toString() { return String.format("Version: %s, sbLib Version: %s, Features: 0x%04X, App-start: 0x%04X", - getVersion(), getVersionSBLib(), getFeatures(), getApplicationFirstAddress()); - } - - public long getFeatures() - { - return features; - } - - public long getApplicationFirstAddress() - { - return applicationFirstAddress; - } - - public long getVersionMajor() - { - return versionMajor; - } - - public long getVersionMinor() - { - return versionMinor; - } - - public long getVersionSBLibMajor() { - return versionSBLibMajor; - } - - public long getVersionSBLibMinor() { - return versionSBLibMinor; + getVersion(), getVersionSBLib(), features(), applicationFirstAddress()); } public String getVersion() { - return String.format("%d.%02d", getVersionMajor(), getVersionMinor()); + return String.format("%d.%02d", versionMajor(), versionMinor()); } public String getVersionSBLib() { - return hexVersionToString(getVersionSBLibMajor(), getVersionSBLibMinor()); + return hexVersionToString(versionSBLibMajor(), versionSBLibMinor()); } } From 0c7ebb64669d21a1d9cb3690c4f16bb37251c30c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 8 Aug 2024 23:51:04 +0200 Subject: [PATCH 089/359] [Updater] No need to unbox --- .../source/src/main/java/org/selfbus/updater/SBKNXLink.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index b08e7191..6f77cc8f 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -60,7 +60,7 @@ private KNXNetworkLink createRoutingLink(InetSocketAddress local, KNXMediumSetti } private static InetSocketAddress createLocalSocket(final InetAddress host, final Integer port) { - final int p = port != null ? port.intValue() : 0; + final int p = port != null ? port : 0; return host != null ? new InetSocketAddress(host, p) : new InetSocketAddress(p); } From 76a3b83676677e7a6ac5ababd341e647e6c6a00f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 00:46:00 +0200 Subject: [PATCH 090/359] [Updater] Fix use correct `SBKNXLink` class for logger --- .../source/src/main/java/org/selfbus/updater/SBKNXLink.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index 6f77cc8f..36db923d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -23,7 +23,7 @@ import static org.fusesource.jansi.Ansi.Color.*; public class SBKNXLink { - private static final Logger logger = LoggerFactory.getLogger(CliOptions.class.getName()); + private static final Logger logger = LoggerFactory.getLogger(SBKNXLink.class); private CliOptions cliOptions; public SBKNXLink() { From 526a413e0b1a50a0a6174184641d05ad3d6604af Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 01:14:43 +0200 Subject: [PATCH 091/359] [Updater] Use `class` instead of `getName()` for `logger` creation This ensures that the logger is created by the class and not by its class name (String). (Actually `LoggerFactory.getLogger(Class clazz) calls first `clazz.getName()`. So the chance is still good and hopefully future-proof.) --- .../source/src/main/java/org/selfbus/updater/BinImage.java | 2 +- .../source/src/main/java/org/selfbus/updater/CliOptions.java | 2 +- .../src/main/java/org/selfbus/updater/DeviceManagement.java | 2 +- .../source/src/main/java/org/selfbus/updater/FlashDiffMode.java | 2 +- .../source/src/main/java/org/selfbus/updater/FlashFullMode.java | 2 +- .../source/src/main/java/org/selfbus/updater/ToolInfo.java | 2 +- .../source/src/main/java/org/selfbus/updater/Updater.java | 2 +- .../updater/source/src/main/java/org/selfbus/updater/Utils.java | 2 +- .../source/src/main/java/org/selfbus/updater/gui/GuiMain.java | 2 +- .../org/selfbus/updater/mode/differential/Decompressor.java | 2 +- .../java/org/selfbus/updater/mode/differential/FlashDiff.java | 2 +- .../src/main/java/org/selfbus/updater/upd/UPDProtocol.java | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java index 7c8ba779..4a558d29 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java @@ -21,7 +21,7 @@ import static org.fusesource.jansi.Ansi.Color.*; public class BinImage { - private final static Logger logger = LoggerFactory.getLogger(BinImage.class.getName()); + private final static Logger logger = LoggerFactory.getLogger(BinImage.class); private final byte[] binData; private final long startAddress; private final long endAddress; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 143234c1..91e36681 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -42,7 +42,7 @@ * - check with cmdLine.hasOption(OPT_SHORT_XXX) in method parse(.) is it set or not */ public class CliOptions { - private static final Logger logger = LoggerFactory.getLogger(CliOptions.class.getName()); + private static final Logger logger = LoggerFactory.getLogger(CliOptions.class); private static final String OPT_SHORT_FILENAME = "f"; private static final String OPT_LONG_FILENAME = "fileName"; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java index a1cb0753..9817bff0 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java @@ -46,7 +46,7 @@ private DeviceManagement () { setProtocolVersion(UDPProtocolVersion.UDP_V1); } - private final static Logger logger = LoggerFactory.getLogger(DeviceManagement.class.getName()); + private final static Logger logger = LoggerFactory.getLogger(DeviceManagement.class); private SBManagementClientImpl mc; //!< calimero device management client private Destination progDestination; private KNXNetworkLink link; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java index 5b87d6bb..9b298721 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java @@ -23,7 +23,7 @@ * experimental (WIP) Provides differential flash mode for the bootloader (MCU) */ public final class FlashDiffMode { - private final static Logger logger = LoggerFactory.getLogger(FlashDiffMode.class.getName()); + private final static Logger logger = LoggerFactory.getLogger(FlashDiffMode.class); // hexCacheDir will be used as a cache directory for the cached *.hex files used by the differential update mode // windows: C:\Users\[currentUser]\AppData\Local\Selfbus\Selfbus-Updater\Cache\ // linux : /home/[user-home]]/.cache/Selfbus-Updater/ diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java index a0241d08..63916212 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java @@ -17,7 +17,7 @@ * Provides full flash mode for the bootloader (MCU) */ public class FlashFullMode { - private final static Logger logger = LoggerFactory.getLogger(FlashFullMode.class.getName()); + private final static Logger logger = LoggerFactory.getLogger(FlashFullMode.class); /** * Normal update routine, sending complete image diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ToolInfo.java index 12af3d77..403eb788 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ToolInfo.java @@ -17,7 +17,7 @@ public final class ToolInfo private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md private static final long minMinorVersionBootloader = 0; - private static final Logger logger = LoggerFactory.getLogger(ToolInfo.class.getName()); + private static final Logger logger = LoggerFactory.getLogger(ToolInfo.class); private static final String author = "Selfbus"; private static final String tool = "Selfbus-Updater"; private static final String desc = "A Tool for updating firmware of a Selfbus device in a KNX network."; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index 75262edf..ca652f49 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -46,7 +46,7 @@ * @author Oliver Stefan */ public class Updater implements Runnable { - private final static Logger logger = LoggerFactory.getLogger(Updater.class.getName()); + private final static Logger logger = LoggerFactory.getLogger(Updater.class); private final CliOptions cliOptions; private final SBKNXLink sbKNXLink; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java index 7be907d0..eae579d5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java @@ -17,7 +17,7 @@ * Basic utilities usable for the application */ public class Utils { - private static final Logger logger = LoggerFactory.getLogger(Utils.class.getName()); + private static final Logger logger = LoggerFactory.getLogger(Utils.class); public static long streamToLong(byte[] stream, int offset) { return ((stream[offset] & 0xFF)) | diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 2502d1fa..6b519a57 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -95,7 +95,7 @@ public class GuiMain extends JFrame { private CliOptions cliOptions; private Thread updaterThread; public static GuiMain guiMainInstance; - private final static Logger logger = LoggerFactory.getLogger(GuiMain.class.getName()); + private final static Logger logger = LoggerFactory.getLogger(GuiMain.class); private static final Properties userProperties = new Properties(); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java index 621393fd..b5765960 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java @@ -11,7 +11,7 @@ * (based on diff stream, original ROM content, and RAM buffer to store some latest ROM pages already flashed) */ public class Decompressor { - private final static Logger logger = LoggerFactory.getLogger(Decompressor.class.getName()); + private final static Logger logger = LoggerFactory.getLogger(Decompressor.class); private enum State { EXPECT_COMMAND_BYTE, EXPECT_COMMAND_PARAMS, diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index 2c3efd3d..79a256a6 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -15,7 +15,7 @@ import static org.fusesource.jansi.Ansi.Color.*; public class FlashDiff { - private final static Logger logger = LoggerFactory.getLogger(FlashDiff.class.getName()); + private final static Logger logger = LoggerFactory.getLogger(FlashDiff.class); private static final int MINIMUM_PATTERN_LENGTH = 6; // less that this is not efficient (metadata would be larger than data) private static final int MAX_COPY_LENGTH = 2048-1; // 2^12 = 8 bits + 6 bits (remaining in CMD byte). Needs to match flash PAGE_SIZE? private static final int MAX_LENGTH_SHORT = 64-1; // 2^6 = 6 bits (remaining in CMD byte) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java index 2ae449b8..3ebe8350 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java @@ -10,7 +10,7 @@ * Implementation of the UPD/UDP protocol handling */ public final class UPDProtocol { - private static final Logger logger = LoggerFactory.getLogger(UPDProtocol.class.getName()); + private static final Logger logger = LoggerFactory.getLogger(UPDProtocol.class); public static final int COMMAND_POSITION = 2; public static final int DATA_POSITION = 3; From 0cba1d2a1907cd36cabd54a1a1295d6bbaa76f11 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 01:29:52 +0200 Subject: [PATCH 092/359] [Updater] Fix redundant call to `.format` --- .../org/selfbus/updater/mode/differential/FlashDiffUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java index c3446f9d..3e1b35d7 100644 --- a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java +++ b/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java @@ -20,11 +20,11 @@ public static void dumpSideBySide(BinImage img1, BinImage img2) { public static void dumpSideBySide(byte[] ar1, byte[] ar2, int offset) { for (int i = offset; i < offset+2048; i+=8) { - System.out.print(String.format("%04x: %02x %02x %02x %02x %02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x", + System.out.printf("%04x: %02x %02x %02x %02x %02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x", i, ar1[i], ar1[i+1], ar1[i+2], ar1[i+3], ar1[i+4], ar1[i+5], ar1[i+6], ar1[i+7], ar2[i], ar2[i+1], ar2[i+2], ar2[i+3], ar2[i+4], ar2[i+5], ar2[i+6], ar2[i+7] - )); + ); if (isEqual(ar1, ar2, i, 8)) { System.out.println(" EQ"); } else { From 564435bb31c5c605013243999838d11a9456ad3a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 01:36:31 +0200 Subject: [PATCH 093/359] [Updater] Fix path to bootloader --- .../java/org/selfbus/updater/bootloader/BootDescriptor.java | 3 ++- .../org/selfbus/updater/bootloader/BootloaderIdentity.java | 2 +- .../src/main/java/org/selfbus/updater/upd/UDPResult.java | 3 ++- .../src/main/java/org/selfbus/updater/upd/UPDCommand.java | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java index 3e82c5ad..2cb28569 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java @@ -7,7 +7,8 @@ /** * Holds Application Boot Block Descriptor the MCU's bootloader - * see software-arm-lib/Bus-Updater/inc/boot_descriptor_block.h for more information + *

+ * see /firmware_updater/bootloader/inc/boot_descriptor_block.h for more information */ public class BootDescriptor { private static final long INVALID_ADDRESS = 0xFFFFFFFFL; diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java index f87b29ed..38f6f4e2 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java @@ -5,7 +5,7 @@ /** * Holds Bootloader identity information *

- * see software-arm-lib/Bus-Updater/src/update.cpp (method updRequestBootloaderIdentity) for more information. + * see /firmware_updater/bootloader/src/update.cpp (method updRequestBootloaderIdentity) for more information. */ public record BootloaderIdentity(long versionMajor, long versionMinor, diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java index 2b034a6c..16faa2e3 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java @@ -5,7 +5,8 @@ /** * Implementation of the UPD/UDP protocol result commands - * see /Bus-Updater/inc/upd_protocol.h for details + *

+ * see /firmware_updater/bootloader/inc/upd_protocol.h for details */ public enum UDPResult { /** Flash (IAP) Command is executed successfully */ diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java index 505bb055..609b61a0 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java @@ -5,7 +5,8 @@ /** * Implementation of the UPD/UDP protocol control commands - * see /Bus-Updater/inc/upd_protocol.h for details + *

+ * see /firmware_updater/bootloader/inc/upd_protocol.h for details */ public enum UPDCommand { // ERASE_SECTOR(0, "ERASE_SECTOR"), //!< Erase flash sector number (data[3]) @note device must be unlocked From e0901b72163337337fef09afc7d185989ee8540c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 01:44:10 +0200 Subject: [PATCH 094/359] [Updater] Fix redundant initialization --- .../java/org/selfbus/updater/gui/ConColorsToStyledDoc.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java index 82aa2f2e..b9fef3fc 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -65,7 +65,7 @@ public static void Convert(String input, JTextPane textPane) throws BadLocationE Pattern pattern = Pattern.compile(RegExAnsi); Matcher matcher = pattern.matcher(input); - String cleanedString = ""; + String cleanedString; String ansiCode = ""; int index = 0; while (matcher.find()) { @@ -119,9 +119,8 @@ private static boolean processAnsiCursor(String ansiCode, JTextPane textPane) th Pattern pattern = Pattern.compile(RegExAnsiCursor); Matcher matcher = pattern.matcher(ansiCode); - String cursorMovement = ""; while (matcher.find()) { - cursorMovement = matcher.group(1); + String cursorMovement = matcher.group(1); if (cursorMovement.isEmpty()) { continue; } From 4cf3138bd7e69bc0c5b2acc2509dcc57eb4bec65 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 02:38:13 +0200 Subject: [PATCH 095/359] [Updater] Fix delete erroneous code from c&p This code came with my commit 63851db and is most likely c&p code from a calimero `ManagementClientImpl.sendWait` method. This code is not functional nor required here. :cold_sweat: --- .../org/selfbus/updater/SBManagementClientImpl.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java index 6d88bbfe..ca2a0007 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java @@ -1,7 +1,6 @@ package org.selfbus.updater; import tuwien.auto.calimero.DataUnitBuilder; -import tuwien.auto.calimero.IndividualAddress; import tuwien.auto.calimero.KNXInvalidResponseException; import tuwien.auto.calimero.KNXTimeoutException; import tuwien.auto.calimero.link.KNXLinkClosedException; @@ -10,9 +9,6 @@ import tuwien.auto.calimero.mgmt.KNXDisconnectException; import tuwien.auto.calimero.mgmt.ManagementClientImpl; -import java.util.Optional; -import java.util.function.BiFunction; - import static org.selfbus.updater.Mcu.MAX_ASDU_LENGTH; /** @@ -45,15 +41,6 @@ private byte[] prepareAsdu(final int cmd, final byte[] data) { // for Selfbus updater public byte[] sendUpdateData(final Destination dst, final int cmd, final byte[] data) throws KNXTimeoutException, KNXLinkClosedException, KNXInvalidResponseException, KNXDisconnectException, InterruptedException, UpdaterException { - - BiFunction> responseFilter = (responder, apdu) -> { - if (responder.equals(dst.getAddress())) { - return Optional.of(apdu); - } - else { - return Optional.empty(); - } - }; final byte[] asdu = prepareAsdu(cmd, data); byte[] send; From ae470213b1cf0f755143e02ff6f05a951a0f5fae Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 02:44:33 +0200 Subject: [PATCH 096/359] [Updater] Update translations with changes from v1.20 --- .../main/resources/GuiTranslation.properties | 12 ++-- .../resources/GuiTranslation_de.properties | 64 +++++++++---------- .../resources/GuiTranslation_en.properties | 13 ++-- 3 files changed, 43 insertions(+), 46 deletions(-) diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties index 393bc682..854804dd 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties @@ -10,8 +10,8 @@ appDevice=device with application allOptions=all options loadFile=load file medium=medium -serial=serial -tpuart=tpuart +serial=ft12 +tpuart=TPUART knxDeviceAddr=device address knxProgDeviceAddr=bootloader device address knxOwnAddress=own KNX address @@ -21,7 +21,8 @@ knxMessageDelay=delay [ms] knxTimeout=timeout eraseFlash=erase complete flash noFlash=no flash -newDeviceHint=There is a new device with flashed bootloader
\\ and it is in programming mode. +newDeviceHint=There is a new device with flashed bootloader\ + appDeviceHint=A device is flashed with an application over
\ bootloader and now it should be updated portHint=UDP port on (default 3671) @@ -35,9 +36,8 @@ including the physical KNX address and all settings of
\ the device. Only the bootloader is not deleted. stopFlash=stop flashing requestUid=request UID from device -requestUidHint=The UID can be requested from a Selfbus device.
\ -The Selfbus device has to be in programmable
\ -mode into the bootloader +requestUidHint=The UID can be requested from a Selfbus device.\ + KnxGatewayConnectionSettings=KNX gateway connection settings KnxBusSettings=KNX bus settings UpdaterSettings=Updater settings diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties index 312e22c7..283c235b 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties @@ -1,69 +1,67 @@ loadFile=lade Flashdatei fileName=Dateiname selectKnxIpGateway=KNX IP Schnittstelle -ipAddress=IP Adresse +ipAddress=IP-Adresse scenario=Szenario uid=UID startFlash=Starte Flashvorgang language=Sprache -newDevice=neues Gerät -appDevice=Gerät mit Applikation +newDevice=neues Gerät +appDevice=Gerät mit Applikation allOptions=alle Optionen -newDeviceHint=Es wurde auf ein Selfbus Gerät der Bootloader geflasht
\ -und das Gerät wurde in den programmierbaren Modus versetzt. -appDeviceHint=Das Gerät wurde bereits mit einer Applikation
\ -über den Bootloader geflasht und soll nun aktualisiert werden.
\ -Dazu muss die physikalische Adresse und das Applikationsprogramm
\ -in der ETS geschrieben worden sein. +newDeviceHint=Es wurde auf ein Selfbus Gerät der Bootloader geflasht\ + +appDeviceHint=Das Gerät wurde bereits mit einer Applikation
\ +über den Bootloader geflasht und soll nun aktualisiert werden.\ + eraseCompleteFlashHint=MIT VORSICHT VERWENDEN! Es wird der komplette
\ -Speicher gelöscht, inklusive der physikalischen KNX Adresse
\ +Speicher gelöscht, inklusive der physikalischen KNX Adresse
\ und allen Einstellungen. Nur der Bootloader bleibt bestehen. noFlashHint=Flashen der Firmware deaktivieren -uidHint=die UID des Selfbus Gerätes +uidHint=die UID des Selfbus Gerätes fileNameHint=Der Dateipfad kann manuell bearbeitet werden
\ -(Enter zum bestätigen) +(Enter zum bestätigen) ipAddrHint=IP-Adresse der KNX Schnittstelle portHint=TCP/UDP Portnummer der KNX Schnittstelle (default 3671) stopFlash=Stoppe Flashvorgang medium=Medium -serial=Seriell -tpuart=TpUart -knxDeviceAddr=Geräteadresse -knxProgDeviceAddr=Geräteadresse im Bootloader +serial=ft12 +tpuart=TPUART +knxDeviceAddr=Geräteadresse +knxProgDeviceAddr=Geräteadresse im Bootloader knxOwnAddress=eigene KNX Adresse port=Port useNat=NAT -knxMessageDelay=Verzögerung [ms] +knxMessageDelay=Verzögerung [ms] knxTimeout=Timeout -eraseFlash=Lösche kompletten Flash -noFlash=keine Daten übertragen -requestUid=Erfasse UID von Gerät -requestUidHint=Die UID wird von einem Selfbus Gerät abgefragt
\ -und angezeigt. Das Selfbus Gerät muss sich
\ -im programmierbaren Modus im Bootloader befinden +eraseFlash=Lösche kompletten Flash +noFlash=keine Daten übertragen +requestUid=Erfasse UID von Gerät +requestUidHint=Die UID wird von einem Selfbus Gerät abgefragt.\ + UpdaterSettings=Updater KnxBusSettings=KNX Bus KnxGatewayConnectionSettings=KNX Schnittstelle advancedSettings=Erweiterte Einstellungen knxSecureUserPwd=Benutzer Passwort -messagePriority=KNX Telegramm Priorität +messagePriority=KNX Telegramm Priorität knxSecureUser=KNX IP-Secure Benutzer knxSecureUserHint=KNX IP Secure tunneling Benutzer Identifikator
\ (1..127) (default 1) knxSecureUserPwdHint=\ KNX IP Secure tunneling Benutzer Passwort
\ -(Inbetriebnahmepasswort), Anführungszeichen
\ -(") im Passwort könnten nicht funktionieren\ +(Inbetriebnahmepasswort), Anführungszeichen
\ +(") im Passwort könnten nicht funktionieren\ -knxSecureDevicePwd=Gerätepasswort +knxSecureDevicePwd=Gerätepasswort knxSecureDevicePwdHint=\ -KNX IP Secure Geräte Authentifizierung Code
\ -(Authentifizierungscode), Anführungszeichen
\ -(") im Passwort könnten nicht funktionieren\ +KNX IP Secure Geräte Authentifizierung Code
\ +(Authentifizierungscode), Anführungszeichen
\ +(") im Passwort könnten nicht funktionieren\ reloadKnxIpGateways=Gateways erneut laden -selectInterface=wähle Schnittstelle +selectInterface=wähle Schnittstelle diffFlash=differenzieller Flashvorgang -diffFlashHint=Es werden nur veränderte Teile der Software
\ -übertragen [experimentell] +diffFlashHint=Es werden nur veränderte Teile der Software
\ +übertragen [experimentell] logMessageCanceledFlashing=Flashvorgang wurde abgebrochen. \ No newline at end of file diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties index 94ffe20d..ecc29880 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties @@ -9,8 +9,8 @@ language=language newDevice=new device appDevice=device with application allOptions=all options -newDeviceHint=There is a new device with flashed bootloader
\ -and it is in programming mode. +newDeviceHint=There is a new device with flashed bootloader\ + appDeviceHint=A device is flashed with an application over
\ bootloader and now it should be updated eraseCompleteFlashHint=USE WITH CAUTION! Erases the complete flash memory
\ @@ -24,8 +24,8 @@ ipAddrHint=IP address of the KNX interface portHint=UDP port on (default 3671) stopFlash=stop flashing medium=medium -serial=serial -tpuart=tpuart +serial=ft12 +tpuart=TPUART knxDeviceAddr=device address knxProgDeviceAddr=bootloader device address knxOwnAddress=own KNX address @@ -36,9 +36,8 @@ knxTimeout=timeout eraseFlash=erase complete flash noFlash=no flash requestUid=request UID from device -requestUidHint=The UID can be requested from a selbus device.
\ -The selfbus device has to be in programmable
\ -mode into the bootloader +requestUidHint=The UID can be requested from a Selfbus device.\ + UpdaterSettings=Updater settings KnxBusSettings=KNX bus settings KnxGatewayConnectionSettings=KNX gateway connection settings From 03044ca0c80346f88a8dc6688edf864fecb2b351 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 03:10:19 +0200 Subject: [PATCH 097/359] [Updater] Fix typos --- .../updater/mode/differential/FlashDiff.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java index 79a256a6..3fd5fec7 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java @@ -39,9 +39,9 @@ static class SearchResult { final int length; SourceType sourceType = SourceType.FORWARD_ROM; - public SearchResult(int logestCandidateSrcOffset, int logestCandidateLength) { - this.offset = logestCandidateSrcOffset; - this.length = logestCandidateLength; + public SearchResult(int longestCandidateSrcOffset, int longestCandidateLength) { + this.offset = longestCandidateSrcOffset; + this.length = longestCandidateLength; } @Override @@ -55,9 +55,9 @@ public String toString() { } private SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOffset, int oldDataMinimumAddr, int maxLength) { - // search as long as possible ar2[beginOffset..n] bytes (pattern) common with ar1[s..t], where n and t are up to length-1 of appropriate arrays and s is unknown - int logestCandidateSrcOffset = 0; - int logestCandidateLength = 0; + // search as long as possible ar2[beginOffset...n] bytes (pattern) common with ar1[s...t], where n and t are up to length-1 of appropriate arrays and s is unknown + int longestCandidateSrcOffset = 0; + int longestCandidateLength = 0; for (int i = oldDataMinimumAddr; i < ar1.length; i++) { int j = 0; while ((patternOffset + j < ar2.length) @@ -67,24 +67,24 @@ private SearchResult letLongestCommonBytes(byte[] ar1, byte[] ar2, int patternOf ) { j++; } - if (j > logestCandidateLength) { + if (j > longestCandidateLength) { // found better (or first) solution - logestCandidateLength = j; - logestCandidateSrcOffset = i; + longestCandidateLength = j; + longestCandidateSrcOffset = i; } } - if (logestCandidateLength > 0) { + if (longestCandidateLength > 0) { int firstDstPage = patternOffset/FlashPage.PAGE_SIZE; - int lastDstPage = (patternOffset+logestCandidateLength-1)/FlashPage.PAGE_SIZE; + int lastDstPage = (patternOffset+longestCandidateLength-1)/FlashPage.PAGE_SIZE; if (lastDstPage != firstDstPage) { // truncate to a single destination page - logestCandidateLength = (firstDstPage + 1) * FlashPage.PAGE_SIZE - patternOffset; + longestCandidateLength = (firstDstPage + 1) * FlashPage.PAGE_SIZE - patternOffset; } - if (logestCandidateLength >= MINIMUM_PATTERN_LENGTH) { - logger.trace("{} {} {}", ar1[logestCandidateSrcOffset] & 0xff, ar1[logestCandidateSrcOffset + 1] & 0xff, - ar1[logestCandidateSrcOffset + 2] & 0xff); + if (longestCandidateLength >= MINIMUM_PATTERN_LENGTH) { + logger.trace("{} {} {}", ar1[longestCandidateSrcOffset] & 0xff, ar1[longestCandidateSrcOffset + 1] & 0xff, + ar1[longestCandidateSrcOffset + 2] & 0xff); } - return new SearchResult(logestCandidateSrcOffset, logestCandidateLength); + return new SearchResult(longestCandidateSrcOffset, longestCandidateLength); } else { return new SearchResult(0, 0); } From f190e4652f47e7e1cc955d6a2a2b508a55a66ee1 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 03:27:25 +0200 Subject: [PATCH 098/359] [Updater] Delete unused properties --- .../updater/source/src/main/resources/GuiTranslation.properties | 2 -- .../source/src/main/resources/GuiTranslation_de.properties | 2 -- .../source/src/main/resources/GuiTranslation_en.properties | 2 -- 3 files changed, 6 deletions(-) diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties index 854804dd..3635a6c6 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties @@ -4,10 +4,8 @@ ipAddress=IP address scenario=scenario uid=UID startFlash=start flash -language=language newDevice=new device appDevice=device with application -allOptions=all options loadFile=load file medium=medium serial=ft12 diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties index 283c235b..12877bf2 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties @@ -5,10 +5,8 @@ ipAddress=IP-Adresse scenario=Szenario uid=UID startFlash=Starte Flashvorgang -language=Sprache newDevice=neues Gerät appDevice=Gerät mit Applikation -allOptions=alle Optionen newDeviceHint=Es wurde auf ein Selfbus Gerät der Bootloader geflasht\ appDeviceHint=Das Gerät wurde bereits mit einer Applikation
\ diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties index ecc29880..4e34a4e9 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties @@ -5,10 +5,8 @@ ipAddress=IP address scenario=scenario uid=UID startFlash=start flash -language=language newDevice=new device appDevice=device with application -allOptions=all options newDeviceHint=There is a new device with flashed bootloader\ appDeviceHint=A device is flashed with an application over
\ From 80592767674c70d95e321915dcd631fc316ba36a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 05:50:14 +0200 Subject: [PATCH 099/359] [GUI] Refactor `GuiMain.setCliOptions` - move parts of command line creation to new class `CliConverter` - use `OPT_LONG_...` constants from `CliOptions` --- .../java/org/selfbus/updater/CliOptions.java | 56 +++++++++---------- .../org/selfbus/updater/gui/CliConverter.java | 52 +++++++++++++++++ .../java/org/selfbus/updater/gui/GuiMain.java | 56 +++++++------------ 3 files changed, 99 insertions(+), 65 deletions(-) create mode 100644 firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 91e36681..3b0cff8d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -45,71 +45,71 @@ public class CliOptions { private static final Logger logger = LoggerFactory.getLogger(CliOptions.class); private static final String OPT_SHORT_FILENAME = "f"; - private static final String OPT_LONG_FILENAME = "fileName"; + public static final String OPT_LONG_FILENAME = "fileName"; private static final String OPT_SHORT_LOCALHOST = "H"; - private static final String OPT_LONG_LOCALHOST = "localhost"; + public static final String OPT_LONG_LOCALHOST = "localhost"; private static final String OPT_SHORT_LOCALPORT = "P"; - private static final String OPT_LONG_LOCALPORT = "localport"; + public static final String OPT_LONG_LOCALPORT = "localport"; private static final String OPT_SHORT_PORT = "p"; - private static final String OPT_LONG_PORT = "port"; + public static final String OPT_LONG_PORT = "port"; private static final String OPT_SHORT_FT12 = "s"; - private static final String OPT_LONG_FT12 = "serial"; + public static final String OPT_LONG_FT12 = "serial"; private static final String OPT_SHORT_TPUART = "t"; - private static final String OPT_LONG_TPUART = "tpuart"; + public static final String OPT_LONG_TPUART = "tpuart"; private static final String OPT_SHORT_MEDIUM = "m"; - private static final String OPT_LONG_MEDIUM = "medium"; + public static final String OPT_LONG_MEDIUM = "medium"; - private static final String OPT_LONG_USER_ID = "user"; - private static final String OPT_LONG_USER_PASSWORD = "user-pwd"; - private static final String OPT_LONG_DEVICE_PASSWORD = "device-pwd"; + public static final String OPT_LONG_USER_ID = "user"; + public static final String OPT_LONG_USER_PASSWORD = "user-pwd"; + public static final String OPT_LONG_DEVICE_PASSWORD = "device-pwd"; private static final String OPT_SHORT_PROG_DEVICE = "D"; - private static final String OPT_LONG_PROG_DEVICE = "progDevice"; + public static final String OPT_LONG_PROG_DEVICE = "progDevice"; private static final String OPT_SHORT_DEVICE = "d"; - private static final String OPT_LONG_DEVICE = "device"; + public static final String OPT_LONG_DEVICE = "device"; private static final String OPT_SHORT_OWN_ADDRESS = "o"; - private static final String OPT_LONG_OWN_ADDRESS = "own"; + public static final String OPT_LONG_OWN_ADDRESS = "own"; private static final String OPT_SHORT_UID = "u"; - private static final String OPT_LONG_UID = "uid"; + public static final String OPT_LONG_UID = "uid"; - private static final String OPT_LONG_DELAY = "delay"; + public static final String OPT_LONG_DELAY = "delay"; private static final String OPT_SHORT_TUNNEL_V2 = "t2"; - private static final String OPT_LONG_TUNNEL_V2 = "tunnelingv2"; + public static final String OPT_LONG_TUNNEL_V2 = "tunnelingv2"; private static final String OPT_SHORT_TUNNEL_V1 = "t1"; - private static final String OPT_LONG_TUNNEL_V1 = "tunneling"; + public static final String OPT_LONG_TUNNEL_V1 = "tunneling"; private static final String OPT_SHORT_NAT = "n"; - private static final String OPT_LONG_NAT = "nat"; + public static final String OPT_LONG_NAT = "nat"; private static final String OPT_SHORT_ROUTING = "r"; - private static final String OPT_LONG_ROUTING = "routing"; + public static final String OPT_LONG_ROUTING = "routing"; private static final String OPT_SHORT_FULL = "f1"; - private static final String OPT_LONG_FULL = "full"; + public static final String OPT_LONG_FULL = "full"; private static final String OPT_SHORT_HELP = "h"; - private static final String OPT_LONG_HELP = "help"; + public static final String OPT_LONG_HELP = "help"; private static final String OPT_SHORT_VERSION = "v"; - private static final String OPT_LONG_VERSION = "version"; + public static final String OPT_LONG_VERSION = "version"; private static final String OPT_SHORT_NO_FLASH = "f0"; public static final String OPT_LONG_NO_FLASH = "NO_FLASH"; private static final String OPT_SHORT_LOGLEVEL = "l"; - private static final String OPT_LONG_LOGLEVEL = "logLevel"; + public static final String OPT_LONG_LOGLEVEL = "logLevel"; - private static final String OPT_LONG_PRIORITY = "priority"; + public static final String OPT_LONG_PRIORITY = "priority"; - private static final String OPT_LONG_ERASEFLASH = "ERASEFLASH"; + public static final String OPT_LONG_ERASEFLASH = "ERASEFLASH"; - private static final String OPT_LONG_DUMPFLASH = "DUMPFLASH"; + public static final String OPT_LONG_DUMPFLASH = "DUMPFLASH"; - private static final String OPT_LONG_LOGSTATISTIC = "statistic"; + public static final String OPT_LONG_LOGSTATISTIC = "statistic"; - private static final String OPT_SHORT_BLOCKSIZE = "bs"; + public static final String OPT_SHORT_BLOCKSIZE = "bs"; public static final String OPT_LONG_BLOCKSIZE = "blocksize"; private final static List VALID_BLOCKSIZES = Arrays.asList(256, 512, 1024); diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java new file mode 100644 index 00000000..7b27c146 --- /dev/null +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java @@ -0,0 +1,52 @@ +package org.selfbus.updater.gui; + +import javax.swing.JTextField; +import javax.swing.JComboBox; +import java.util.ArrayList; +import java.util.Objects; + +public final class CliConverter { + @SuppressWarnings("unused") + CliConverter() {}; + + public static String argument(String cliLongOption, JTextField textField) { + return argument(cliLongOption, textField.getText(), true); + } + + public static String argument(String cliLongOption, JComboBox comboBox) { + return argument(cliLongOption, Objects.requireNonNull(comboBox.getSelectedItem()).toString(), true); + } + + public static String argument(String cliLongOption) { + return argument(cliLongOption, "", false); + } + + public static String toString(ArrayList list) { + StringBuilder result = new StringBuilder(); + for (String s : list) { + if (!s.isEmpty()) { + result.append(" ").append(s); + } + } + return result.toString().trim(); + } + + public static String argument(String cliLongOption, String argValue, boolean argValueRequired) { + cliLongOption = cliLongOption.trim(); + argValue = argValue.trim(); + + if ((argValueRequired) && (argValue.isEmpty())) { + return ""; //todo maybe throw an exception in this case? + } + + if (argValue.isEmpty()) { + return String.format("--%s", cliLongOption); + } + + if (cliLongOption.isEmpty()) { + return argValue; + } + + return (String.format("--%s=%s", cliLongOption, argValue)); + } +} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 6b519a57..6df206a5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -29,6 +29,8 @@ import java.util.List; import static java.awt.Font.PLAIN; +import static org.selfbus.updater.CliOptions.*; +import static org.selfbus.updater.gui.CliConverter.argument; import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultBackgroundColor; import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultForegroundColor; @@ -284,54 +286,34 @@ private void loadAllParameters() { } } - private String cliParameterToString(ArrayList list) { - StringBuilder result = new StringBuilder(); - for (String s : list) { - if (!s.isEmpty()) { - result.append(" ").append(s); - } - } - return result.toString().trim(); - } - private void setCliOptions() { ArrayList argsList = new ArrayList<>(); - argsList.add(textBoxKnxGatewayIpAddr.getText()); - - argsList.add("-f" + textFieldFileName.getText()); - if (textFieldPort.isVisible() && !Objects.equals(textFieldPort.getText(), "")) - argsList.add("-p" + textFieldPort.getText()); - if (textFieldUid.isVisible() && !Objects.equals(textFieldUid.getText(), "")) - argsList.add("-u" + textFieldUid.getText()); + argsList.add(argument("", textBoxKnxGatewayIpAddr)); + argsList.add(argument(OPT_LONG_FILENAME, textFieldFileName)); + argsList.add(argument(OPT_LONG_PORT, textFieldPort)); + argsList.add(argument(OPT_LONG_UID, textFieldUid)); if (comboBoxMedium.isVisible()) - argsList.add("-m" + comboBoxMedium.getSelectedItem()); - if (textFieldSerial.isVisible() && !Objects.equals(textFieldSerial.getText(), "")) - argsList.add("-s" + textFieldSerial.getText()); - if (textFieldTpuart.isVisible() && !Objects.equals(textFieldTpuart.getText(), "")) - argsList.add("-t" + textFieldTpuart.getText()); - if (textFieldDeviceAddress.isVisible() && !Objects.equals(textFieldDeviceAddress.getText(), "")) - argsList.add("-d" + textFieldDeviceAddress.getText()); - if (textFieldBootloaderDeviceAddress.isVisible() && !Objects.equals(textFieldBootloaderDeviceAddress.getText(), "")) - argsList.add("-D" + textFieldBootloaderDeviceAddress.getText()); - if (textFieldOwnAddress.isVisible() && !Objects.equals(textFieldOwnAddress.getText(), "")) - argsList.add("-o" + textFieldOwnAddress.getText()); + argsList.add(argument(OPT_LONG_MEDIUM, comboBoxMedium)); + argsList.add(argument(OPT_LONG_FT12, textFieldSerial)); + argsList.add(argument(OPT_LONG_TPUART, textFieldTpuart)); + argsList.add(argument(OPT_LONG_DEVICE, textFieldDeviceAddress)); + argsList.add(argument(OPT_LONG_PROG_DEVICE, textFieldBootloaderDeviceAddress)); + argsList.add(argument(OPT_LONG_OWN_ADDRESS, textFieldOwnAddress)); if ((CheckBoxDiffFlash.isVisible() && !CheckBoxDiffFlash.isSelected()) || !CheckBoxDiffFlash.isVisible()) - argsList.add("-f1"); + argsList.add(argument(OPT_LONG_FULL)); if (natCheckBox.isVisible() && natCheckBox.isSelected()) - argsList.add("-n"); - if (textFieldDelay.isVisible() && !Objects.equals(textFieldDelay.getText(), "")) - argsList.add("--delay" + textFieldDelay.getText()); - if (textFieldTimeout.isVisible() && !Objects.equals(textFieldTimeout.getText(), "")) - argsList.add("--timeout" + textFieldTimeout.getText()); + argsList.add(argument(OPT_LONG_NAT)); + argsList.add(argument(OPT_LONG_DELAY, textFieldDelay)); if (eraseCompleteFlashCheckBox.isVisible() && eraseCompleteFlashCheckBox.isSelected()) - argsList.add("--ERASEFLASH"); - if (noFlashCheckBox.isVisible() && noFlashCheckBox.isSelected()) argsList.add("-f0"); + argsList.add(argument(OPT_LONG_ERASEFLASH)); + if (noFlashCheckBox.isVisible() && noFlashCheckBox.isSelected()) + argsList.add(argument(OPT_LONG_NO_FLASH)); String updaterFileName = String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()); String[] args = new String[argsList.size()]; args = argsList.toArray(args); - logger.info("java -jar {} {}", updaterFileName, cliParameterToString(argsList)); + logger.info("java -jar {} {}", updaterFileName, CliConverter.toString(argsList)); try { // read in user-supplied command line options this.cliOptions = new CliOptions(args, updaterFileName, From bf9e65fc3c3e53e5a8f6fad700aabdd20bfaaa92 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 05:52:37 +0200 Subject: [PATCH 100/359] [Updater] Fix `--medium` could be used with `--serial` or `--tpuart` --- .../src/main/java/org/selfbus/updater/CliOptions.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 3b0cff8d..d4e969de 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -306,12 +306,12 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe // options will be shown in order as they are added to cliOptions cliOptions.addOption(fileName); - // ft12 or medium, not both + cliOptions.addOption(medium); + + // ft12 or tpuart, not both OptionGroup grpBusAccess = new OptionGroup(); - grpBusAccess.addOption(medium); grpBusAccess.addOption(ft12); grpBusAccess.addOption(tpuart); - cliOptions.addOptionGroup(grpBusAccess); cliOptions.addOption(device); From d0f5865ce11977fb7fc765e61801e12a2baec77c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 05:54:48 +0200 Subject: [PATCH 101/359] [Updater] Group for tunneling v1, v2 (exclusive) or routing --- .../src/main/java/org/selfbus/updater/CliOptions.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index d4e969de..a407fe4b 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -329,10 +329,15 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe cliOptions.addOption(localhost); cliOptions.addOption(localport); cliOptions.addOption(port); - cliOptions.addOption(tunnelingV2); - cliOptions.addOption(tunnelingV1); + + // Tunneling v1, Tunneling v2 or routing + OptionGroup grpTunnelingOrRouting = new OptionGroup(); + grpTunnelingOrRouting.addOption(tunnelingV2); + grpTunnelingOrRouting.addOption(tunnelingV1); + grpTunnelingOrRouting.addOption(routing); + cliOptions.addOptionGroup(grpTunnelingOrRouting); + cliOptions.addOption(nat); - cliOptions.addOption(routing); // help or version, not both OptionGroup grpHelper = new OptionGroup(); From a32ce916c20e0a6d9db5492ff314488069c792c3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 06:04:43 +0200 Subject: [PATCH 102/359] [Updater] Update cli changes in README --- firmware_updater/updater/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 51f42903..d5bf114f 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -19,10 +19,10 @@ windows: gradlew.bat fatJar ## Usage ``` -java -jar SB_updater-x.xx-all.jar [-f ] [-m | -s | -t - ] [-d ] [-D ] [-o ] [--priority ] +java -jar SB_updater-x.xx-all.jar [-f ] [-m ] [-s | -t + ] [-d ] [-D ] [-o ] [--priority ] [-bs <256|512|1024>] [--user ] [--user-pwd ] [--device-pwd ] [-u - ] [-f1] [-H ] [-P ] [-p ] [-t2] [-t1] [-n] [-r] [-h | -v] + ] [-f1] [-H ] [-P ] [-p ] [-t2 | -t1 | -r] [-n] [-h | -v] [--delay ] [-l ] [--ERASEFLASH] [--DUMPFLASH ] [-f0] [--statistic] @@ -55,9 +55,9 @@ Selfbus KNX-Firmware update tool options: -p,--port UDP port on (default 3671) -t2,--tunnelingv2 use KNXnet/IP tunneling v2 (TCP) (experimental) -t1,--tunneling use KNXnet/IP tunneling v1 (UDP) + -r,--routing use KNXnet/IP routing/multicast (experimental) -n,--nat enable Network Address Translation (NAT) (only available with tunneling v1) - -r,--routing use KNXnet/IP routing/multicast (experimental) -h,--help show this help message -v,--version show tool/library version --delay delay telegrams during data transmission to reduce bus From 205f247fa0a30daac328e1815bccac937b35b639 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 06:42:12 +0200 Subject: [PATCH 103/359] [Updater] Remove also `--timeout` CLI option in GUI `--timeout` was in cli removed with commit 301cf89 --- .../java/org/selfbus/updater/gui/GuiMain.form | 16 ---------------- .../java/org/selfbus/updater/gui/GuiMain.java | 12 ------------ .../src/main/resources/GuiTranslation.properties | 1 - .../main/resources/GuiTranslation_de.properties | 1 - .../main/resources/GuiTranslation_en.properties | 1 - 5 files changed, 31 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.form b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.form index 637361d4..40885a28 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.form +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.form @@ -369,22 +369,6 @@ - - - - - - - - - - - - - - - - diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 6df206a5..c35fea4d 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -58,7 +58,6 @@ public class GuiMain extends JFrame { private JCheckBox natCheckBox; private JTextField textFieldOwnAddress; private JTextField textFieldDelay; - private JTextField textFieldTimeout; private JCheckBox eraseCompleteFlashCheckBox; private JCheckBox noFlashCheckBox; private JLabel labelMedium; @@ -68,7 +67,6 @@ public class GuiMain extends JFrame { private JLabel labelBootloaderDeviceAddr; private JLabel labelOwnAddress; private JLabel labelDelay; - private JLabel labelTimeout; private JLabel labelPortHint; private JLabel labeIIpHint; private JLabel labelFileNameHint; @@ -226,7 +224,6 @@ private void saveAllParameters() { userProperties.setProperty("DeviceAddress", textFieldDeviceAddress.getText()); userProperties.setProperty("OwnAddress", textFieldOwnAddress.getText()); userProperties.setProperty("DelayMs", textFieldDelay.getText()); - userProperties.setProperty("Timeout", textFieldTimeout.getText()); userProperties.setProperty("TelegramPriority", Objects.requireNonNull(comboBoxKnxTelegramPriority.getSelectedItem()).toString()); userProperties.setProperty("SecureUser", textFieldKnxSecureUser.getText()); userProperties.setProperty("SecureUserPassword", textFieldKnxSecureUserPwd.getText()); @@ -263,7 +260,6 @@ private void loadAllParameters() { textFieldDeviceAddress.setText(userProperties.getProperty("DeviceAddress")); textFieldOwnAddress.setText(userProperties.getProperty("OwnAddress")); textFieldDelay.setText(userProperties.getProperty("DelayMs")); - textFieldTimeout.setText(userProperties.getProperty("Timeout")); comboBoxKnxTelegramPriority.setSelectedItem(userProperties.getProperty("TelegramPriority")); textFieldKnxSecureUser.setText(userProperties.getProperty("SecureUser")); textFieldKnxSecureUserPwd.setText(userProperties.getProperty("SecureUserPassword")); @@ -544,9 +540,6 @@ private void InitGuiElementsVisibility() { GuiObjectsMap.put(labelDelay, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.ADVSET)); GuiObjectsMap.put(textFieldDelay, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.ADVSET)); - GuiObjectsMap.put(labelTimeout, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.ADVSET)); - GuiObjectsMap.put(textFieldTimeout, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.ADVSET)); - GuiObjectsMap.put(CheckBoxDiffFlash, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.ADVSET)); GuiObjectsMap.put(labelFullFlashHint, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.ADVSET)); @@ -751,11 +744,6 @@ private void setGuiElementsVisibility() { labelDelay = new JLabel(); this.$$$loadLabelText$$$(labelDelay, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxMessageDelay")); panel1.add(labelDelay, new GridConstraints(23, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); - textFieldTimeout = new JTextField(); - panel1.add(textFieldTimeout, new GridConstraints(24, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); - labelTimeout = new JLabel(); - this.$$$loadLabelText$$$(labelTimeout, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxTimeout")); - panel1.add(labelTimeout, new GridConstraints(24, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); panel1.add(comboBoxKnxTelegramPriority, new GridConstraints(25, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelTelegramPriority = new JLabel(); this.$$$loadLabelText$$$(labelTelegramPriority, this.$$$getMessageFromBundle$$$("GuiTranslation", "messagePriority")); diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties index 3635a6c6..9d2138b4 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties @@ -16,7 +16,6 @@ knxOwnAddress=own KNX address port=port useNat=NAT knxMessageDelay=delay [ms] -knxTimeout=timeout eraseFlash=erase complete flash noFlash=no flash newDeviceHint=There is a new device with flashed bootloader\ diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties index 12877bf2..5c2a951a 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties @@ -31,7 +31,6 @@ knxOwnAddress=eigene KNX Adresse port=Port useNat=NAT knxMessageDelay=Verzögerung [ms] -knxTimeout=Timeout eraseFlash=Lösche kompletten Flash noFlash=keine Daten übertragen requestUid=Erfasse UID von Gerät diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties index 4e34a4e9..497df8d5 100644 --- a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties +++ b/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties @@ -30,7 +30,6 @@ knxOwnAddress=own KNX address port=port useNat=NAT knxMessageDelay=delay [ms] -knxTimeout=timeout eraseFlash=erase complete flash noFlash=no flash requestUid=request UID from device From 12497347331afcd58c3cd9dfff424d129bd7a6e8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 07:30:51 +0200 Subject: [PATCH 104/359] [Updater] Use public `OPT_LONG_FILENAME` --- .../source/src/main/java/org/selfbus/updater/CliOptions.java | 4 ---- .../source/src/main/java/org/selfbus/updater/Updater.java | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 54e75c31..a9de891b 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -776,10 +776,6 @@ public boolean logStatistics() { return logStatistics; } - public String getOptionLongFileName() { - return OPT_LONG_FILENAME; - } - public int getBlockSize() { return blockSize; } diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java index ca652f49..0587c4ed 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java @@ -194,7 +194,7 @@ public void run() { else { System.out.println(); logger.info(ansi().bg(RED).fg(BLACK).a("No firmware file (*.hex) specified! Specify with --{}").reset().toString(), - cliOptions.getOptionLongFileName()); + CliOptions.OPT_LONG_FILENAME); logger.info(ansi().fgBright(YELLOW).a("Reading only device information").reset().toString()); System.out.println(); } From 377fee532d0a84bec8655aa19f3173f49f8ed3a3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 07:31:38 +0200 Subject: [PATCH 105/359] [Updater] Fix warnings --- .../source/src/main/java/org/selfbus/updater/SBKNXLink.java | 3 +-- .../src/main/java/org/selfbus/updater/gui/CliConverter.java | 2 +- .../updater/source/src/main/resources/javax.usb.properties | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java index 5fe019e6..63777833 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java @@ -114,8 +114,7 @@ private KNXNetworkLink doOpenLink() throws KNXException, return linkTpuart; } else if (!cliOptions.getUsbInterface().isEmpty()) { // create USB network link - KNXNetworkLinkUsb linkUsb = new KNXNetworkLinkUsb(cliOptions.getUsbInterface(), medium); - return linkUsb; + return new KNXNetworkLinkUsb(cliOptions.getUsbInterface(), medium); } // create local and remote socket address for network link diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java index 7b27c146..b4d1455c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java @@ -7,7 +7,7 @@ public final class CliConverter { @SuppressWarnings("unused") - CliConverter() {}; + CliConverter() {} public static String argument(String cliLongOption, JTextField textField) { return argument(cliLongOption, textField.getText(), true); diff --git a/firmware_updater/updater/source/src/main/resources/javax.usb.properties b/firmware_updater/updater/source/src/main/resources/javax.usb.properties index cdd31269..af654c33 100644 --- a/firmware_updater/updater/source/src/main/resources/javax.usb.properties +++ b/firmware_updater/updater/source/src/main/resources/javax.usb.properties @@ -1,3 +1,4 @@ +# suppress inspection "UnusedProperty" for whole file # Same values like in calimero-core/javax.usb.properties # tell the USB host manager of javax-usb to use usb4java javax.usb.services = org.usb4java.javax.Services From 82bb6b56fe822ba95394818118bb9ab407eeefae Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 13:38:02 +0200 Subject: [PATCH 106/359] [Updater] Bump net.harawata:appdirs from 1.2.1 to 1.2.2 --- firmware_updater/updater/source/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 5b69e9fa..39c584ee 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -97,7 +97,7 @@ dependencies { implementation 'com.github.calimero:calimero-rxtx:2.6-rc1' // find specific directories under linux and windows - implementation 'net.harawata:appdirs:1.2.1' + implementation 'net.harawata:appdirs:1.2.2' // For search in byte array implementation 'com.google.guava:guava:33.2.1-jre' From d1df9111de42a23830856646f7c4d67ad2f88dd8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 14:07:09 +0200 Subject: [PATCH 107/359] [Updater] Cleanup/reorganize .gitignore --- firmware_updater/updater/.gitignore | 1 - firmware_updater/updater/source/.gitignore | 2 ++ firmware_updater/updater/source/bin/.gitignore | 3 --- 3 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 firmware_updater/updater/.gitignore delete mode 100644 firmware_updater/updater/source/bin/.gitignore diff --git a/firmware_updater/updater/.gitignore b/firmware_updater/updater/.gitignore deleted file mode 100644 index 92eb2403..00000000 --- a/firmware_updater/updater/.gitignore +++ /dev/null @@ -1 +0,0 @@ -settings.xml diff --git a/firmware_updater/updater/source/.gitignore b/firmware_updater/updater/source/.gitignore index 8d3bf67a..f5c3e9cd 100644 --- a/firmware_updater/updater/source/.gitignore +++ b/firmware_updater/updater/source/.gitignore @@ -3,3 +3,5 @@ build log out +bin +settings.xml \ No newline at end of file diff --git a/firmware_updater/updater/source/bin/.gitignore b/firmware_updater/updater/source/bin/.gitignore deleted file mode 100644 index 7ac70cd7..00000000 --- a/firmware_updater/updater/source/bin/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/main/ -/test/ -/default/ From c993ffe5f5a858779b32f245995ff171c8387bfb Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 14:08:44 +0200 Subject: [PATCH 108/359] [Updater] Use constant for `settings.xml` handling --- .../java/org/selfbus/updater/gui/GuiMain.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index c35fea4d..6d74069a 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -97,7 +97,7 @@ public class GuiMain extends JFrame { public static GuiMain guiMainInstance; private final static Logger logger = LoggerFactory.getLogger(GuiMain.class); private static final Properties userProperties = new Properties(); - + private static final String FILENAME_SETTINGS = "settings.xml"; public GuiMain() { $$$setupUI$$$(); @@ -206,7 +206,7 @@ public void actionPerformed(ActionEvent e) { } }; - private void saveAllParameters() { + private void saveAllParameters(String fileName) { userProperties.setProperty("AdvancedSettings", advancedSettingsCheckBox.isSelected() ? "true" : "false"); userProperties.setProperty("GatewayIpAddr", textBoxKnxGatewayIpAddr.getText()); userProperties.setProperty("GatewayPort", textFieldPort.getText()); @@ -232,17 +232,17 @@ private void saveAllParameters() { userProperties.setProperty("WindowSizeWidth", String.valueOf(this.getSize().width)); try { - userProperties.storeToXML(new FileOutputStream("settings.xml"), ""); + userProperties.storeToXML(new FileOutputStream(fileName), ""); } catch (IOException ex) { throw new RuntimeException(ex); } } - private void loadAllParameters() { + private void loadAllParameters(String fileName) { - if (new File("settings.xml").exists()) { + if (new File(fileName).exists()) { try { - userProperties.loadFromXML(new FileInputStream("settings.xml")); + userProperties.loadFromXML(new FileInputStream(fileName)); advancedSettingsCheckBox.setSelected(Boolean.parseBoolean(userProperties.getProperty("AdvancedSettings"))); textBoxKnxGatewayIpAddr.setText(userProperties.getProperty("GatewayIpAddr")); @@ -351,14 +351,14 @@ public void startUpdaterGui() { fillMediumComboBox(); fillTelegramPriorityComboBox(); - loadAllParameters(); + loadAllParameters(FILENAME_SETTINGS); mainScrollPane.getVerticalScrollBar().setUnitIncrement(10); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { - saveAllParameters(); + saveAllParameters(FILENAME_SETTINGS); } }); } From cd898ec2b82b5731d2ddba545de436cc730e3904 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 14:19:45 +0200 Subject: [PATCH 109/359] [Updater] Bump commons-cli:commons-cli from 1.5.0 to 1.8.0 --- firmware_updater/updater/source/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 39c584ee..31034786 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -103,7 +103,7 @@ dependencies { implementation 'com.google.guava:guava:33.2.1-jre' // command line option - implementation 'commons-cli:commons-cli:1.5.0' + implementation 'commons-cli:commons-cli:1.8.0' // console and file logging implementation 'ch.qos.logback:logback-classic:1.5.6' From 688e97f08aa103f003571cb75b8a15b568aa9b87 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 14:37:42 +0200 Subject: [PATCH 110/359] [Updater] Bump org.fusesource.jansi:jansi from 2.4.0 to 2.4.1 --- firmware_updater/updater/source/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 31034786..961cd6e6 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -109,7 +109,7 @@ dependencies { implementation 'ch.qos.logback:logback-classic:1.5.6' // Console ansi color support - implementation 'org.fusesource.jansi:jansi:2.4.0' + implementation 'org.fusesource.jansi:jansi:2.4.1' // GUI implementation files('libs/forms_rt.jar') From 309245c69a96d9dc534c9f5b27303f8a376ccfa6 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 14:59:08 +0200 Subject: [PATCH 111/359] [Updater] With c081ba3 `--fileName` is no more required --- .../source/src/main/java/org/selfbus/updater/CliOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index a9de891b..4362be10 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -196,7 +196,7 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe Option fileName = Option.builder(OPT_SHORT_FILENAME).longOpt(OPT_LONG_FILENAME) .argName("filename") .numberOfArgs(1) - .required(false) ///\todo .required(true) leads to an exception with --help or --version + .required(false) .type(String.class) .desc("Filename of hex file to program").build(); Option localhost = Option.builder(OPT_SHORT_LOCALHOST).longOpt(OPT_LONG_LOCALHOST) From 9d90fd3c11072fbab5ad1190b8d81b5d69a932d2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 15:49:29 +0200 Subject: [PATCH 112/359] [Updater] Suppress inspection on `javax.javax.usb.properties` --- .../updater/source/src/main/resources/javax.usb.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware_updater/updater/source/src/main/resources/javax.usb.properties b/firmware_updater/updater/source/src/main/resources/javax.usb.properties index af654c33..2d32e866 100644 --- a/firmware_updater/updater/source/src/main/resources/javax.usb.properties +++ b/firmware_updater/updater/source/src/main/resources/javax.usb.properties @@ -1,3 +1,4 @@ +# suppress inspection "GrazieInspection" for whole file # suppress inspection "UnusedProperty" for whole file # Same values like in calimero-core/javax.usb.properties # tell the USB host manager of javax-usb to use usb4java From 0a49ff2f05542890636a28490ccc15d022d1a782 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 15:56:11 +0200 Subject: [PATCH 113/359] [Updater] Use `CliInvalidException` for logic errors There is no reason to log/print the stack-trace on `CliInvalidException` (logic errors, e.g. IP-Secure together with USB-Interface) --- .../selfbus/updater/CliInvalidException.java | 13 +++++ .../java/org/selfbus/updater/CliOptions.java | 56 ++++++++++++------- 2 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliInvalidException.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliInvalidException.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliInvalidException.java new file mode 100644 index 00000000..d1d9f9f1 --- /dev/null +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliInvalidException.java @@ -0,0 +1,13 @@ +package org.selfbus.updater; + +import org.apache.commons.cli.ParseException; + +public class CliInvalidException extends ParseException { + public CliInvalidException(String message) { + super(message); + } + + public CliInvalidException(Throwable e) { + super(e); + } +} diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 4362be10..0a701da7 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -570,35 +570,35 @@ private void parse(final String[] args) { // nat only possible with tunneling v1 if (nat() && (!tunnelingV1())) { - throw new ParseException(String.format(ansi().fg(RED).a("Option --%s can only be used together with --%s").reset().toString(), + throw new CliInvalidException(String.format(ansi().fg(RED).a("Option --%s can only be used together with --%s").reset().toString(), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1)); } // nat not allowed with tunneling v2 if (nat() && (tunnelingV2())) { - throw new ParseException(String.format(ansi().fg(RED).a("Option --%s can not be used together with --%s").reset().toString(), + throw new CliInvalidException(String.format(ansi().fg(RED).a("Option --%s can not be used together with --%s").reset().toString(), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2)); } // check IP-secure configuration if (!(userPassword().isEmpty()) || !(devicePassword().isEmpty())) { if (knxInterface() == null) { - throw new ParseException(ansi().fg(RED).a("No IP-Interface specified for IP-secure").reset().toString()); + throw new CliInvalidException(ansi().fg(RED).a("No IP-Interface specified for IP-secure").reset().toString()); } else if (!ft12().isEmpty()) { - throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_FT12)).reset().toString()); + throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_FT12)).reset().toString()); } else if (!tpuart().isEmpty()) { - throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_TPUART)).reset().toString()); + throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TPUART)).reset().toString()); } else if (nat()) { - throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_NAT)).reset().toString()); + throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_NAT)).reset().toString()); } else if (tunnelingV1()) { - throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_TUNNEL_V1)).reset().toString()); + throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TUNNEL_V1)).reset().toString()); } else if (tunnelingV2()) { - throw new ParseException(ansi().fg(RED).a(String.format("IP-secure is not possible with %s", OPT_LONG_TUNNEL_V2)).reset().toString()); + throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TUNNEL_V2)).reset().toString()); } } @@ -610,26 +610,42 @@ else if (tunnelingV2()) { if (!getUsbInterface().isEmpty()) interfacesSet++; if (interfacesSet > 1) { - throw new ParseException(ansi().fg(RED).a("Only one bus interface can be used.").reset().toString()); + throw new CliInvalidException(ansi().fg(RED).a("Only one bus interface can be used.").reset().toString()); } else if (interfacesSet == 0) { - throw new ParseException(ansi().fg(RED).a("No bus interface specified.").reset().toString()); + throw new CliInvalidException(ansi().fg(RED).a("No bus interface specified.").reset().toString()); } + } + catch (CliInvalidException e) { + logCliException(e, args, false); + System.exit(0); + } + catch (ParseException | KNXFormatException e) { + logCliException(e, args, true); + System.exit(0); + } + } - } catch (ParseException | KNXFormatException e) { - StringBuilder cliParsed = new StringBuilder(); - for (String arg : args) { - cliParsed.append(String.format("%s ", arg)); - } - logger.error("Invalid command line parameters:"); - logger.error("{}", cliParsed); - logger.error(ansi().fg(RED).a(e.getMessage()).reset().toString()); - logger.error("For more information about the usage start with --{}", OPT_LONG_HELP); + private void logCliException(Exception e, String[] args, boolean verbose) { + String cliParsed = argsToString(args); + System.out.printf("Invalid command line parameters: '%s'%s", cliParsed, + System.lineSeparator()); // don't log possible IP-secure parameters + logger.debug("Invalid command line parameters:"); + logger.error(ansi().fg(RED).a(e.getMessage()).reset().toString()); + logger.error("For more information about the usage start with --{}", OPT_LONG_HELP); + if (verbose) { logger.error("", e); - System.exit(0); } } + private String argsToString(final String[] args) { + StringBuilder result = new StringBuilder(); + for (String arg : args) { + result.append(String.format("%s ", arg)); + } + return result.toString().trim(); + } + public String helpToString() { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); From 0bfe3744b444cb4c0e3cf29f306fcaec007ccf1b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 15:57:36 +0200 Subject: [PATCH 114/359] [Updater] log styling --- .../src/main/java/org/selfbus/updater/CliOptions.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index 0a701da7..d74e4f9c 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -387,7 +387,7 @@ private void parse(final String[] args) { priority = Priority.get(cmdLine.getOptionValue(OPT_LONG_PRIORITY)); } catch (KNXIllegalArgumentException e) { - logger.warn(ansi().fg(RED).a("invalid {} {}, using {}").reset().toString(), + logger.warn(ansi().fg(RED).a("invalid --{} {}, using {}").reset().toString(), OPT_LONG_PRIORITY, cmdLine.getOptionValue(OPT_LONG_PRIORITY), priority); } } @@ -542,17 +542,17 @@ private void parse(final String[] args) { logger.debug("knxInterface={}", knxInterface); if (cmdLine.hasOption(OPT_LONG_USER_ID)) { - logger.debug("KNX IP Secure userId is set"); // don't log knx secure ip specific options + logger.debug("KNX IP Secure --{} is set", OPT_LONG_USER_ID); // log only that it´s, but not the actual value userId = ((Number)cmdLine.getParsedOptionValue(OPT_LONG_USER_ID)).intValue(); } if (cmdLine.hasOption(OPT_LONG_USER_PASSWORD)) { - logger.debug("KNX IP Secure userPassword is set"); // don't log knx secure ip specific options + logger.debug("KNX IP Secure --{} is set", OPT_LONG_USER_PASSWORD); // log only that it´s, but not the actual value userPassword = cmdLine.getOptionValue(OPT_LONG_USER_PASSWORD); } if (cmdLine.hasOption(OPT_LONG_DEVICE_PASSWORD)) { - logger.debug("KNX IP Secure devicePassword is set"); // don't log knx secure ip specific options + logger.debug("KNX IP Secure --{} is set", OPT_LONG_DEVICE_PASSWORD); // log only that it´s, but not the actual value devicePassword = cmdLine.getOptionValue(OPT_LONG_DEVICE_PASSWORD); } @@ -652,7 +652,7 @@ public String helpToString() { helper.setWidth(PRINT_WIDTH); helper.setOptionComparator(null); helper.printHelp(pw, helper.getWidth(), helpApplicationName + " ", - "\n" + helpHeader + ":\n", cliOptions, helper.getLeftPadding(), + System.lineSeparator() + helpHeader + ":" + System.lineSeparator(), cliOptions, helper.getLeftPadding(), helper.getDescPadding(), helpFooter, true); pw.flush(); return sw.toString(); From 2334d439c693100f248e0a7e04755e0b070b26a9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 15:59:06 +0200 Subject: [PATCH 115/359] [Updater] More cli-logic checks on IP-Secure, USB --- .../src/main/java/org/selfbus/updater/CliOptions.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java index d74e4f9c..4aa2b266 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java @@ -585,6 +585,9 @@ private void parse(final String[] args) { if (knxInterface() == null) { throw new CliInvalidException(ansi().fg(RED).a("No IP-Interface specified for IP-secure").reset().toString()); } + else if (!getUsbInterface().isEmpty()) { + throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_USB)).reset().toString()); + } else if (!ft12().isEmpty()) { throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_FT12)).reset().toString()); } @@ -600,6 +603,12 @@ else if (tunnelingV1()) { else if (tunnelingV2()) { throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TUNNEL_V2)).reset().toString()); } + + // ensure that all three IP-Secure arguments are set + if ((userPassword().isEmpty()) || (devicePassword().isEmpty())) { + throw new CliInvalidException(ansi().fg(RED).a(String.format("For IP-secure --%s, --%s and --%s must be set", OPT_LONG_USER_ID, + OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD)).reset().toString()); + } } int interfacesSet = 0; From 3cb63b40d992bd13d2e8243bf435415dfa953d5c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 17:17:42 +0200 Subject: [PATCH 116/359] [Updater] Fix gradle warnings --- firmware_updater/updater/source/build.gradle | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 961cd6e6..2f7cf03b 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -8,7 +8,10 @@ group 'org.selfbus' version '1.20' ///\todo also change in ../README.md and ToolInfo.java (String version) java { - sourceCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + // withJavadocJar() //todo fails, because there is no real docu } application { @@ -47,13 +50,14 @@ artifacts { archives fatJar } -gradle.projectsEvaluated{ - tasks.withType(JavaCompile){ - options.compilerArgs << "-Xlint:unchecked" - options.compilerArgs << "-Xlint:deprecation" - options.compilerArgs << "-Xlint:all" - } -} +compileJava.options.compilerArgs = [ + '-Xlint:all', // enable all warnings + '-Xlint:-serial', // disable serialisation warning +] + +compileTestJava.options.compilerArgs = [ + '-Xlint:all', // enable all warnings +] testing { // From 5fedec86571b62a089226b7365adb9752151cfb5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 17:27:52 +0200 Subject: [PATCH 117/359] [Updater] Ensure UTF-8 encoding, add description --- firmware_updater/updater/source/build.gradle | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 2f7cf03b..a9dbeff8 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -4,8 +4,9 @@ plugins { id 'jvm-test-suite' } -group 'org.selfbus' -version '1.20' ///\todo also change in ../README.md and ToolInfo.java (String version) +group = 'org.selfbus' +version = '1.20' ///\todo also change in ../README.md and ToolInfo.java (String version) +description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { toolchain { @@ -50,6 +51,11 @@ artifacts { archives fatJar } +// ensure UTF-8 encoding +compileJava.options.encoding = 'UTF-8' +compileTestJava.options.encoding = 'UTF-8' +javadoc.options.encoding = 'UTF-8' + compileJava.options.compilerArgs = [ '-Xlint:all', // enable all warnings '-Xlint:-serial', // disable serialisation warning From ea89fc71d2b2e233541c08f3773708e5fc2400d7 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 18:15:35 +0200 Subject: [PATCH 118/359] [Updater] Serialisation warning disabled in 3cb63b4 --- .../src/main/java/org/selfbus/updater/UpdaterException.java | 1 - .../source/src/main/java/org/selfbus/updater/gui/GuiMain.java | 1 - 2 files changed, 2 deletions(-) diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/UpdaterException.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/UpdaterException.java index 638895e4..6cb6dba5 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/UpdaterException.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/UpdaterException.java @@ -3,7 +3,6 @@ /** * Basic exception throwable by the application */ -@SuppressWarnings("serial") public class UpdaterException extends Exception { /** * Constructs a new UpdaterException without a detail message. diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java index 6d74069a..e7945297 100644 --- a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java @@ -34,7 +34,6 @@ import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultBackgroundColor; import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultForegroundColor; -@SuppressWarnings("serial") public class GuiMain extends JFrame { private JButton buttonLoadFile; private JTextField textBoxKnxGatewayIpAddr; From b7f2c55ac1c8a802ba4055f3d6c66dc10a200e59 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 18:59:40 +0200 Subject: [PATCH 119/359] [Updater] MANIFEST.MF is created dynamically during build --- firmware_updater/updater/source/src/META-INF/MANIFEST.MF | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 firmware_updater/updater/source/src/META-INF/MANIFEST.MF diff --git a/firmware_updater/updater/source/src/META-INF/MANIFEST.MF b/firmware_updater/updater/source/src/META-INF/MANIFEST.MF deleted file mode 100644 index 6c11289e..00000000 --- a/firmware_updater/updater/source/src/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Main-Class: Updater - From 4c88a762fc033c7647605e3aff855d9e8dab2d4c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 19:14:57 +0200 Subject: [PATCH 120/359] [Updater] Add sourceSets, move .hex files to tests --- firmware_updater/updater/source/build.gradle | 12 + .../resources/updater.ino.slto.v1.hex | 3000 ++++++++-------- .../resources/updater.ino.slto.v2.hex | 3002 ++++++++--------- 3 files changed, 3013 insertions(+), 3001 deletions(-) rename firmware_updater/updater/source/src/{main => test}/resources/updater.ino.slto.v1.hex (97%) rename firmware_updater/updater/source/src/{main => test}/resources/updater.ino.slto.v2.hex (97%) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index a9dbeff8..7e16af9a 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -25,6 +25,18 @@ jar { } } +sourceSets { + main { + java.srcDirs = ['src/main'] + resources.srcDirs = ['src/main/resources'] + } + test { + java.srcDirs = ['src/test'] + //java.exclude 'dir1/', 'dir2' + resources.srcDirs = ['src/test/resources'] + } +} + task fatJar(type: Jar) { manifest.from jar.manifest archiveClassifier.set('all') diff --git a/firmware_updater/updater/source/src/main/resources/updater.ino.slto.v1.hex b/firmware_updater/updater/source/src/test/resources/updater.ino.slto.v1.hex similarity index 97% rename from firmware_updater/updater/source/src/main/resources/updater.ino.slto.v1.hex rename to firmware_updater/updater/source/src/test/resources/updater.ino.slto.v1.hex index 94e4dde4..5469cf5b 100644 --- a/firmware_updater/updater/source/src/main/resources/updater.ino.slto.v1.hex +++ b/firmware_updater/updater/source/src/test/resources/updater.ino.slto.v1.hex @@ -1,1500 +1,1500 @@ -:020000040800F2 -:1080000000000120A5C50008F5C50008F5C5000859 -:10801000F5C50008F5C50008F5C50008000000001A -:10802000000000000000000000000000F5C500088E -:10803000F5C5000800000000F5C500083F830008F2 -:10804000F5C50008F5C50008F5C50008F5C5000828 -:10805000F5C50008F5C50008CBB40008C5B4000894 -:10806000F5C50008BFB40008B9B40008F5C500089C -:10807000F5C50008F5C50008F5C50008F5C50008F8 -:10808000F5C50008F5C50008F5C50008F5C50008E8 -:10809000F5C50008F5C50008F5C50008A3B400083B -:1080A0000992000879920008F9910008F5C50008C6 -:1080B00069920008599200084992000873900008DC -:1080C0006D9000086790000861900008F5C50008F1 -:1080D000F5C500085DA4000845A4000829A400080F -:1080E0008BB4000851950008F5C50008F5C50008D7 -:1080F00019920008F5C50008F5C50008F5C5000887 -:10810000F5C500080000000000000000F5C50008EB -:108110000DA40008F1A30008399200082992000874 -:10812000F5C50008F5C50008F5C50008F5C5000847 -:10813000F5C50008F5C500080000000000000000BB -:10814000F5C50008F5C50008F5C5000800000000E9 -:10815000000000000000000000000000000000001F -:108160005B90000855900008F5C50008F5C50008AB -:10817000F5C50008F5C50008E9910008F5C5000837 -:10818000F5C50008F5C5000800000000000000006B -:04819000F5C5000829 -:1081940010B5054C237833B9044B13B10448AFF33D -:1081A40000800123237010BD0C010020000000009A -:1081B4002CD700080C4B43B10C480D4910B5AFF354 -:1081C40000800C48036823B910BD0A48036833B91A -:1081D4007047094B002BF7D0BDE810401847064BF9 -:1081E400002BF5D0184700BF000000002CD7000872 -:1081F400100100200C01002000000000074A937CBD -:1082040000EB4000023318448318805C5B7813F061 -:10821400200F0ABF024B8018C01870476401002069 -:10822400D4060020074B9A7C00EB40001044034422 -:108234001A79052AC3BF044B9B1813F8060C0020B7 -:10824400704700BF6401002044D70008704790F8CD -:10825400242062B1064A916949B1126C3AB9054ABF -:1082640092F80021520702D503685B6818477047EB -:1082740018030020D4060020002380F8243070471F -:1082840010B50446806C18B1036803211B68984735 -:10829400002384F8243010BD4B060D4B1B7803F0EB -:1082A4000C0363F04F0380F82C30836A80F830307D -:1082B40044BF02F03C0211431A12602380F83210CA -:1082C40080F82F2080F8313007212C3002F0DCBCFC -:1082D4001803002010B54FF48072044600210748AB -:1082E40004F0BEF9064A00231370012284F84C20DE -:1082F400044A23651268626510BD00BF6401002052 -:108304006C020020D8070020044B41685A6891424F -:1083140004BF012283F88D2002F0C2B84406002075 -:10832400431C02D1002070470C3191F900305A1CD3 -:10833400F8D09842F8D148687047024A136801336C -:1083440013607047D807002008B5054901F033F9D8 -:10835400C0F3C43002280CBF1020002008BD00BFA9 -:10836400DCD7000808B5064901F025F9C0F3C4308C -:10837400023802289DBF034BC018807A002008BD34 -:1083840000D8000844D7000888B0072867D8DFE879 -:1083940000F004121E2A36424E5A324B5A6942F4F5 -:1083A40000325A615B6903F400330093009B4FF081 -:1083B400904008B070472B4B2B485A6942F48022F6 -:1083C4005A615B6903F480230193019BF1E7254B18 -:1083D40026485A6942F400225A615B6903F4002377 -:1083E4000293029BE5E71F4B21485A6942F480122D -:1083F4005A615B6903F480130393039BD9E7194B18 -:108404001C485A6942F400125A615B6903F4001370 -:108414000493049BCDE7134B17485A6942F4800236 -:108424005A615B6903F480030593059BC1E70D4B17 -:1084340012485A6942F400025A615B6903F400036A -:108444000693069BB5E7074B0D485A6942F4803200 -:108454005A615B6903F480330793079BA9E7002003 -:10846400A7E700BF001002400004004800080048CD -:10847400000C004800100048001400480018004890 -:10848400001C0048C0F30312012300F00F0003FA9C -:1084940000F051F82230034341F822307047C0F312 -:1084A400031300F00F0051F8233023FA00F000F01A -:1084B40001007047034B19681A68521A8242FBD3B1 -:1084C400704700BFD807002001207047024B1622D6 -:1084D4001A604FF0FF307047C4080020FEE72DE912 -:1084E400F74F504E8A4633684F4901930220154690 -:1084F400FFF716FF20B90025284603B0BDE8F08F2A -:108504004A4A002352F823105FFA83F911B109682B -:10851400884224D00133052BF4D1454C94F87430AF -:10852400042B1AD9434B50E090424DD193F90030BB -:1085340084F88930022384F888304FF41653A367F3 -:108544003B480023C4F88430E367C4F8803001F06A -:1085540040F894F87430042BCDD894F874901FFA32 -:1085640085F8324B53F8294094F86930202B51D1C7 -:10857400BAF1000F4ED0B8F1000F4BD094F8683028 -:10858400012B47D04FF0010384F868300023E366E1 -:10859400212384F86930D6F800B0A4F85080A4F8F8 -:1085A40052805746B4F852309BB2A3B94FF47A7351 -:1085B4005A464021204602F0B9F958BB202384F8DA -:1085C400693084F8680097E70C335A68002AABD105 -:1085D4004FF0FF33ACE7B4F85230013B9BB2A4F840 -:1085E40052305A464FF47A738021204602F09EF9A5 -:1085F40080B9A3682268B3F5805F07D123692BB9DA -:1086040037F8023BC3F308031385CBE73B781385A4 -:108614000137C7E73368019A9B1AB3F57A7FA0D371 -:1086240069E700BFD8070020CCD900082801002042 -:108634000800002050DA000800207047002070472E -:10864400012070474FF400534B60002070474FF0F7 -:10865400FF307047094B5968024609B90849596007 -:108664005868694602448A4289BF064B5A600C2204 -:108674004FF0FF3088BF1A60704700BF6C020020C3 -:10868400C8080020C408002030B50B4B0412997DA3 -:10869400C2B21B33012088420ADC13F8025CAC42EC -:1086A40003D113F8015CAA4204D001300233F2E78B -:1086B4004FF0FF3030BD00BF640100202DE9F04FC2 -:1086C400344C637CA57C1F19E35C85B05B0002908D -:1086D4000E4690460193FFF7D7FFB0F1000A56DB30 -:1086E400AB1C07F1010903934FF0030B019BA9EBAA -:1086F400070293424BDB99F80020924532D199F856 -:108704000150039B1BFB05322244802E527801D07A -:10871400402E30D102F01402142A23D12846FFF748 -:108724006DFD01462846FFF77DFD00281DDDC21DB5 -:1087340042440139801A12F801E901F801EFC242FA -:10874400F9D1A27C144B22445278D2180AD015F0E5 -:10875400010F4FEA650508BF0820515D18BF80204E -:108764000143515509F10209C0E798F8072002F0C6 -:108774003F020A70E5E7002EF4D102F00C020C2A45 -:10878400F0D101220299284600F008F8EAE705B082 -:10879400BDE8F08F64010020D4060020F8B5174628 -:1087A40004460D46FFF72AFD06462046FFF73AFD2C -:1087B400194BBC221A710A12DA7119720022411C77 -:1087C400974201F00F019A7261F01F0114BF402219 -:1087D400802204465972DA7258B1013830440B339E -:1087E400211A4218002A09DD10F8012903F8012F83 -:1087F400F7E7317801F03F010A43DA722846802115 -:10880400064AFFF75BFF04F1080189B20348BDE89B -:10881400F84002F039BA00BFEC050020F005002052 -:1088240090F824302DE9F74F0446002B00F0BB806C -:10883400FFF70DFD94F84C30DFF89881002B7DD0C4 -:108844005E4B1B6C002B79D15D4E626D33689B1AB5 -:10885400226D934269D35B4B9A7CD1184F785A4965 -:108864007F1862D0D8F8085093F811E0D15C0191D8 -:1088740005EB45009E44104418440EF101091633DB -:10888400019A954280F289804FEA650C15F0010A3D -:1088940017F80C100A4618BF0A1102F0030BBBF1BB -:1088A400030F21D1BAF1000F14BF6FF0300A6FF03B -:1088B400030A01EA0A0107F80C109EF800C04946B1 -:1088C4000EEB4C0C8C450FD991F801A055450ED1F7 -:1088D40009784900013101EB030A11F803C09AF841 -:1088E400011051EA0C2104D101350330C8E70231EB -:1088F400E8E790F803C01CF0040FF5D012F004026E -:1089040044D03248BC2303710B12C371E123437278 -:10891400002301728372C3720821043002F0B4F997 -:1089240001353368C8F808506365636D3268D21A3C -:10893400B2F5FA6F24BF03F57A736365A36A3BB19A -:108944001F4BA26B1B689B1A41F2707293422DD885 -:1089540098F800303BB3194B93F828201ABB1A6CD3 -:108964000ABB9B69FBB9A36AEBB9194A144910689D -:108974000B68B0B11B1A002B15DDA06C98B103680D -:108984001B68022103B0BDE8F04F18471CF0400FEC -:10899400AAD02846FFF702FFC2E70023C8F8083030 -:1089A400C3E73233136003B0BDE8F08F00228121A6 -:1089B4002046FFF771FC0023A362C9E718030020D7 -:1089C400D807002064010020D4060020EC05002014 -:1089D400640200206C0200202DE9F743B24E96F8A1 -:1089E4000680F2783779F5794FEA082303F4407367 -:1089F4002B4357EA0227044699B22ED1AB4A92F888 -:108A04000021D0070ED5C02B11D13389A84A5BBAF7 -:108A14009BB21912D1751376A64A336201211170E3 -:108A2400A54A17600023B36103B0BDE8F083B1F534 -:108A3400807FF7D13378E77103F00C0363F04F03C1 -:108A44002371E12363720123A37240232772E3722B -:108A54000821201D02F018F9E4E796F90520002A00 -:108A640008F0C30EC0F25981326A9742DAD11EF07F -:108A7400800F7778B27843D042EA072718F0400293 -:108A840024D0BEF1C20F14D190F83C206AB1826A9E -:108A940097420AD1036C08F03C029A4205D0436B1A -:108AA4000264043303F03C034363012384F83C3041 -:108AB400B8E7BEF1C30FF8D1826A9742F5D100221C -:108AC4008121FFF7E9FB0023A362EEE7B8F1800FF1 -:108AD4000ED1826A002AA5D1784987620968816328 -:108AE4004FF0FF33426380F83C200364726299E7DD -:108AF400B8F1810F96D1816A8F4293D18262F5E7F2 -:108B0400806A42EA072282428CD16C4A1268A263CC -:108B140001F470720020B2F5007FA0725AD012D80E -:108B2400B2F5C07F22D0B3F5607F09DB40F2813219 -:108B3400934240F3C78040F2D132934200F0E08088 -:108B4400C32100251FE0B2F5207F43D0B2F5407F5A -:108B5400E9D18A06F4D1632363724323A3724023C9 -:108B6400E37200232373122363730AE06422337ACB -:108B7400627265F03F054122A272E572237360734D -:108B8400A073C221012508F03C022046FFF784FBB4 -:108B9400002D3FF447AF3378617A03F00C0363F0A0 -:108BA4004F032371A36A23721A12A37AE27113F09A -:108BB400400217BF626B84F83C2023F03C0313434C -:108BC40018BFA37201F00F011CBF012384F83C30CD -:108BD40007313EE73789B2F5207F7FBA05F00F05EC -:108BE400BFB240F0C380606C30B1036839469B6803 -:108BF4009847002840F09C80A7F58070FF283B46EA -:108C04000DD82B4B2E492A46184404F0CFFA294B91 -:108C140001221A70284B00221A60C22191E7FF2F0B -:108C2400FBDC602F06D9214825492A46384404F044 -:108C3400BDFAF2E75A195F2AF5D90A35204A1B49C9 -:108C440035449542E9D0602B107814BFC85481F89C -:108C5400000101320133F4E7FF2F72DD606C28B1AB -:108C6400036839469B689847002873D105F163036C -:108C740063724223A37245F040053B12E5722373ED -:108C840067737EE779195F2960D9084912F1FF32C9 -:108C9400E4D3602B14BF11F803E091F800E180F8ED -:108CA40000E001330130F1E718030020D40600206E -:108CB400640100206C02002064020020D807002018 -:108CC40022030020CB0708D5327A737A2AB9FF2B06 -:108CD40002BF4FF080532E4A1A60A06C18B103688B -:108CE40001211B689847BFF34F8F2A492A4BCA6852 -:108CF40002F4E0621343CB60BFF34F8F00BFFDE784 -:108D0400622363724323A372D223E3720023237387 -:108D140037E7002384F82C3084E6BEF1000F7FF49B -:108D240081AE324601F470713846FFF7C7FC79E62C -:108D3400606C1A4A03683946D3F800902B46C8473A -:108D44005AE7602F04F10E002A469BD914492A469B -:108D5400394419E0606C002203680092D3F804904F -:108D640004F10E022B463946C8477FE7B2F5007F6F -:108D7400CFD1A7F58071FF293B463FF66DAF094B74 -:108D84002A46194404F10E0004F010FA66E700BF05 -:108D940055B01F5E00ED00E00400FA052203002038 -:108DA400D406002064010020044B01685A689142F3 -:108DB40004BF012283F88C2001F072BB440600201A -:108DC4002DE9F04106460D4601EB02080C464445E8 -:108DD400A4EB050707D0336814F8011B1B68304661 -:108DE40098470028F3D13846BDE8F081B0F89A30AE -:108DF400013303F03F03A0F89A30B0F89A30B0F88A -:108E040098009BB298424FF0FF3018BF0020704783 -:108E1400037C33B1B0F82C21B0F82E319BB29A42C6 -:108E2400FAD17047B0F82421B0F8263192B29A42B0 -:108E34001FBFD0F82021D25C013303F03F031ABFD7 -:108E4400A0F8263110464FF0FF307047B0F82431B7 -:108E5400B0F826219BB293421ABFD0F82031985C17 -:108E64004FF0FF307047B0F82431B0F82601181ADB -:108E740000F03F00704738B5012303740446B0F88E -:108E84002C01C31803F03F03B4F82E2192B293428D -:108E9400FAD0D4F82821194D115494F80821A4F8D3 -:108EA4002C3155F8221091F8693091F86A100B436F -:108EB40003F02103212B1ED0114B124943F8221039 -:108EC40003EB820304F194025A610021022294F913 -:108ED4001E0101F049FA94F91E0101F079FAB4F87F -:108EE4002E11D4F8282194F8083112FA81F155F89A -:108EF400230002F0A7FA012038BD00BF280100209A -:108F040078020020F18D000838B5044620B390F8AB -:108F14007420124B53F8220090F8693090F86A20BC -:108F2400134303F02203222B16D0214611F8755F58 -:108F340001F0BAFCB4F89030B4F89220013303F095 -:108F44003F03934208D0B4F89020D4F88C1092B226 -:108F54009BB28D54A4F8903038BD00BF2801002086 -:108F6400FEE770B505460C46084649B102F00EFB13 -:108F74002B680246214628465B68BDE870401847C6 -:108F840070BD30B58BB00B4600218DF82410012A3A -:108F9400D8BF0A220DF12304B3FBF2F505FB12330B -:108FA40003F0FF03092B94BF30333733DBB2214680 -:108FB40004F801392B46002DEED1FFF7D2FF0BB098 -:108FC40030BD2DE9F84306460C46071D00F107099C -:108FD400002517F8011B0A222046FFF7D2FF236859 -:108FE40080461B682E2120469847B94540440544D5 -:108FF400EFD10A22F1792046FFF7C3FF2844BDE8E8 -:10900400F88300BF0368DA69D20782B003D59B8C6A -:109014009BB2019308E0DA699007F8D4DA69510742 -:10902400F5D4DA691207F2D4019B02B0704710B587 -:1090340048B10649002351F82340A042DAB203D0D4 -:109044000133052BF7D10522104610BD280100205D -:10905400002001F059B9002000F07CBD002001F08F -:1090640053B9002000F076BD002001F04DB9002076 -:1090740000F070BD436D10B5044663B190F8502004 -:10908400012A08D190F82810C1F1200111F0FF0144 -:1090940001D05C3098472046BDE8104000F061BD27 -:1090A40038B583689A42044650D101294FD1002330 -:1090B40080F87C3080F85030836D03B1984794F881 -:1090C400413003F02803282B40D194F87C50002D24 -:1090D4003CD00521204602F067F994F84030012B7A -:1090E40034D0012384F8403094F841302A2B03D142 -:1090F4000221204602F058F9292384F84130202324 -:1091040084F842302346002153F85C2B6164506894 -:1091140020F40040506065856362638D23854FF0C1 -:109124000073E36290692C4B6363C50344BF08205A -:10913400D06184F8401029498B424AD028498B4297 -:109144000CBFB823FA2311680B43136038BD90F8A1 -:10915400413003F028030125282B80F85050F5D125 -:10916400062102F021F994F84030AB42EED094F895 -:10917400413084F84050292B03D12946204602F07F -:1091840013F92A2384F841302025234684F84250D9 -:1091940053F85C2B00216164506820F40040506057 -:1091A40065856362638D23854FF00073E362906984 -:1091B400094B6363C0035CBF0820D06184F840108E -:1091C40006498B4205D006498B420CBFB823FC23C9 -:1091D400B9E7B823B7E700BFFB9F0008ABA10008BD -:1091E4008D9F0008024BD86C08B101F0A1BF7047F5 -:1091F400A0020020024B186C08B101F099BF70471F -:10920400A0020020024B986B08B101F091BF704797 -:10921400A0020020024BD86908B101F089BF704751 -:10922400A0020020024B986908B101F081BF704789 -:10923400A0020020024B586908B101F079BF7047C1 -:10924400A0020020024BD86808B101F071BF70473A -:10925400A0020020024B986808B101F069BF704772 -:10926400A0020020024B586808B101F061BF7047AA -:10927400A002002010B5064C206808B101F058FF88 -:10928400E06B18B1BDE8104001F052BF10BD00BF43 -:10929400A0020020836C2BB1027F012A02D100219D -:1092A40004381847704708B5431C12D0214B984224 -:1092B4003DD019D8A3F59233984230D00BD8B0F1F1 -:1092C400804F28D0A3F58063984226D01A4803F033 -:1092D400A5FC002008BD194B984222D003F58063F9 -:1092E4009842F3D1372008BD154B98421FD009D8B6 -:1092F400A3F58053984216D003F540639842E5D114 -:10930400182008BD0F4B98420FD003F50063984214 -:10931400DCD14E2008BD1C2008BD1D2008BD1E2028 -:1093240008BD362008BD2C2008BD1A2008BD192010 -:1093340008BD00BF002C014046DB000800100040BF -:1093440000440140004801400368594A93428CB0EC -:109354002CD102F56442916941F4006191619269F2 -:1093640002F400620192019A524A1060524A9342F6 -:109374003CD102F5FE32D16941F02001D161D269BC -:1093840002F020020692069A4A4A90614B4A93429E -:109394003BD14B4B9A6942F480129A619B6903F466 -:1093A40080130B930B9B434BD8642EE0B3F1804F97 -:1093B4000CD1434AD16941F00101D161D26902F073 -:1093C40001020292029A3B4A5060CFE73D4A93421F -:1093D4001DD102F50332D16941F00201D161D26994 -:1093E40002F002020392039A324A9060364A934290 -:1093F4001DD1334B9A6942F400529A619B6903F47C -:1094040000530793079B2B4BD8610CB070472F4A2E -:1094140093421CD102F50232D16941F00401D161B9 -:10942400D26902F004020492049A224AD060284AC3 -:1094340093422CD1224B9A6942F480329A619B69FF -:1094440003F480330893089B1A4B9863DDE7214AA1 -:1094540093428BD102F50032D16941F01001D16100 -:10946400D26902F010020592059A124A50611A4A12 -:1094740093428BD1124B9A6942F480229A619B6980 -:1094840003F480230A930A9B0A4B1864BDE7134A2A -:109494009342ECD10A4B9A6942F400329A619B6977 -:1094A40003F400330993099B024BD863ADE700BF73 -:1094B400002C0140A0020020001400400050014094 -:1094C4000010024000040040003401400008004045 -:1094D40000400140001000400048014000440140A9 -:1094E4001549D1F8883043F47003C1F88830134B20 -:1094F4001A6842F001021A605868114A02405A6020 -:109504001A6822F0847222F480321A601A6822F4F3 -:1095140080221A605A6822F4FE025A60DA6A22F043 -:109524000F02DA62186B074A02401A6300229A603B -:10953400054B8B60704700BF00ED00E00010024057 -:109544000CC07FF8CCFC00FF00800008104A936D2B -:10955400996811F4805F114607D0DA68D20541BFDB -:10956400DA68D2B262F4C072DA609A68900407D5FD -:10957400DA68920541BFDA68D2B262F42072DA6026 -:10958400044B4FF400325A61012381F8753070475F -:10959400A002002000040140431C04D00C3111F946 -:1095A4000C3C5A1C01D100207047984201F10C0177 -:1095B400F5D101207047431C04D091F900305A1CA6 -:1095C40001D118467047984201D1886870470C3120 -:1095D400F3E72DE9F04104468AB080B1B94990F926 -:1095E4008800FEF79DFE94F98950B749064628463F -:1095F400FEF796FE00B12EB9B44803F00FFB0AB093 -:10960400BDE8F081864218BF0020206008B9B04848 -:10961400F3E7B04B984240F0BB8003F55843DA6857 -:1096240042F48042DA60DA6822F48042DA609A69AD -:1096340042F480429A619B6903F480430093009B47 -:10964400002384F87430252384F88A30C5F303108A -:10965400FEF79AFE94F989509B4980462846FFF705 -:10966400AAFF012700FA07F300F0070203F0100332 -:109674001343069305F00F05C0F30113C0F306204E -:1096840007FA05F505A907930990032340460893B3 -:10969400059500F099FA94F98800C0F30310FEF7D9 -:1096A40073FE94F98880874905464046FFF783FF97 -:1096B400430000F0070208F00F08C0F3011003F0A4 -:1096C4001003134307FA08F7079005A928460597DE -:1096D400069300F079FA0C2094F874207E4BA06174 -:1096E4000020261DE0612062A062606294F86D0093 -:1096F40043F822602568A16FE26FD4F88070D4F833 -:1097040084306560A160E2602761636100F0FF0E50 -:1097140008B984F86CE0242084F86D00286820F0EF -:1097240001002860D5F800E06C481A4342F00C02AE -:109734000EEA000002432A606B6823F440533B4363 -:109744006B60AB6823F43063AB60624B9D4240F0C6 -:109754008C8003F55843624A1B6B03F00303D35C0C -:10976400082B3FF64CAF01A252F823F08F98000863 -:1097740079990008B3980008039600087D9A0008B8 -:10978400039600080396000803960008839A0008CD -:10979400544B984217D103F5E6331A6942F4003268 -:1097A4001A611A6922F400321A61DA6942F4003249 -:1097B400DA61DB6903F400330193019B012384F82C -:1097C4007430262340E7484B984217D103F5E4331D -:1097D4001A6942F480221A611A6922F480221A61F9 -:1097E400DA6942F48022DA61DB6903F480230293AC -:1097F400029B022384F87430272325E73B4B9842CD -:1098040017D103F5E2331A6942F400221A611A6986 -:1098140022F400221A61DA6942F40022DA61DB6977 -:1098240003F400230393039B032384F87430342349 -:109834000AE72F4B98427FF409AF03F5E0331A6926 -:1098440042F480121A611A6922F480121A61DA69E8 -:1098540042F48012DA61DB6903F480130493049BFD -:10986400042384F874303523EEE61E4B9D4241D127 -:1098740003F5E6331B6B03F44033B3F5803F00F08C -:10988400FB800CD8002B7FF4BAAE01F049FDA3682D -:1098940000EB5300B0FBF3F080B2E86074E0B3F582 -:1098A400003F00F0EC80B3F5403F7FF4A8AE4B08D6 -:1098B40003F5F40303F59053B3FBF1F39BB2EB60B0 -:1098C40062E000BFCCD9000850DA00085EDB000873 -:1098D4008EDB00080038014028010020F369FFEF07 -:1098E40061D700080044004000480040004C00409C -:1098F400005000406A4B9D4210D103F5E4331B6BCA -:1099040003F44023B3F5802F00F0B680BAD9B3F541 -:10991400002F00F0B480B3F5402FC6E7614B9D42A1 -:1099240010D103F5E2331B6B03F44013B3F5801F2E -:1099340000F0A280A6D9B3F5001F00F0A080B3F513 -:10994400401FB2E7584B9D427FF459AE03F5E03314 -:109954001A6B02F44002B2F5800F00F08D8002D839 -:10996400002A92D04BE6B2F5000F00F08880B2F5E1 -:10997400400F9AE701F0BCFCA36800EB5300B0FB76 -:10998400F3F04A4B80B2D860A26A6368002A48D0D8 -:10999400D70705D55968E06A21F4003101435960BD -:1099A400950705D55968206B21F48031014359602E -:1099B400500705D55968606B21F480210143596033 -:1099C400110705D55968A06B21F400410143596082 -:1099D400D70605D59968E06B21F48051014399605D -:1099E400950605D59968206C21F4005101439960CE -:1099F40050060ED55968606C21F480110143B0F50E -:109A0400801F596005D15968A06C21F4C00101433D -:109A14005960110605D55A68E16C22F400220A4304 -:109A24005A605A6822F490425A609A6822F02A02D4 -:109A34009A601A6842F001021A60002222671C4AE6 -:109A440015681B681A071ED463681B685B070AD570 -:109A54006FF07E432A464FF48001304600F066FFE3 -:109A640000287FF4CCAD202384F86D3084F86E3068 -:109A7400002384F86C30C2E501F06AFC07E74B0868 -:109A840003F5004318E76FF07E432A464FF40011B4 -:109A9400304600F04BFF00287FF4B1ADD4E700BF9F -:109AA40000480040004C0040005000400038014095 -:109AB400D807002038B5002580F8425090F841308E -:109AC400292B04460AD1282380F84130212303633B -:109AD400012101F069FC84F8405038BD90F8413010 -:109AE4002A2BFAD1282380F8413022230363022150 -:109AF400EFE7174B10B52022C362002303634363CF -:109B040080F8412080F842304B07044611D5436A5F -:109B14005A1C42620268526A1A70038D4BB1013BAF -:109B24000385438D013B9BB24385436C43F004039F -:109B340043642046072101F037FC23681022DA61D0 -:109B4400002384F840302046BDE81040FFF792BA65 -:109B54000000FFFF0268436B9169126803B1184764 -:109B6400704790F84130202B0BD10268282380F8ED -:109B74004130054B4363136843F0B803136000207E -:109B840070470220704700BFFB9F00080C4B5A69C6 -:109B9400024213D058610023012803F1010202D0CC -:109BA40040081346F8E71422DBB25343054A181D54 -:109BB4001044816811B11A4413691847704700BFF3 -:109BC40000040140780300202DE9F74FD1F800800C -:109BD400DFF89091614A002338FA03F402D103B00C -:109BE400BDE8F08F4FF0010E0EFA03FE18EA0E06E0 -:109BF40000F0A6804C6824F01007022F16D14FEA1B -:109C0400D30A00EB8A0A03F0070BDAF820504FEA74 -:109C14008B0B4FF00F0C0CFA0BFC25EA0C0C0D69A6 -:109C240005FA0BF545EA0C05CAF820504FEA430A39 -:109C34000325D0F800B005FA0AF5ED4304F0030C4F -:109C44000BEA050B0CFA0AFC013F4CEA0B0C012F42 -:109C5400C0F800C011D8876807EA050BCF6807FA77 -:109C64000AFC4CEA0B078760476827EA0E0EC4F328 -:109C740000179F4047EA0E074760C7683D408F685A -:109C840007FA0AF73D43C560E50059D5D9F81850DD -:109C940045F00105C9F81850D9F8185023F0030706 -:109CA40007F1804705F0010507F58037019503F0BA -:109CB400030C019DBD684FEA8C0C4FF00F0E0EFA99 -:109CC4000CFEB0F1904F25EA0E0E3BD0244DA84275 -:109CD4003AD005F58065A84238D005F58065A842DC -:109CE40036D005F58065A84234D005F58065A842D4 -:109CF40032D005F58065A84214BF0725062505FA6C -:109D04000CF545EA0E05BD601568F74314F4803F71 -:109D14000CBF3D4035431560556814F4003F0CBF3B -:109D24003D4035435560956814F4801F0CBF3D4099 -:109D340035439560D568A40254BF3D403543D56092 -:109D4400013349E70025DAE70125D8E70225D6E7FC -:109D54000325D4E70425D2E70525D0E70004014014 -:109D6400000400480010024010B590F82120022A97 -:109D740003D004228263012010BD0268C46B116801 -:109D8400436B21F00E011160116821F00101116093 -:109D9400026C012101FA02F26260002480F82110B1 -:109DA40080F8204013B19847204610BD184610BDD6 -:109DB4000023354A10B580F84230C2624385436CB3 -:109DC4001943416490F84130282B044607D090F899 -:109DD4004130292B03D090F841302A2B22D1032182 -:109DE400204601F0E1FA282384F84130002323635C -:109DF400264B6363236819681A6811F480411ED0E6 -:109E040022F48042A06B1A60214B4363002384F840 -:109E14004030FFF7A9FF002835D0A06B436BBDE8A5 -:109E240010401847072101F0BFFA94F84130602B25 -:109E34001CBF202384F8413000232363D9E712F4A4 -:109E440000420DD01A68E06B22F400421A60104BF5 -:109E5400436384F84010FFF787FFA0B1E06BDDE7B0 -:109E640094F84130602B05D1202384F8413084F8E4 -:109E7400402010BD94F8513084F840201BB920468E -:109E8400BDE810406DE610BD0000FFFFFB9F000819 -:109E9400A9AD000838B5036820220446DA61082217 -:109EA400DA610D46072101F07FFA23685A6842F40B -:109EB40000425A605A6822F0FF7222F48B3222F474 -:109EC400FF7222F001025A60204601F09BFA2268D8 -:109ED4001368580402D41368190409D594F841305E -:109EE400212B0CBFA36BE36B1B685B689BB2638580 -:109EF400638D9BB21BB1636C43F0040363646B0713 -:109F040010D5636A526A591C61621A70238D4BB171 -:109F1400013B2385638D013B9BB26385636C43F0F6 -:109F2400040363640023236384F842306363636C33 -:109F340063B1616C2046FFF73BFF94F84130282B56 -:109F44001FD129462046BDE83840D2E5E26A12F521 -:109F5400803F0BD00B4AE262202284F8412084F82F -:109F640040302046BDE83840FFF784B894F84120DB -:109F740084F84030222A4FF0200284F8412000D196 -:109F840038BD38BD0000FFFF10B590F84030012BFC -:109F940004462ED0012380F84030C80619D5D306D4 -:109FA40017D594F841302268212B0CBFA36BE36BC7 -:109FB4001B685B68B3FA83F310215B09D1611BB1A1 -:109FC400002084F8400010BD636C43F00403636414 -:109FD400F6E7080705D5130703D523680822DA61D5 -:109FE400EEE78806ECD59306EAD52046FFF752FF44 -:109FF400E6E7022010BD2DE9F84390F84030012B2C -:10A0040004460E46174600F0CB800123CD0680F8A7 -:10A0140040303ED5D0063CD5638D9BB28BBBE36A02 -:10A02400B3F1007F03D0E36AB3F1007F14D194F855 -:10A034004130282B10D131462046FFF75AFDB20695 -:10A0440005D5BB0603D531462046FFF723FF002084 -:10A0540084F84000BDE8F883E36A13F5803F236881 -:10A064000CD094F84120292A08D11022DA61204624 -:10A0740001F0C8F92046FFF71DFDE0E71022DA6180 -:10A08400DDE723681022DA61636C43F00403636440 -:10A09400D5E771071AD57A0718D5638D9BB263B1DA -:10A0A400636A5A1C62622268526A1A70238D013BE9 -:10A0B4002385638D013B9BB26385638D9BB2002B2B -:10A0C400BDD1E36A13F5803FD4D1B8E7330746D551 -:10A0D4003D0744D594F8413003F02803282B236826 -:10A0E40037D19A69E1689D6902294FEA1545C2F39F -:10A0F400004805F0FE059A68D3F80C9020D1C2F30D -:10A10400090285EAD21515F006050FD1A16C0131BB -:10A11400A164A16C022992D10821A564D96184F8B3 -:10A12400405041462046FEF7BBFF88E70421204605 -:10A1340001F03AF9002384F8403009F0FE02F0E718 -:10A144000421204601F030F9002384F840302A46E7 -:10A15400E7E70822DA61002384F840306FE7B007AC -:10A164007FF56DAFB9077FF56AAF638D9BB26BB1B5 -:10A17400636A2268591C61621B789362638D013B98 -:10A184009BB26385238D013B238558E7E36AB3F1D2 -:10A19400807F3FF46FAFE36A002B3FF46BAF4EE771 -:10A1A4000220BDE8F88370B590F84030012B0446D6 -:10A1B40000F0A480012380F8403011F010001DD07D -:10A1C400D3061BD522684E491023D361636C43F038 -:10A1D40004036364636B8B420ED04A498B420CBF09 -:10A1E4006023F42311680B431360204601F00AF93D -:10A1F400002084F8400070BD6023F3E70E063ED5CE -:10A2040055063CD523681A6822F040021A60628D14 -:10A2140092B2002A2ED05A68618D89B2FF2999BF63 -:10A22400618D2185FF2121855968208D21F07F7161 -:10A2340021F45831C2F30902C6B221F47F7142EA13 -:10A24400064221F003018CBF4FF080754FF000757A -:10A254000A432A435A60628D121A92B2628594F8B4 -:10A264004120222A1A680CBF42F4004242F4804280 -:10A274001A60BDE740212046FFF79AFDB8E789063A -:10A28400B6D59306B4D523682022DA615A6822F041 -:10A29400FF7222F48B3222F4FF7222F001025A6020 -:10A2A400002222636263A2F58032E26228B11022A6 -:10A2B400DA61636C43F004036364204601F0A2F89E -:10A2C4000321204601F070F8636C1BB994F8412017 -:10A2D400602A01D1616CCEE794F84120212A07D18C -:10A2E400202284F8412094F8422084F842307FE709 -:10A2F40094F84120222A7FF47BAFF1E7022070BD5D -:10A30400ABA100088D9F0008036899691A6810B40E -:10A31400CC0508D5140606D5446C44F00104446405 -:10A324004FF48074DC614C0508D5140606D5446CE2 -:10A3340044F0080444644FF48064DC61890508D562 -:10A34400120606D5426C42F0020242644FF40072D7 -:10A35400DA61436C13F00B0F03D0416C5DF8044BCE -:10A3640026E55DF8044B7047174B30B5DC68C4F341 -:10A374000224C4F10703251D042B28BF0423062D42 -:10A384004FF0010505FA03F303F1FF338CBF033CDF -:10A3940000241940A14005FA04F4013C224000289D -:10A3A40042EA01024FEA0212ADBF00F1604000F040 -:10A3B4000F00064B00F56140B5BFD2B2D2B21A54B9 -:10A3C40080F8002330BD00BF00ED00E014ED00E094 -:10A3D400002808DB4209012300F01F0003FA00F003 -:10A3E400014B43F82200704700E100E0044B4FF4B6 -:10A3F4000012C3F88421034B186900F0ECBC00BFC1 -:10A4040000E100E028010020044B4FF48012C3F85F -:10A414008421034BD86800F0DEBC00BF00E100E0FB -:10A4240028010020044B8022C3F88421034B986840 -:10A4340008B100F0D0BC704700E100E02801002022 -:10A44400034B4022C3F88421024B586800F0C3BC7C -:10A4540000E100E028010020034B2022C3F88421FE -:10A46400024B186800F0B7BC00E100E028010020AE -:10A474000A4AD3695B684FF6FE71D962D96841F024 -:10A484000201D960D96821F00101D9604FF6FF714A -:10A494009963002382F82830536270471803002020 -:10A4A4002DE9F041A44CE36903204FF6FF75022621 -:10A4B400002794F82820092A00F2C881DFE812F066 -:10A4C4000A00A1001900EE003E014E016201920152 -:10A4D400BA01A401E36993F88C30002B00F0898061 -:10A4E4000023636384F85C30FF2263620123A26566 -:10A4F4006365E26992F88C30002B66D1626D1AB103 -:10A50400A36DB3FA83F35B090022626294F85C20C2 -:10A51400002A36D1656B072D22DD002B51D0E2785D -:10A52400207940EA022094F90520002A10DA18B1B3 -:10A53400FEF7AAF8C343DB0F94F8BC2452070CD4EB -:10A54400A56194F8BC349B071BD5CC23636218E047 -:10A55400236AC11A4B424B41EEE793B1A561F4E77C -:10A56400012D2ED194F83830A363CC2B03D0E26AAA -:10A57400236B9A4205DD226C1AB1E36A002B00F3C7 -:10A584006881E369606A5B6841F2E83140F2314214 -:10A59400002808BF0A46DA626FF001021A61DA681D -:10A5A40042F00102DA606FF002021A61DA6842F0E6 -:10A5B4000202DA60002384F85C30032384F8283034 -:10A5C40017E00023A3610C23C0E7536840F2424222 -:10A5D400DA626FF0010100225A621961D96841F010 -:10A5E4000101D960012384F82830A263E264A364E2 -:10A5F4002365E369002283F88C2083F88D20BDE86D -:10A60400F081E56995F88DE0BEF1000F38D16B68F3 -:10A614005F6BE36C03F14402BA4218DA6833A16C4D -:10A62400A06B226DE364002603F1440847459C4671 -:10A6340030DDB1F5807F03F1680324DD26B1C4F871 -:10A644004CC0A164A0632265A36C5B00A364BEF14B -:10A65400000FCED0636D226D1340626B6365172AC1 -:10A6640006DC531CA16B6363A36DA1544B40A3652B -:10A67400022384F828306B684FF4D072DA62B8E7AA -:10A6840040F24247C5E7B2FA82F208435209490050 -:10A694000126C9E7002ED7D0E364D2E793F88C20D3 -:10A6A40012B184F8287004E7636AC3B1E2636822D4 -:10A6B400E3695B689A632332DA626FF001021A611C -:10A6C400DA6842F00102DA606FF002021A61DA68B5 -:10A6D40042F00202DA600023636304236EE7E16A56 -:10A6E400226B914206DD226C1370626C22646364F7 -:10A6F400E362E363236C002B00F0A8801A78C2F3B2 -:10A704008101682201FB0222597901F00F0108310D -:10A71400E163E16A0129CBD1197801F0DF011970F5 -:10A72400E36B206C013BC15C81F02001C154E36AFE -:10A734000133E362BCE700BF1803002093F88C20C8 -:10A7440062B15A68D2F824E0916B0A398E4503D27B -:10A75400956384F82860ACE605232FE7636AF3B1B8 -:10A76400636AA363A36B002101221A424FEA4202E7 -:10A774001CBF83F480730121FF2AF6DD01B1A363BA -:10A784000123A364A06BA26C682310424FEA420128 -:10A794000BD0B2F5807F08DC0A466833F5E7636BBB -:10A7A400226C591C6163D35CDBE7682B0CBF062267 -:10A7B4000722B1F5007FA16484F8282054DD616B81 -:10A7C400E26B914203F59C7302DA626A002A48D074 -:10A7D400092247E0D16821F00201D1606FF0020143 -:10A7E40011614EE0E3695B68596B9A6B453A91429B -:10A7F40008D24FF6FF729A63012384F8283084F854 -:10A804005C30F6E60623D9E6E3695B6841F2504220 -:10A81400DA626FF002021A61DA6842F00202DA6068 -:10A82400636A1BB1002363620823C7E6E36A01334A -:10A83400E362F9E793F88C202AB15A68526BB2F5B7 -:10A84400936F7FF6D6AE84F8280032E6FFF710FE49 -:10A85400CFE600231370626C22646364E362E363F3 -:10A864008FE6052284F8282094F82820072AE26934 -:10A874005268AFD16FF002011161D16841F0020159 -:10A88400D16094F82820092A0BBFE269E169526873 -:10A89400496813BFA3F123024FF6FF7191638A63E2 -:10A8A400E2695268D362A4E690F86A30202B3BD167 -:10A8B400002937D090F86830012B35D0012380F877 -:10A8C4006830A0F85830A0F85A3083684165B3F571 -:10A8D400805F19D10369ABB940F2FF13A0F85C3073 -:10A8E40000232222C36680F86A20026880F8683058 -:10A8F400916841F001019160116841F49071116017 -:10A9040018467047FF23E9E723B90369002BF9D000 -:10A914007F23E3E7B3F1805FE2D10369002BF7D033 -:10A924003F23DBE7012070470220704770B50468BD -:10A93400104EE56931EA050501D1002070BD5D1CAA -:10A94400F7D08BB9236823F4D0732360A36823F072 -:10A954000103A360202380F8693080F86A30002363 -:10A9640080F86830032070BDD6F8C054AD1AAB42ED -:10A97400DFD2E7E71803002010B507490446042096 -:10A98400FDF78DFD30B14FF0904310220CB19A6168 -:10A9940010BD9A6210BD00BFDC0700202DE9F04114 -:10A9A4003B2890B056D8A84C204490F93850681CE5 -:10A9B40050D0A6492846FDF772FD4FEA151805F058 -:10A9C4000F0798B304F174012846FEF7E5FD00284B -:10A9D40043D004F174012846FDF7A2FC0446C8B133 -:10A9E4002846FDF7B1FC30F0100213D12368012191 -:10A9F40001FA00F023EA0000206002238DF804101D -:10AA040093498DF80430CB6923F00053CB61049251 -:10AA14008DF804208D4908F00F00012303FA07F292 -:10AA240051F8203023EA020341F8203008F00F00E7 -:10AA3400FDF7AAFC012303FA07F7032201936946F1 -:10AA44000023009703920293FFF7BEF8814928463A -:10AA5400FDF718FD10B0BDE8F08104F19801284617 -:10AA6400FEF79AFD0028D5D004F198012846FDF799 -:10AA740057FC04460028CDD02846FDF773FC142863 -:10AA8400064600F2DC80744BC340D907C2D5734933 -:10AA94002846FEF790FDC202236A4CD5042202FA2E -:10AAA40006F623EA06062662236A2362226A41F234 -:10AAB40011131A4208D1226A40F244431A4202BFD7 -:10AAC400636C23F400436364226A41F211131A4253 -:10AAD40008D1226A40F244431A4203D1236823F086 -:10AAE4000103236002238DF83D30226A41F21113E1 -:10AAF4001A4208D1226A40F244431A4202BF236830 -:10AB040023F001032360564B9C4253D1504A93696E -:10AB140023F400639361534B9C425CD14C4AD36948 -:10AB240023F02003D361504B9C425CD1484A936983 -:10AB340023F4801356E0012202FA06F623EA0606FD -:10AB44002662236A2362464B9C4213D003F50063BA -:10AB54009C420FD003F540639C420BD003F5806305 -:10AB64009C4207D003F580639C4203D003F5006345 -:10AB74009C420DD1226A41F211131A4208D1226A71 -:10AB840040F244431A4202BF636C23F400436364FB -:10AB9400226A41F211131A4208D1226A40F2444354 -:10ABA4001A4202BF236823F00103236001238DF8B6 -:10ABB4003D3097E7B4F1804F05D1254AD36923F09E -:10ABC4000103D361A7E7294B9C4210D1204AD369E2 -:10ABD40023F00203D361264B9C4210D11C4A936993 -:10ABE40023F40053936100238DF83D3012E7214B89 -:10ABF4009C420CD1164AD36923F00403D3611E4B43 -:10AC04009C4214D1124A936923F48033EAE71B4B24 -:10AC14009C4280D10E4AD36923F01003D361184BB0 -:10AC24009C4280D10A4A936923F48023DAE7154BC6 -:10AC34009C42F4D1064A936923F40033D2E73C28BA -:10AC44007FF4E8AE23E700BF68D70008FC070020C4 -:10AC540000100240DC0700201111110000D8000888 -:10AC6400002C01400014004000500140000400404A -:10AC74000034014000080040004001400010004042 -:10AC84000048014000440140184B1A6A12124270F5 -:10AC94001A6A827030B50022FF24551C92B28A428F -:10ACA40003DA825C54402A46F7E744545A6C002A7B -:10ACB400FDD11A6CC2B9186472B693F8282002F058 -:10ACC400FF017AB90322D96283F82820DB695B6823 -:10ACD4000122DA626FF001021A61DA6842F00102BD -:10ACE400DA60596262B630BD5864E5E718030020A3 -:10ACF40008B5044B2BB10448FEF7B5F808B1AFF31F -:10AD0400008008BD00000000B804002010B590F8D1 -:10AD14006A30222B036833D1998C8368B0F85C20A5 -:10AD2400B3F5805F02EA0102436D0DD1016959B99F -:10AD340023F8022B4365B0F85A40013CA4B2A0F8B2 -:10AD44005A402CB1002010BD591C41651A70F2E71D -:10AD540003681A6822F490721A609A6822F0010259 -:10AD64009A60202380F86A30FEF761F90428E9D854 -:10AD7400064A074B53F8203052F820009847E1E781 -:10AD84009A6942F008029A61022010BD5001002025 -:10AD94003C010020406A0023A0F85A30A0F8523049 -:10ADA400FEF730B9406A0268536843F400435360C5 -:10ADB400826B00235363C26B536390F84130602B62 -:10ADC40003D1202380F84130704790F851300BB9FB -:10ADD400FEF7C7BE70470368DA69196870B512F0E8 -:10ADE4000F05044606D1960604D58E0602D5BDE8A5 -:10ADF40070408BE79868002D68D010F0010502D1EF -:10AE040011F4907F62D0D60707D5C80505D5012077 -:10AE14001862E06E40F00100E066960706D52DB199 -:10AE240002201862E06E40F00400E066500706D588 -:10AE34002DB104201862E06E40F00200E0661607AF -:10AE440007D5880600D425B108201862E36E0343B1 -:10AE5400E366E36E002B00F0A880920604D58B060F -:10AE640002D52046FFF752FFE36E13F0080F236864 -:10AE740003D19D6815F0400523D01A6822F490721E -:10AE84001A609A6822F001029A60202284F86A20EB -:10AE94009A68560610D59A68606E22F040029A604D -:10AEA40050B1424B4363FEF75FFF00287DD0606ED4 -:10AEB400BDE87040436B18472046BDE87040FEF77C -:10AEC400A1B82046FEF79EF8E56670BDD50215D5FB -:10AED400400213D54FF480121A62202384F869309B -:10AEE400204684F86A30FEF7A2F8314B53F820105C -:10AEF40020467531BDE87040FFF7D6BC16062BD549 -:10AF04000D0629D594F86920212A4ED1B4F852208F -:10AF140092B242B91A6822F080021A601A6842F0AA -:10AF240040021A6070BDA268B2F5805FE26C0ED177 -:10AF3400216961B932F8021BC1F308011985E26481 -:10AF4400B4F85230013B9BB2A4F8523070BD511C8E -:10AF5400E16412781A85F3E7500626D54A0624D50B -:10AF64001A6822F040021A60202384F869302046CF -:10AF7400FEF75DF8042818D80E4B53F820400E4B0A -:10AF840053F820302046984701300ED0D4F894303E -:10AF9400B4F89A1094F8742013FA81F1074BBDE8C1 -:10AFA400704053F8220000F04DBA70BD99AD00080E -:10AFB400500100208C02002078020020280100208B -:10AFC400036A23F010030362036A70B5446882695C -:10AFD4000D6822F0807222F4E64242EA05228D686E -:10AFE40023F0200343EA05131A4DA84207D005F5C0 -:10AFF4000065A84203D005F5E055A8420DD1CD68FF -:10B0040023F0800343EA0513124DA84223F04003C2 -:10B0140012D005F50065A8420ED00F4DA8420BD002 -:10B0240005F58065A84207D005F58065A84203D0E0 -:10B0340005F50065A84206D124F440658E694C6983 -:10B04400344345EA8404446082614A68826303624B -:10B0540070BD00BF002C014000400140324A03682B -:10B06400904212D0B0F1804F0FD0A2F594329042AA -:10B074000BD002F58062904207D002F596329042DE -:10B0840003D002F5E052904215D14A6823F07003D0 -:10B094001343254A90421DD0B0F1804F1AD0A2F537 -:10B0A4009432904216D002F58062904212D002F59A -:10B0B400963290420ED01D4A90420BD002F5806227 -:10B0C400904207D002F58062904203D002F50062FC -:10B0D400904203D1CA6823F4407313434A6923F0AE -:10B0E4008003134303608B68C3620B6883620E4B57 -:10B0F400984213D003F5006398420FD003F54063E0 -:10B1040098420BD003F58063984207D003F580631F -:10B11400984203D003F50063984201D10B6903639D -:10B1240001234361704700BF002C014000400140EF -:10B1340003681A69910710B504460ED5DA689207B8 -:10B144000BD56FF002021A61012202779B699B07FB -:10B1540075D0FDF729FE0023237723681A69500769 -:10B1640010D5DA6851070DD56FF004021A61022276 -:10B1740022779B6913F4407F204663D0FDF714FEC9 -:10B184000023237723681A6912070FD5DA6810079A -:10B194000CD56FF008021A6104222277DB69990743 -:10B1A400204652D0FDF700FE0023237723681A6956 -:10B1B400D20610D5DA68D0060DD56FF010021A61E8 -:10B1C40008222277DB6913F4407F204640D0FDF744 -:10B1D400EBFD0023237723681A69D10709D5DA68C0 -:10B1E400D20706D56FF001021A61636C201F03B108 -:10B1F400984723681A69140605D5DA68100644BF0F -:10B204006FF080021A611A69D10505D5DA68120651 -:10B2140044BF6FF480721A611A69540605D5DA685E -:10B22400500644BF6FF040021A611A69910605D5B1 -:10B23400DA68920644BF6FF020021A6110BDFEF76F -:10B2440029F888E7FEF726F89AE7FEF723F8ABE734 -:10B25400FEF720F8BDE70368012202FA01F11A6A39 -:10B2640022EA01021A621A6A1143114A19629342CC -:10B2740013D002F5006293420FD002F5406293426C -:10B284000BD002F58062934207D002F580629342AC -:10B2940003D002F50062934203D15A6C42F4004297 -:10B2A4005A641A6842F001021A600020704700BF15 -:10B2B400002C01400F2303600B4B5A6802F0030279 -:10B2C40042605A6802F0F00282605A6802F4E06256 -:10B2D400C2605B68DB0803F4E0630361034B1B6833 -:10B2E40003F007030B607047001002400020024087 -:10B2F400084B4FF460525B6892FAA2F2B2FA82F2FF -:10B3040003F46053D340044AD35C044A1068D84021 -:10B31400704700BF00100240D4DA0008A4000020E7 -:10B32400084B4FF4E0625B6892FAA2F2B2FA82F23E -:10B3340003F4E063D340044AD35C044A1068D84061 -:10B34400704700BF00100240D4DA0008A4000020B7 -:10B35400124B5A6802F00C01082910B51BD14FF4A6 -:10B36400701191FAA1F10F20B1FA81F1DB6A90FA20 -:10B37400A0F00B4CB0FA80F003F00F0302F470124B -:10B38400C340CA40234422441B7A0648B0FBF3F36B -:10B39400107E584310BD034810BD00BF001002408A -:10B3A400D4DA000800127A0011F0010307D090F8F3 -:10B3B400413003F02803282B0CBF4223F2238A07D1 -:10B3C40009D590F8412002F02802282A0CBF43F046 -:10B3D400440343F0F4034A0701F0110248BF43F069 -:10B3E400B80301F01201112A08BF43F09003122997 -:10B3F40001680A6808BF43F0600322EA03030B6094 -:10B404000020704703689A69910744BF00229A623A -:10B414009A69D2075EBF9A6942F001029A61704745 -:10B42400074938B5A1F5A075A1F11404E3681039F2 -:10B4340013B1032208469847AC422146F4D138BDE3 -:10B44400B804002090F86930202B1AD1B9B190F8D3 -:10B454006830012B15D00123A0F85230C164A0F844 -:10B464005030016800232122C36680F8683080F8D8 -:10B4740069200A6842F080020A6018467047012079 -:10B4840070470220704738B506244FF48065A8B28F -:10B49400FEF77CFB013C4FEA4505F8D138BD38B5D1 -:10B4A40005242025A8B2FEF771FB013C4FEA4505AF -:10B4B400F8D138BD1020FEF769BB0820FEF766BB43 -:10B4C4000220FEF763BB0120FEF760BB01F066BEFD -:10B4D40001F064BE01F062BE01F058BE01F056BE38 -:10B4E40000000000000000000000000001F0FF0167 -:10B4F400102A2BDB10F0070F08D010F8013B013A9B -:10B504008B422DD010F0070F42B3F6D1F0B441EACC -:10B51400012141EA014122F007047FF000070023E2 -:10B52400F0E80256083C85EA010586EA010685FA38 -:10B5340047F5A3FA87F586FA47F6A5FA87F68EB992 -:10B54400EED1F0BC01F0FF0102F0070232B110F8B5 -:10B55400013B013A83EA010313B1F8D1002070479B -:10B5640001387047002D06BF35460338073815F0FB -:10B57400010F07D1013015F4807F02BF013015F4AB -:10B58400C03F0130F0BC0138704700BF034613F8D8 -:10B59400012B002AFBD1181A013870472DE9804885 -:10B5A400C34CC44EA7B00D20FFF7F8F904F194077B -:10B5B4004FF4E1330025C4F80C313846062384F8EF -:10B5C4003031C4F81851C4F81451C4F81051FEF7BE -:10B5D40000F894F8083156F8231091F8692091F88E -:10B5E4006A100A4302F02202222A16D006EB8303D1 -:10B5F400B14A5A612946012294F91E019F62FEF75D -:10B60400B3FE94F91E01FEF7E3FE94F8083104F248 -:10B61400091156F82300FFF747F9A8490420FDF75C -:10B62400BBFF0546002835D0A5490420FCF737FFA9 -:10B6340020BBA3490420FCF725FF01271422002185 -:10B644000FA801F00DF89D490420FCF769FE04469B -:10B65400002800F085830420FCF776FE05460028C8 -:10B6640000F00C83102840F07B83002F40F0088307 -:10B67400002303930394039B143351E30027DDE772 -:10B6840003F47013B3F5701F40F06783636843F0ED -:10B69400020361E38B490420FDF77EFF0446002882 -:10B6A40000F07D8286490420FCF7F9FE20B9844924 -:10B6B4000420FCF7E7FE01254022002116A800F033 -:10B6C400CFFF1C2200210FA800F0CAFF7D490420EF -:10B6D400FCF726FE04461690002800F0418314224D -:10B6E40000210AA800F0BCFF26A9002341F8743DFC -:10B6F4000AA8FFF7DFFD671C00F0C680724B9C426E -:10B7040000F0B58200F2AE80A3F592339C4209D0DA -:10B7140000F2A180B4F1804F04D0A3F580639C4271 -:10B7240040F0AF800D9CFFF7FBFDB4F5A06F04D093 -:10B7340000F2B680B4F5806F00D14000634BB0FBDB -:10B74400F3F3013B1793FE23199300231A931893E1 -:10B754001B938DF895308DB35D4C9DF89530042086 -:10B76400207003F0FF08FBB956498DF89480FDF76B -:10B7740022FF074616A8FDF7E7FD94F90000C0F381 -:10B784000310FCF701FE02230B9303230D9323788C -:10B79400CDF8308003F00F03C7F306279D400AA9B4 -:10B7A4000E970A95FEF710FA022317A916988DF83A -:10B7B4009530FFF753FC01238DF895300420FCF7F6 -:10B7C400D1FD1428044600F2DD84424BC340DD075A -:10B7D40040F1C68260230F930023119313931293B5 -:10B7E400159314936C2310939DF89430012B00F05F -:10B7F400B78201238DF89430082C4FF002038DF8A2 -:10B80400953000F0158171D8002C00F0AD80042C27 -:10B8140000F0FC8001238DF895302A490023042090 -:10B824008DF89430FDF7C7FEC00240F1B381169B3A -:10B83400042202FA04F41A6A22EA04021A621A6A54 -:10B8440014431C625A6C42F400425A641A6842F06F -:10B8540001021A6084E2204B9C423FF463AF03F57B -:10B8640080635CE71D4B9C4200F0018211D8A3F574 -:10B8740080539C4200F0FB8103F540639C4200F03E -:10B88400F681174801F0CAF9164801F0C7F90020FB -:10B8940054E7154B9C4200F0EA8103F50063EDE7A1 -:10B8A400B4F5C06F3FF449AFB4F5E06F44E700BFAF -:10B8B400B8040020280100200D8F0008DCD7000800 -:10B8C400FC07002000D80008002C014018E4030005 -:10B8D4000400002011111100001000400044014038 -:10B8E400FCDA00082CDB000800480140102C00F0B2 -:10B8F400E280142C00F018810C2C8BD1169BB54DD2 -:10B904001A6A22F480521A62196A5868DA6922F0B3 -:10B91400807222F4E642AB4242F4C04221F4005168 -:10B9240013D005F50065AB420FD005F54065AB4279 -:10B934000BD005F58065AB4207D005F58065AB42B9 -:10B9440003D005F50065AB4201D120F48040586076 -:10B95400DA616C221A641962DA6942F40062DA610B -:10B96400DA6922F48062A2E0169B9A4F1A6A22F0E6 -:10B9740001021A621D6A59689A6922F4803222F01F -:10B984007302BB4242F0600225F0020013D007F5B7 -:10B994000067BB420FD007F54067BB420BD007F5E9 -:10B9A4008067BB4207D007F58067BB4203D007F529 -:10B9B4000067BB421AD125F00E00864DAB4213D06E -:10B9C40005F50065AB420FD005F54065AB420BD0E1 -:10B9D40005F58065AB4207D005F58065AB4203D021 -:10B9E40005F50065AB4201D121F4407159609A61BB -:10B9F4006C225A6318629A6942F008029A619A6941 -:10BA040022F004029A619A699A6103E70FA91698D1 -:10BA1400FFF7D6FA169B13999A6942F400629A6169 -:10BA24009A6922F480629A619A6942EA0122EBE7F8 -:10BA3400169B68491A6A22F480721A621F6A58684F -:10BA4400DA6922F4803222F073028B4242F06002FF -:10BA540027F4007507D0DFF880E1734503D00EF5B5 -:10BA6400E05E734506D18B4227F4606511D05A49D4 -:10BA74008B420ED059498B420BD001F580618B4229 -:10BA840007D001F580618B4203D001F500618B4240 -:10BA940001D120F440505860DA616C22DA631D62EF -:10BAA400DA6942F00802DA61DA6922F00402DA6142 -:10BAB400DA69DA61AEE6169B464D1A6A22F48032E0 -:10BAC4001A62196A58685A6D22F4803222F07002A0 -:10BAD400AB4242F0600221F4003113D005F5006559 -:10BAE400AB420FD005F54065AB420BD005F5806540 -:10BAF400AB4207D005F58065AB4203D005F5006580 -:10BB0400AB4201D120F4803058605A656C229A65AA -:10BB140019625A6D42F008025A655A6D22F0040205 -:10BB24005A655A6D5A6575E6169B2A4D1A6A22F4AF -:10BB340080121A62196A58685A6D22F0807222F4CF -:10BB4400E042AB4242F4C04221F4001113D005F5A7 -:10BB54000065AB420FD005F54065AB420BD005F54F -:10BB64008065AB4207D005F58065AB4203D005F58F -:10BB74000065AB4201D120F4802058605A656C22E4 -:10BB8400DA6519625A6D42F400625A655A6D22F4FC -:10BB94008062C5E7214616A8FFF75DFBE0E0302090 -:10BBA400FEF7FCFE2046FEF7E7FED9E0FEE794F838 -:10BBB4008910012940F0A781196841F00101196039 -:10BBC400022384F889309EE1FFF7AAFB4000C2E11A -:10BBD400FEE700BF002C014000340140004001405A -:10BBE4000A223946ACE249462046FDF7BAF9002359 -:10BBF400AB618021BC22112300274FF0010B4FF0D1 -:10BC0400E10A8DF85F1016A808218DF858200192DA -:10BC14008DF8593000938DF85A808DF85B708DF84B -:10BC24005CB08DF85DA08DF85E70FFF72DF8642090 -:10BC3400FCF740FC009B019A8DF8593008218123C0 -:10BC440016A88DF858208DF85F308DF85A808DF83D -:10BC54005B708DF85CB08DF85DA08DF85E70FFF7B9 -:10BC640013F86420FCF726FCFFF742F85AE20E9C16 -:10BC7400FFF73EFB59E5039503944FE0002FFAD0FC -:10BC84000223BD4F8DF84030FB69DFF81C9323F08D -:10BC94000053FB61002313938DF84030042389F88B -:10BCA40000309DF8403003F0FF08CBB94046FCF764 -:10BCB4006BFBFB6943F00053FB61FB69CDF86080CB -:10BCC40003F000530293029B99F8003003F00F0233 -:10BCD40001239340169316A903231793FDF774FFCA -:10BCE4000223002101208DF8403013918DF840008B -:10BCF4008DF84030236840F6FE72AA4023EA02031E -:10BD040023602368C022AA4023EA020323608DF83B -:10BD1400400003910394002D7FF4ADAC039B0833E2 -:10BD24000393039B4FF4D8621A609DF84130012BB2 -:10BD340016D002238DF8403022680123AB40134310 -:10BD440023602368002D7FF49BAC03F03C033C2B61 -:10BD540003D1636843F00103636001238DF840302D -:10BD640086498748874CDFF84492DFF84482824F43 -:10BD7400FDF7F7F884498248FDF7F3F84FF47A7039 -:10BD8400FCF798FB814B824A9A642046FCF7A2FA9E -:10BD94004FF0FF32226486F8492096F8531096F843 -:10BDA40054207C482E23002589F8003142EA0122E0 -:10BDB400012389F862504FF0020A89F80131C8F86A -:10BDC40020200422C8F81850257188F8285084F8D7 -:10BDD4002C506563A562C8F8245086F8485086F84C -:10BDE4003F5086F8415086F8423086F84330C6F812 -:10BDF4003C5186F840A0C8F830207A6984F83C5059 -:10BE040042F4003284F82430C8F84050D8F81C407A -:10BE1400C8F8445088F85C507A617A6902F40032B8 -:10BE24000792079A7A6942F480227A617A690A93BE -:10BE340002F48022032306920AA9069A0B930C9516 -:10BE4400FDF7C2FE08230AA952480C950D950E93DE -:10BE5400CDF828A0CDF82CA0FDF7B6FE40226266EE -:10BE64007F22E26694F889204B4BA56612F0100FEE -:10BE74002567A567C4F884503A4623664F4645466D -:10BE84007FF494AE916941F001019161916901F0EF -:10BE940001010991099994F8891001F0FF0009B989 -:10BEA40084F8880000210891196808910899606F46 -:10BEB40021F0700141F040010891089921F002013C -:10BEC4000891089921F440210891089921F47051AE -:10BED4000891089921F400410891089901430891B7 -:10BEE4000899E06F014308910899D4F88000014350 -:10BEF400089108991960012184F8891094F889102F -:10BF0400C9067FF554AEA046244B48F8043BD369D8 -:10BF140043F00203D361D36903F002030593059B45 -:10BF2400C02300220A930AA902234FF090400B93E6 -:10BF34000C920D920E93FDF747FE194B4FF00052F1 -:10BF44001A6004A90FA8FFF7B5F9129B002B7FF420 -:10BF54003BAEFFF7E5F923686360124BB0FBF3F0E7 -:10BF64000023E3606361A36194F8413001384FF624 -:10BF7400FF72A0601EE000BF0010024018DB000842 -:10BF8400B8040020EC0500201EDB00084406002055 -:10BF94000D8300080004004828000140000400400C -:10BFA40000E100E040420F0004000020D40600201D -:10BFB40018030020226103F0FF0993B9404684F876 -:10BFC4004090FDF7C1F96068FDF76DF94A460F210D -:10BFD40040B2FEF7C9F96068FDF765F940B2FEF7B3 -:10BFE400F7F94FF0020984F8419008F10401606800 -:10BFF400FFF734F8012384F8413094F84030012BE2 -:10C004003FF4E6AD606884F84190036A604923F028 -:10C014000103036288428369026A16D0B0F1804F3B -:10C0240013D0A1F5943188420FD001F58061884284 -:10C034000BD001F59631884207D001F54061884262 -:10C0440003D001F58051884201D123F0030343F06A -:10C05400010323F0F00322F00A0242F0020243F04B -:10C06400400383610262836923F00C038361836963 -:10C0740083614FF00109602384F841904FF0020A74 -:10C0840016934FF0000B4FF6FF731793CDF860A093 -:10C09400CDF868B016A984F8409084F841A0FEF762 -:10C0A4008FFF63689A6942F400629A619A6922F484 -:10C0B40080629A6199691A9A41EA02229A6184F823 -:10C0C400419084F840B09A6922F400629A6184F83D -:10C0D40041A01A6842EA09021A6084F84190DA68B9 -:10C0E40042EA0A02DA601A6A22F001021A621A6A41 -:10C0F40042EA09021A621A6842EA09021A60042131 -:10C104004046FFF7A8F897F800315B0705D597F884 -:10C11400003183F0840387F800311E4CDFF87C90F3 -:10C124004FF00A08D6F840717B1C002FC6F8403146 -:10C13400BFF656AD2D212046FCF79DFE0A2279421A -:10C144002046FCF71EFF49462046FCF70AFFAB6970 -:10C15400002B3FF44EAD1049DFF844A02046FCF715 -:10C1640000FF0027AB69BB427FF73DAD1FB15146CD -:10C174002046FCF7F6FEE95D10222046FCF701FF9D -:10C184000137EFE73C287FF4EBADFFF723BB00BF9B -:10C19400002C0140B804002021DB00081EDB00084D -:10C1A400B4DB00089A499B48CA689B4B70B54FF6AC -:10C1B400FF0422400243CA601A6842F010021A6067 -:10C1C400CB6823400343954CCB6000F0C3F963680C -:10C1D40013F00C0F0AD0636803F00C03082B69D129 -:10C1E400636803F4C033B3F5004F63D1022393FAB9 -:10C1F400A3F2226893FAA3F32368F82292FAA2F234 -:10C204001021B2FA82F223F0F80301FA02F2134386 -:10C214002360636803F00C03082B6AD04FF080732B -:10C2240093FAA3F3B3FA83F303F1845303F584136A -:10C234009B0000221A607A4A16684FF000700121B0 -:10C2440090FAA0F3256890FAA0F3B3FA83F303F00D -:10C254001F0301FA03F32B4240F08580E36A23F0C5 -:10C264000F03E362636823F4761343F4E413636017 -:10C274004FF0807393FAA3F3B3FA83F303F1845377 -:10C2840003F584139B000125196016684FF00071B3 -:10C2940091FAA1F3206891FAA1F3B3FA83F303F0BE -:10C2A4001F0305FA03F3034223D113689B1B022BDC -:10C2B400EED91EE0012292FAA2F3B3FA83F303F15A -:10C2C400845303F584139B0055491A600E680220B9 -:10C2D40090FAA0F3256890FAA0F3B3FA83F303F07D -:10C2E4001F0302FA03F32B4286D10B689B1B022B1C -:10C2F400EED9494A136803F00703012B39D963685F -:10C3040023F0F00363604FF0007393FAA3F2216803 -:10C3140093FAA3F3B3FA83F303F01F03012202FA9F -:10C3240003F30B4231D13F4B4FF47A721B68B3FBDA -:10C33400F2F3013BB3F1807F09D23B4A5360344BA3 -:10C34400F02183F823100023936007231360364AF7 -:10C35400136843F004031360002211464FF0FF30CA -:10C36400FEF702F870BD13689B1B022B7FF668AFC3 -:10C37400BFE7136823F0070343F002031360136855 -:10C3840003F00703022BCED1B9E7636823F003035C -:10C3940043F002036360224B196841F288306268FB -:10C3A40002F00C02082A22D11B4A136803F0070387 -:10C3B400022B21D8636823F4E06343F48063636051 -:10C3C400636823F460536360FEF7C2FF6368F0227E -:10C3D40092FAA2F2B2FA82F203F0F003D340134AC3 -:10C3E400D35CD8400F4B186000F0B4F89BE71A6890 -:10C3F400521A8242D3D996E7136823F0070343F015 -:10C4040002031360136803F00703022B8BD1D1E7F7 -:10C4140000ED00E00003FA05002002400010024095 -:10C42400D8070020A400002010E000E051D7000845 -:10C4340070B5444C444A4549454E062384F860305F -:10C44400072384F87430082384F88830092384F897 -:10C454009C300A23002584F8B030172384F8C430B4 -:10C4640084F8D83084F8EC3084F8003184F814313E -:10C474002846282384F8283184F83C3184F8503144 -:10C4840084F8643184F8783184F88C31E566C4F832 -:10C494008050C4F89450C4F8A850C4F8BC50C4F8F0 -:10C4A400D050C4F8E450C4F8F850C4F80C51C4F89F -:10C4B4002051C4F83451C4F84851C4F85C51C4F84C -:10C4C4007051C4F88451C4F8985100F092F84FF4B4 -:10C4D4007A73C4F8A83106F10803C4F8A031032321 -:10C4E40084F8BD32022384F8BC3204F2B113C4F8D8 -:10C4F400C032C4F8A45104F2F1135822294604F5B9 -:10C504003570A4F8C452C4F8C832A4F8C652A4F8CA -:10C51400CC52A4F8CE5200F0A3F80123C4F8F432AC -:10C52400283604F54B7384F8285084F8F852C4F87C -:10C53400D462C4F8885384F8B85384F8B953E361D7 -:10C5440070BD00BF180300200000002025B40008BF -:10C5540068D700080F4B4FF47A721B68B3FBF2F3F1 -:10C56400013BB3F1807F10B511D20B4A53600B4BE2 -:10C57400F02183F823100024072394600F21136013 -:10C584004FF0FF302246FDF7EFFE204610BD01209C -:10C5940010BD00BFA400002010E000E000ED00E0AA -:10C5A400DFF834D0002103E00C4B5B5843500431D6 -:10C5B4000B480C4B42189A42F6D30B4A02E0002374 -:10C5C40042F8043B094B9A42F9D3FCF789FF00F087 -:10C5D40023F8FEF7E3FFFEE70000012064DC000817 -:10C5E400000000200C0100200C010020C8080020DD -:10C5F400FEE70B460146184600F000B810B5054C9E -:10C6040013462CB10A4601460220AFF3008010BD48 -:10C61400204610BD0000000070B50D4E0D4CA41B4B -:10C62400A4100025A54209D10B4E0C4C01F07CF856 -:10C63400A41BA4100025A54205D170BD56F82530D1 -:10C6440098470135EEE756F8253098470135F2E76B -:10C6540044DC000844DC000844DC000860DC00081A -:10C6640002440346934200D1704703F8011BF9E7E3 -:10C674009368013B002B10B4936009DA9469A342D8 -:10C6840002DBCBB20A2B03D15DF8044B00F0DEBA17 -:10C694001368581C10601970C8B25DF8044B7047D9 -:10C6A400F8B506460F461446D518AC4201D1002011 -:10C6B40007E03A4614F8011B3046FFF7D9FF431C44 -:10C6C400F3D1F8BD2DE9F04F9DB00C461746039306 -:10C6D400064618B183690BB900F06AFC7C4B9C4296 -:10C6E40057D17468A38918075DD52369002B5AD0E4 -:10C6F4000023099320238DF8293030238DF82A3024 -:10C704004FF0010BB846454615F8013B002B55D1B7 -:10C71400B8EB070A0BD053463A4621463046FFF79A -:10C72400BFFF013000F0C480099B5344099398F87B -:10C734000030002B00F0BC8000234FF0FF32049344 -:10C744000793059206938DF853301A93052229789E -:10C754006048FEF7CBFE05F10108049B002831D1A7 -:10C76400D90644BF20228DF853201A0744BF2B2238 -:10C774008DF853202A782A2A2CD0079A00210A20DF -:10C78400A846013598F80030303B092B6DD971B3B8 -:10C7940026E0514B9C4201D1B468A3E74F4B9C4225 -:10C7A40008BFF4689EE72146304600F0A1FA00284D -:10C7B4009ED04FF0FF301DB0BDE8F08F252BA7D0E1 -:10C7C400A846A0E7434A801A0BFA00F018430490E5 -:10C7D4004546BBE7039A111D12680391002A01DB49 -:10C7E400079204E0524243F002030792049398F83C -:10C7F40000302E2B10D198F801302A2B08F10101BA -:10C8040037D1039B1A1D1B680392002BB8BF4FF04E -:10C81400FF3308F102080593314D98F80010032204 -:10C824002846FEF763FE40B14023401B03FA00F0A4 -:10C83400049B0343049308F1010898F80010294865 -:10C844008DF82810062208F10107FEF74FFE002894 -:10C8540034D0254B03BB039B073323F00703083372 -:10C864000393099B4B4409934CE700FB02320121DB -:10C8740086E70023059318460A258846013198F86F -:10C884000020303A092A03D9002BC5D00590C3E70C -:10C8940005FB00200123F0E703AB00932246134B72 -:10C8A40004A93046AFF30080B0F1FF3F8146D8D1F0 -:10C8B400A3895B063FF57DAF09987CE703AB009342 -:10C8C40022460A4B04A9304600F088F8ECE700BF82 -:10C8D40004DC0008B0DB000824DC0008E4DB00080A -:10C8E400B6DB0008BADB000800000000A5C600089B -:10C8F4002DE9F04791461F468A680B69DDF82080D0 -:10C904009342B8BF1346C9F8003091F84320064655 -:10C914000C4612B10133C9F800302368990642BFAE -:10C92400D9F800300233C9F80030256815F006053F -:10C9340007D104F1190AE368D9F800209B1A9D4233 -:10C9440029DB94F843302268003318BF0123920690 -:10C954002ED404F1430239463046C047013021D079 -:10C964002368E568D9F8002003F00603042B08BF08 -:10C97400AD1AA36822690CBF25EAE5750025934228 -:10C98400C4BF9B1AED184FF000091A344D451BD152 -:10C994000020BDE8F0870123524639463046C0479F -:10C9A400013003D14FF0FF30BDE8F0870135C2E715 -:10C9B400E1185A1C302081F84300224494F84510B1 -:10C9C40082F843100233C4E7012322463946304635 -:10C9D400C0470130E6D009F10109D7E72DE9F0435A -:10C9E40017460A7E85B06E2A984606460C460C9B6E -:10C9F40001F1430E00F0BC801AD8632A2ED00AD865 -:10CA0400002A00F0C880582A00F08A8004F1420508 -:10CA140084F842202AE0642A01D0692AF6D12168E8 -:10CA24001A6811F0800F23D0111D1960136827E0D4 -:10CA3400732A00F0B48008D86F2A2AD0702AE5D16E -:10CA44000A6842F020020A6003E0752A21D0782A9D -:10CA5400DCD1782284F845206E4964E01A6801F13B -:10CA64004205111D1960136884F842300123A3E0C4 -:10CA740011F0400F02F104011960D7D0B2F900306F -:10CA8400002B03DA2D225B4284F8432062490A22F8 -:10CA940017E02068196810F0800F03D0081D186093 -:10CAA4000B6806E010F0400F01F104001860F7D0A5 -:10CAB4000B8859496F2A14BF0A220822002084F8DF -:10CAC40043006568A560002DC0F29C80206820F0BA -:10CAD40004002060002B3FD1002D40F09580754666 -:10CAE400082A0BD12368DA0708D5236962689A42B9 -:10CAF400DEBF302305F8013C05F1FF35AEEB05033D -:10CB04002361CDF800803B4603AA21463046FFF757 -:10CB1400EFFE013055D14FF0FF3005B0BDE8F08392 -:10CB240081F845203C492268186812F0800F00F112 -:10CB340004051D6001D0036802E05506FBD5038897 -:10CB4400D00744BF42F0200222601BB9226822F0C1 -:10CB5400200222601022B1E77546B3FBF2F002FB1B -:10CB64001033CB5C05F8013D03460028F5D1B7E747 -:10CB740008681A68496910F0800F04D0101D186005 -:10CB84001368196007E010F0400F02F10400186008 -:10CB94001368F6D01980002323617546B1E71A683B -:10CBA400111D19601568626800212846FEF79EFC75 -:10CBB40008B1401B606063682361002384F843303C -:10CBC4009FE723692A4639463046C0470130A2D040 -:10CBD40023689B0707D5002504F11909E368039A24 -:10CBE4009B1A9D4205DBE068039B9842B8BF184638 -:10CBF40093E701234A4639463046C04701308AD07C -:10CC04000135EBE7002BA7D10B7884F8423004F10F -:10CC1400420565E7D2DB0008C1DB00080FB40A4B0C -:10CC240013B51C6824B1A36913B9204600F0C0F9F8 -:10CC340005AB049AA16801932046FFF743FD02B0B7 -:10CC4400BDE8104004B07047A8000020F8B50E46B7 -:10CC54001446054618B183690BB900F0A9F9214BB4 -:10CC64009C422AD16C68A369A360A3891A072ED5B4 -:10CC7400236963B323692068C01A6369F6B29842D2 -:10CC8400374604DB2146284600F028F928BBA36870 -:10CC9400013BA36023685A1C22601E706369013043 -:10CCA400984204D0A389DB0719D50A2E17D121464F -:10CCB400284600F013F990B10FE00B4B9C4201D1D0 -:10CCC400AC68D0E7094B9C4208BFEC68CBE721462F -:10CCD400284600F00DF80028CCD04FF0FF37384636 -:10CCE400F8BD00BF04DC000824DC0008E4DB000815 -:10CCF400324B70B51D6806460C4625B1AB6913B9B5 -:10CD0400284600F055F92E4B9C420FD16C68B4F9BB -:10CD14000C309AB215072CD4D00611D409223260F3 -:10CD240043F04003A3814FF0FF3070BD254B9C427C -:10CD340001D1AC68EBE7244B9C4208BFEC68E6E702 -:10CD4400510712D5616B41B104F14403994202D0F9 -:10CD5400304600F037FA00236363A38923F02403E9 -:10CD6400A3810023636023692360A38943F008033C -:10CD7400A38123694BB9A38903F42073B3F5007F1E -:10CD840003D02146304600F0C1F9A28912F0010314 -:10CD94000CD00023A36063695B42A361236953B988 -:10CDA400B4F90C3013F08000BAD170BD920758BFAB -:10CDB4006369A360F2E70020F7E700BFA800002042 -:10CDC40004DC000824DC0008E4DB00088A892DE97F -:10CDD400F041054610070C465AD44B68002B05DC7D -:10CDE4000B6C002B02DC0020BDE8F081E66A002E0B -:10CDF400F9D0002312F480522F682B6033D0606D79 -:10CE0400A3895A0705D56368C01A636B0BB1236CF9 -:10CE1400C01A00230246E66A216A2846B047431C2A -:10CE2400A38906D129681D294BD82B4ACA40D607A5 -:10CE340047D500226260D9042269226004D5421CCD -:10CE440001D12B6803B96065616B2F600029CAD0DA -:10CE540004F14403994202D0284600F0B3F90020BB -:10CE64006063BDE8F081216A01232846B047411C74 -:10CE7400C6D12B68002BC3D01D2B01D0162B01D19A -:10CE84002F60B0E7A38943F04003A381BDE8F0819C -:10CE94000F69002FA7D093070E6808BF4B690F6076 -:10CEA40018BF0023A6EB07088B60B8F1000F9ADDCA -:10CEB40043463A46216AA66A2846B047002807DC5A -:10CEC400A38943F04003A3814FF0FF30BDE8F08114 -:10CED4000744A8EB0008E8E70100402038B50B69D7 -:10CEE40005460C46DBB118B183690BB900F060F854 -:10CEF4000C4B9C4209D16C68B4F90C307BB12146CF -:10CF04002846BDE83840FFF761BF074B9C4201D17A -:10CF1400AC68F1E7054B9C4208BFEC68ECE70020E5 -:10CF240038BD00BF04DC000824DC0008E4DB000892 -:10CF3400014900F0A9B800BFE1CE0008002310B5F4 -:10CF4400044603604360836081814366C281036158 -:10CF540043618361194608225C30FFF781FB054B6E -:10CF64006362054BA362054BE362054B24622363B2 -:10CF740010BD00BF41D3000863D300089BD3000851 -:10CF8400BFD3000870B54A1E682555430E4605F107 -:10CF9400740100F065F9044640B1002180E84200C4 -:10CFA4000C30A06005F16802FFF75AFB204670BD03 -:10CFB400836910B5044633BB8364C3640365124BB1 -:10CFC400124A1B688262984204BF0123836100F005 -:10CFD4001FF86060204600F01BF8A060204600F0B7 -:10CFE40017F80022E06004216068FFF7A7FF012220 -:10CFF4000921A068FFF7A2FF02221221E068FFF7CF -:10D004009DFF0123A36110BDACDB000835CF0008F0 -:10D01400F8B51C4B1E68B369074613B93046FFF7D1 -:10D02400C7FF4836B4687368013B03D5336833B12E -:10D034003668F7E7B4F90C506DB16834F4E70421AD -:10D044003846FFF79FFF30600028F1D10C233B6086 -:10D0540004462046F8BD4FF6FF73E3810123A38104 -:10D0640065662560A560656025616561A561082226 -:10D07400294604F15C00FFF7F3FA6563A563A56430 -:10D08400E564E6E7ACDB00082DE9F8438046894611 -:10D0940000F14804002614B93046BDE8F883A568B9 -:10D0A4006768013F01D52468F5E7AB89012B07D9EF -:10D0B400B5F90E30013303D029464046C84706432C -:10D0C4006835EEE770B50E46B1F90E10002990B040 -:10D0D40014461D4607DA00232B60B3891A0610D4C0 -:10D0E4004FF480630EE001AA00F08EF90028F2DB11 -:10D0F400029A02F47042A2F500535A425A412A603D -:10D10400EEE740230020236010B070BD8B8973B517 -:10D114009D0706460C4607D504F1470323602361A7 -:10D124000123636102B070BD01AB6A46FFF7CAFF19 -:10D1340000990546304600F093F848B9B4F90C302C -:10D144009A05EFD423F0030343F00203A381E3E73A -:10D154000D4BB362A389206043F08003A381009B3D -:10D164006361019B20615BB1B4F90E10304600F09D -:10D174005DF928B1A38923F0030343F00103A381DC -:10D18400A3891D43A581CDE735CF0008024B014695 -:10D19400186800F065B800BFA8000020024B0146E3 -:10D1A400186800F00FB800BFA800002010B5431E97 -:10D1B4000A44914200D110BD11F8014B03F8014F0C -:10D1C400F7E7000038B50546002945D051F8043C7E -:10D1D4000C1F002BB8BFE41800F04AF91F4A13686B -:10D1E400104633B9636014602846BDE8384000F047 -:10D1F40040B9A3420CD921686218934204BF1A684B -:10D204005B68636004BF521822600460ECE7134655 -:10D214005A680AB1A242FAD919685818A0420BD127 -:10D2240020680144581882421960DDD110685268A0 -:10D234005A6001441960D7E702D90C232B60D3E765 -:10D24400206821188A4204BF11685268626004BFD2 -:10D25400091821605C60C7E738BD00BF1C080020C6 -:10D2640070B5CD1C25F0030508350C2D38BF0C25F1 -:10D27400002D064601DBA94203D90C2333600020AC -:10D2840070BD00F0F5F8234A1468214691B9224C88 -:10D2940023681BB9304600F041F820602946304627 -:10D2A40000F03CF8431C26D10C233360304600F0D8 -:10D2B400E0F8E4E70B685B1B1AD40B2B0FD90B6067 -:10D2C400CD50CC18304600F0D4F804F10B00231DE7 -:10D2D40020F00700C31A1BD05A42E25070BD8C42A2 -:10D2E4000DBF63684B686360136018BF0C46E9E7C1 -:10D2F4000C464968CAE7C41C24F00304A04205D0C4 -:10D30400211A304600F00AF80130CDD02560D9E763 -:10D3140070BD00BF1C0800202008002038B5064C52 -:10D324000023054608462360FBF794F9431C02D109 -:10D33400236803B12B6038BDC408002010B50C4627 -:10D34400B1F90E1000F096F80028ABBF636DA38905 -:10D354001B1823F48053ACBF6365A38110BD2DE972 -:10D36400F0411F468B89DB0505460C46164605D55C -:10D3740002230022B1F90E1000F068F8A389B4F971 -:10D384000E1023F48053A38132463B462846BDE861 -:10D39400F04100F017B810B50C46B1F90E1000F0CA -:10D3A40055F8431CA38915BF606523F4805343F4E7 -:10D3B4008053A38118BFA38110BDB1F90E1000F0F2 -:10D3C40013B8000038B5074C05460846114600223C -:10D3D40022601A46FBF783F8431C02D1236803B189 -:10D3E4002B6038BDC408002038B5064C0023054620 -:10D3F40008462360FBF72BF9431C02D1236803B1D1 -:10D404002B6038BDC408002038B5074C00230546FE -:10D41400084611462360FBF715F9431C02D1236823 -:10D4240003B12B6038BD00BFC408002038B5064CDA -:10D434000023054608462360FBF702F9431C02D18A -:10D44400236803B12B6038BDC408002038B5074CED -:10D45400054608461146002222601A46FBF7EEF8FC -:10D46400431C02D1236803B12B6038BDC4080020DB -:10D474007047704738B5074C0546084611460022E8 -:10D4840022601A46FBF7D8F8431C02D1236803B183 -:10D494002B6038BDC4080020244B1A68D0075CBF39 -:10D4A40001221A60224B1A68D1075CBF01221A605C -:10D4B400204B1A68D2075CBF01221A601E4B1A68FF -:10D4C400D0075CBF01221A601C4B1A68D1075CBFED -:10D4D40001221A601A4B1A68D2075CBF01221A6033 -:10D4E400184B1A68D0075CBF01221A60164B1A68E1 -:10D4F400D1075CBF01221A60144B1A68D2075CBFC3 -:10D5040001221A60124B1A68D0075CBF01221A600C -:10D51400104B1A68D1075CBF01221A600E4B1A68BF -:10D52400D2075CBF01221A60704700BF5008002078 -:10D534004C0800204808002044080020400800202F -:10D544003C0800203808002034080020300800205F -:10D554002C0800202808002024080020244B1A68E6 -:10D56400D0075CBF01221A60224B1A68D1075CBF46 -:10D5740001221A60204B1A68D2075CBF01221A608C -:10D584001E4B1A68D0075CBF01221A601C4B1A6834 -:10D59400D1075CBF01221A601A4B1A68D2075CBF1C -:10D5A40001221A60184B1A68D0075CBF01221A6066 -:10D5B400164B1A68D1075CBF01221A60144B1A6813 -:10D5C400D2075CBF01221A60124B1A68D0075CBFF5 -:10D5D40001221A60104B1A68D1075CBF01221A603D -:10D5E4000E4B1A68D2075CBF01221A60704700BF55 -:10D5F400800800207C08002078080020740800209F -:10D60400700800206C0800206808002064080020CE -:10D61400600800205C0800205808002054080020FE -:10D62400184B1A68D1075CBF01221A60164B1A689E -:10D63400D2075CBF01221A60144B1A68D0075CBF82 -:10D6440001221A60124B1A68D1075CBF01221A60CA -:10D65400104B1A68D2075CBF01221A600E4B1A687D -:10D66400D0075CBF01221A600C4B1A68D1075CBF5B -:10D6740001221A600A4B1A68D2075CBF01221A60A1 -:10D68400704700BFA00800209C08002098080020D4 -:10D6940094080020900800208C08002088080020AE -:10D6A40084080020184B1A68D1075CBF01221A6055 -:10D6B400164B1A68D2075CBF01221A60144B1A6811 -:10D6C400D0075CBF01221A60124B1A68D1075CBFF5 -:10D6D40001221A60104B1A68D2075CBF01221A603B -:10D6E4000E4B1A68D0075CBF01221A600C4B1A68F3 -:10D6F400D1075CBF01221A600A4B1A68D2075CBFCB -:10D7040001221A60704700BFC0080020BC08002036 -:10D71400B8080020B4080020B0080020AC0800209D -:10D72400A8080020A4080020F8B500BFF8BC08BC75 -:10D734009E467047F8B500BFF8BC08BC9E467047CB -:10D74400010102030406080A0E0F04080C0000007D -:10D754000000000000010203040607080901040890 -:10D764000200000000000000000000007B8E0008A2 -:10D77400C58D00086B8E0008298E0008518E0008A4 -:10D78400158E0008000000000000000085820008DB -:10D79400DD89000825880008D982000803020A13DD -:10D7A40015141A0809271607060519182A2C5B0DE3 -:10D7B4000E0F172D2E2F505122232B3229282625C8 -:10D7C4000C0B1C1B12111F1E1D2400010410212010 -:10D7D40007062223251B12240400000000740040C5 -:10D7E40003800000050000000074004003000100F5 -:10D7F400FF00000000000000000000000100000025 -:10D8040000400140128910000400000000040040A0 -:10D8140012020100060000000044014012810000D1 -:10D8240007000000004801401281000008000000C9 -:10D83400002C01401286000009000000002C014069 -:10D84400120601000A000000002C014012860100AB -:10D854000B000000002C0140120B02000C00000021 -:10D8640000440140128100000D000000004401400A -:10D87400128110000E000000003401401205010066 -:10D884000F0000000034014012820000100000006C -:10D89400002C014012061100130000000034014066 -:10D8A40012841000140000000044014012810000A2 -:10D8B4001500000000480140128A00001600000014 -:10D8C400004401401281100017000000004801408C -:10D8D4001281100018000000004401401281000071 -:10D8E4001900000000480140128100001A000000E5 -:10D8F40000000040128101001D000000002C0140C6 -:10D90400128610001E000000004001401281000039 -:10D914001F0000000040014012010100200000002F -:10D92400002C01401282000021000000002C014064 -:10D934001202010022000000002C014012820100AA -:10D9440023000000002C0140120202002600000007 -:10D954000004004012820000270000000004004080 -:10D96400120201002800000000040040128201009D -:10D974002900000000040040120202002A000000F6 -:10D9840000340140128410002B00000000340140D8 -:10D99400120411002C000000003401401284110014 -:10D9A4002D000000002C01401284100050000000E3 -:10D9B400002C014012861100FF000000000000004E -:10D9C40000000000D40600200200000000440040D3 -:10D9D400120700000900000000380140120700008F -:10D9E4000E00000000440040120700001300000075 -:10D9F40000440040120700001600000000380140F7 -:10DA0400120700001900000000480040120700003F -:10DA14001A00000000480040120700002400000023 -:10DA240000380140120700002A000000004C0040AA -:10DA3400120500002C0000000050004012050000F8 -:10DA4400FF000000000000000000000003000000D0 -:10DA540000440040120700000A00000000380140A2 -:10DA6400120700000F0000000044004012070000ED -:10DA740014000000004400401207000017000000DA -:10DA84000038014012070000180000000048004060 -:10DA9400120700001B0000000048004012070000AD -:10DAA4002500000000380140120700002B00000090 -:10DAB400004C0040120500003200000000500040FD -:10DAC40012050000FF00000000000000000000003C -:10DAD4000000000001020304010203040506070814 -:10DAE400090A0B0C0D0E0F100203040506070809A2 -:10DAF4000A0B0C0D0E0F101054494D3A20556E6B45 -:10DB04006E6F776E2074696D657220696E737461CF -:10DB14006E6365005374617274000D0A0054656C81 -:10DB2400656772616D3A200054494D3A20556E6B19 -:10DB34006E6F776E20636C6F636B20736F757263A7 -:10DB4400650054494D3A20556E6B6E6F776E2074A4 -:10DB5400696D6572204952516E004552524F523AD6 -:10DB6400206174206C65617374206F6E652055416B -:10DB740052542070696E20686173206E6F20706546 -:10DB8400726970686572616C0A004552524F523A6C -:10DB940020552853294152542070696E73206D69B1 -:10DBA400736D617463680A00AC000020232D302B70 -:10DBB4002000686C4C00656667454647003031328A -:10DBC40033343536373839414243444546003031E1 -:10DBD4003233343536373839616263646566000040 -:10DBE4000000000000000000000000000000000031 -:10DBF4000000000000000000000000000000000021 -:10DC04000000000000000000000000000000000010 -:10DC14000000000000000000000000000000000000 -:10DC240000000000000000000000000000000000F0 -:10DC340000000000000000000000000000000000E0 -:10DC4400A9C10008B981000835C400089DD40008A2 -:0CDC540061D5000825D60008A9D60008FC -:04DC600095810008A2 -:10DC640000000000FF0000000000000000000000B1 -:10DC740000000000000000000000000000000000A0 -:10DC84000000000000000000000000000000000090 -:10DC94000000000000000000000000000000000080 -:10DCA4000000000000000000000000000000000070 -:10DCB4000000000000000000000000000000000060 -:10DCC4000000000000000000000000000000000050 -:10DCD400000000000000000000000000050000003B -:10DCE4000000000000000000000000000000000030 -:10DCF4000000000000000000000000000000000020 -:10DD04000000000000127A00AC00002000000000B7 -:10DD140004DC000824DC0008E4DB00080000000048 -:10DD240000000000000000000000000000000000EF -:10DD340000000000000000000000000000000000DF -:10DD440000000000000000000000000000000000CF -:10DD540000000000000000000000000000000000BF -:0CDD6400000000000000000000000000B3 -:040000050800C5A585 -:00000001FF +:020000040800F2 +:1080000000000120A5C50008F5C50008F5C5000859 +:10801000F5C50008F5C50008F5C50008000000001A +:10802000000000000000000000000000F5C500088E +:10803000F5C5000800000000F5C500083F830008F2 +:10804000F5C50008F5C50008F5C50008F5C5000828 +:10805000F5C50008F5C50008CBB40008C5B4000894 +:10806000F5C50008BFB40008B9B40008F5C500089C +:10807000F5C50008F5C50008F5C50008F5C50008F8 +:10808000F5C50008F5C50008F5C50008F5C50008E8 +:10809000F5C50008F5C50008F5C50008A3B400083B +:1080A0000992000879920008F9910008F5C50008C6 +:1080B00069920008599200084992000873900008DC +:1080C0006D9000086790000861900008F5C50008F1 +:1080D000F5C500085DA4000845A4000829A400080F +:1080E0008BB4000851950008F5C50008F5C50008D7 +:1080F00019920008F5C50008F5C50008F5C5000887 +:10810000F5C500080000000000000000F5C50008EB +:108110000DA40008F1A30008399200082992000874 +:10812000F5C50008F5C50008F5C50008F5C5000847 +:10813000F5C50008F5C500080000000000000000BB +:10814000F5C50008F5C50008F5C5000800000000E9 +:10815000000000000000000000000000000000001F +:108160005B90000855900008F5C50008F5C50008AB +:10817000F5C50008F5C50008E9910008F5C5000837 +:10818000F5C50008F5C5000800000000000000006B +:04819000F5C5000829 +:1081940010B5054C237833B9044B13B10448AFF33D +:1081A40000800123237010BD0C010020000000009A +:1081B4002CD700080C4B43B10C480D4910B5AFF354 +:1081C40000800C48036823B910BD0A48036833B91A +:1081D4007047094B002BF7D0BDE810401847064BF9 +:1081E400002BF5D0184700BF000000002CD7000872 +:1081F400100100200C01002000000000074A937CBD +:1082040000EB4000023318448318805C5B7813F061 +:10821400200F0ABF024B8018C01870476401002069 +:10822400D4060020074B9A7C00EB40001044034422 +:108234001A79052AC3BF044B9B1813F8060C0020B7 +:10824400704700BF6401002044D70008704790F8CD +:10825400242062B1064A916949B1126C3AB9054ABF +:1082640092F80021520702D503685B6818477047EB +:1082740018030020D4060020002380F8243070471F +:1082840010B50446806C18B1036803211B68984735 +:10829400002384F8243010BD4B060D4B1B7803F0EB +:1082A4000C0363F04F0380F82C30836A80F830307D +:1082B40044BF02F03C0211431A12602380F83210CA +:1082C40080F82F2080F8313007212C3002F0DCBCFC +:1082D4001803002010B54FF48072044600210748AB +:1082E40004F0BEF9064A00231370012284F84C20DE +:1082F400044A23651268626510BD00BF6401002052 +:108304006C020020D8070020044B41685A6891424F +:1083140004BF012283F88D2002F0C2B84406002075 +:10832400431C02D1002070470C3191F900305A1CD3 +:10833400F8D09842F8D148687047024A136801336C +:1083440013607047D807002008B5054901F033F9D8 +:10835400C0F3C43002280CBF1020002008BD00BFA9 +:10836400DCD7000808B5064901F025F9C0F3C4308C +:10837400023802289DBF034BC018807A002008BD34 +:1083840000D8000844D7000888B0072867D8DFE879 +:1083940000F004121E2A36424E5A324B5A6942F4F5 +:1083A40000325A615B6903F400330093009B4FF081 +:1083B400904008B070472B4B2B485A6942F48022F6 +:1083C4005A615B6903F480230193019BF1E7254B18 +:1083D40026485A6942F400225A615B6903F4002377 +:1083E4000293029BE5E71F4B21485A6942F480122D +:1083F4005A615B6903F480130393039BD9E7194B18 +:108404001C485A6942F400125A615B6903F4001370 +:108414000493049BCDE7134B17485A6942F4800236 +:108424005A615B6903F480030593059BC1E70D4B17 +:1084340012485A6942F400025A615B6903F400036A +:108444000693069BB5E7074B0D485A6942F4803200 +:108454005A615B6903F480330793079BA9E7002003 +:10846400A7E700BF001002400004004800080048CD +:10847400000C004800100048001400480018004890 +:10848400001C0048C0F30312012300F00F0003FA9C +:1084940000F051F82230034341F822307047C0F312 +:1084A400031300F00F0051F8233023FA00F000F01A +:1084B40001007047034B19681A68521A8242FBD3B1 +:1084C400704700BFD807002001207047024B1622D6 +:1084D4001A604FF0FF307047C4080020FEE72DE912 +:1084E400F74F504E8A4633684F4901930220154690 +:1084F400FFF716FF20B90025284603B0BDE8F08F2A +:108504004A4A002352F823105FFA83F911B109682B +:10851400884224D00133052BF4D1454C94F87430AF +:10852400042B1AD9434B50E090424DD193F90030BB +:1085340084F88930022384F888304FF41653A367F3 +:108544003B480023C4F88430E367C4F8803001F06A +:1085540040F894F87430042BCDD894F874901FFA32 +:1085640085F8324B53F8294094F86930202B51D1C7 +:10857400BAF1000F4ED0B8F1000F4BD094F8683028 +:10858400012B47D04FF0010384F868300023E366E1 +:10859400212384F86930D6F800B0A4F85080A4F8F8 +:1085A40052805746B4F852309BB2A3B94FF47A7351 +:1085B4005A464021204602F0B9F958BB202384F8DA +:1085C400693084F8680097E70C335A68002AABD105 +:1085D4004FF0FF33ACE7B4F85230013B9BB2A4F840 +:1085E40052305A464FF47A738021204602F09EF9A5 +:1085F40080B9A3682268B3F5805F07D123692BB9DA +:1086040037F8023BC3F308031385CBE73B781385A4 +:108614000137C7E73368019A9B1AB3F57A7FA0D371 +:1086240069E700BFD8070020CCD900082801002042 +:108634000800002050DA000800207047002070472E +:10864400012070474FF400534B60002070474FF0F7 +:10865400FF307047094B5968024609B90849596007 +:108664005868694602448A4289BF064B5A600C2204 +:108674004FF0FF3088BF1A60704700BF6C020020C3 +:10868400C8080020C408002030B50B4B0412997DA3 +:10869400C2B21B33012088420ADC13F8025CAC42EC +:1086A40003D113F8015CAA4204D001300233F2E78B +:1086B4004FF0FF3030BD00BF640100202DE9F04FC2 +:1086C400344C637CA57C1F19E35C85B05B0002908D +:1086D4000E4690460193FFF7D7FFB0F1000A56DB30 +:1086E400AB1C07F1010903934FF0030B019BA9EBAA +:1086F400070293424BDB99F80020924532D199F856 +:108704000150039B1BFB05322244802E527801D07A +:10871400402E30D102F01402142A23D12846FFF748 +:108724006DFD01462846FFF77DFD00281DDDC21DB5 +:1087340042440139801A12F801E901F801EFC242FA +:10874400F9D1A27C144B22445278D2180AD015F0E5 +:10875400010F4FEA650508BF0820515D18BF80204E +:108764000143515509F10209C0E798F8072002F0C6 +:108774003F020A70E5E7002EF4D102F00C020C2A45 +:10878400F0D101220299284600F008F8EAE705B082 +:10879400BDE8F08F64010020D4060020F8B5174628 +:1087A40004460D46FFF72AFD06462046FFF73AFD2C +:1087B400194BBC221A710A12DA7119720022411C77 +:1087C400974201F00F019A7261F01F0114BF402219 +:1087D400802204465972DA7258B1013830440B339E +:1087E400211A4218002A09DD10F8012903F8012F83 +:1087F400F7E7317801F03F010A43DA722846802115 +:10880400064AFFF75BFF04F1080189B20348BDE89B +:10881400F84002F039BA00BFEC050020F005002052 +:1088240090F824302DE9F74F0446002B00F0BB806C +:10883400FFF70DFD94F84C30DFF89881002B7DD0C4 +:108844005E4B1B6C002B79D15D4E626D33689B1AB5 +:10885400226D934269D35B4B9A7CD1184F785A4965 +:108864007F1862D0D8F8085093F811E0D15C0191D8 +:1088740005EB45009E44104418440EF101091633DB +:10888400019A954280F289804FEA650C15F0010A3D +:1088940017F80C100A4618BF0A1102F0030BBBF1BB +:1088A400030F21D1BAF1000F14BF6FF0300A6FF03B +:1088B400030A01EA0A0107F80C109EF800C04946B1 +:1088C4000EEB4C0C8C450FD991F801A055450ED1F7 +:1088D40009784900013101EB030A11F803C09AF841 +:1088E400011051EA0C2104D101350330C8E70231EB +:1088F400E8E790F803C01CF0040FF5D012F004026E +:1089040044D03248BC2303710B12C371E123437278 +:10891400002301728372C3720821043002F0B4F997 +:1089240001353368C8F808506365636D3268D21A3C +:10893400B2F5FA6F24BF03F57A736365A36A3BB19A +:108944001F4BA26B1B689B1A41F2707293422DD885 +:1089540098F800303BB3194B93F828201ABB1A6CD3 +:108964000ABB9B69FBB9A36AEBB9194A144910689D +:108974000B68B0B11B1A002B15DDA06C98B103680D +:108984001B68022103B0BDE8F04F18471CF0400FEC +:10899400AAD02846FFF702FFC2E70023C8F8083030 +:1089A400C3E73233136003B0BDE8F08F00228121A6 +:1089B4002046FFF771FC0023A362C9E718030020D7 +:1089C400D807002064010020D4060020EC05002014 +:1089D400640200206C0200202DE9F743B24E96F8A1 +:1089E4000680F2783779F5794FEA082303F4407367 +:1089F4002B4357EA0227044699B22ED1AB4A92F888 +:108A04000021D0070ED5C02B11D13389A84A5BBAF7 +:108A14009BB21912D1751376A64A336201211170E3 +:108A2400A54A17600023B36103B0BDE8F083B1F534 +:108A3400807FF7D13378E77103F00C0363F04F03C1 +:108A44002371E12363720123A37240232772E3722B +:108A54000821201D02F018F9E4E796F90520002A00 +:108A640008F0C30EC0F25981326A9742DAD11EF07F +:108A7400800F7778B27843D042EA072718F0400293 +:108A840024D0BEF1C20F14D190F83C206AB1826A9E +:108A940097420AD1036C08F03C029A4205D0436B1A +:108AA4000264043303F03C034363012384F83C3041 +:108AB400B8E7BEF1C30FF8D1826A9742F5D100221C +:108AC4008121FFF7E9FB0023A362EEE7B8F1800FF1 +:108AD4000ED1826A002AA5D1784987620968816328 +:108AE4004FF0FF33426380F83C200364726299E7DD +:108AF400B8F1810F96D1816A8F4293D18262F5E7F2 +:108B0400806A42EA072282428CD16C4A1268A263CC +:108B140001F470720020B2F5007FA0725AD012D80E +:108B2400B2F5C07F22D0B3F5607F09DB40F2813219 +:108B3400934240F3C78040F2D132934200F0E08088 +:108B4400C32100251FE0B2F5207F43D0B2F5407F5A +:108B5400E9D18A06F4D1632363724323A3724023C9 +:108B6400E37200232373122363730AE06422337ACB +:108B7400627265F03F054122A272E572237360734D +:108B8400A073C221012508F03C022046FFF784FBB4 +:108B9400002D3FF447AF3378617A03F00C0363F0A0 +:108BA4004F032371A36A23721A12A37AE27113F09A +:108BB400400217BF626B84F83C2023F03C0313434C +:108BC40018BFA37201F00F011CBF012384F83C30CD +:108BD40007313EE73789B2F5207F7FBA05F00F05EC +:108BE400BFB240F0C380606C30B1036839469B6803 +:108BF4009847002840F09C80A7F58070FF283B46EA +:108C04000DD82B4B2E492A46184404F0CFFA294B91 +:108C140001221A70284B00221A60C22191E7FF2F0B +:108C2400FBDC602F06D9214825492A46384404F044 +:108C3400BDFAF2E75A195F2AF5D90A35204A1B49C9 +:108C440035449542E9D0602B107814BFC85481F89C +:108C5400000101320133F4E7FF2F72DD606C28B1AB +:108C6400036839469B689847002873D105F163036C +:108C740063724223A37245F040053B12E5722373ED +:108C840067737EE779195F2960D9084912F1FF32C9 +:108C9400E4D3602B14BF11F803E091F800E180F8ED +:108CA40000E001330130F1E718030020D40600206E +:108CB400640100206C02002064020020D807002018 +:108CC40022030020CB0708D5327A737A2AB9FF2B06 +:108CD40002BF4FF080532E4A1A60A06C18B103688B +:108CE40001211B689847BFF34F8F2A492A4BCA6852 +:108CF40002F4E0621343CB60BFF34F8F00BFFDE784 +:108D0400622363724323A372D223E3720023237387 +:108D140037E7002384F82C3084E6BEF1000F7FF49B +:108D240081AE324601F470713846FFF7C7FC79E62C +:108D3400606C1A4A03683946D3F800902B46C8473A +:108D44005AE7602F04F10E002A469BD914492A469B +:108D5400394419E0606C002203680092D3F804904F +:108D640004F10E022B463946C8477FE7B2F5007F6F +:108D7400CFD1A7F58071FF293B463FF66DAF094B74 +:108D84002A46194404F10E0004F010FA66E700BF05 +:108D940055B01F5E00ED00E00400FA052203002038 +:108DA400D406002064010020044B01685A689142F3 +:108DB40004BF012283F88C2001F072BB440600201A +:108DC4002DE9F04106460D4601EB02080C464445E8 +:108DD400A4EB050707D0336814F8011B1B68304661 +:108DE40098470028F3D13846BDE8F081B0F89A30AE +:108DF400013303F03F03A0F89A30B0F89A30B0F88A +:108E040098009BB298424FF0FF3018BF0020704783 +:108E1400037C33B1B0F82C21B0F82E319BB29A42C6 +:108E2400FAD17047B0F82421B0F8263192B29A42B0 +:108E34001FBFD0F82021D25C013303F03F031ABFD7 +:108E4400A0F8263110464FF0FF307047B0F82431B7 +:108E5400B0F826219BB293421ABFD0F82031985C17 +:108E64004FF0FF307047B0F82431B0F82601181ADB +:108E740000F03F00704738B5012303740446B0F88E +:108E84002C01C31803F03F03B4F82E2192B293428D +:108E9400FAD0D4F82821194D115494F80821A4F8D3 +:108EA4002C3155F8221091F8693091F86A100B436F +:108EB40003F02103212B1ED0114B124943F8221039 +:108EC40003EB820304F194025A610021022294F913 +:108ED4001E0101F049FA94F91E0101F079FAB4F87F +:108EE4002E11D4F8282194F8083112FA81F155F89A +:108EF400230002F0A7FA012038BD00BF280100209A +:108F040078020020F18D000838B5044620B390F8AB +:108F14007420124B53F8220090F8693090F86A20BC +:108F2400134303F02203222B16D0214611F8755F58 +:108F340001F0BAFCB4F89030B4F89220013303F095 +:108F44003F03934208D0B4F89020D4F88C1092B226 +:108F54009BB28D54A4F8903038BD00BF2801002086 +:108F6400FEE770B505460C46084649B102F00EFB13 +:108F74002B680246214628465B68BDE870401847C6 +:108F840070BD30B58BB00B4600218DF82410012A3A +:108F9400D8BF0A220DF12304B3FBF2F505FB12330B +:108FA40003F0FF03092B94BF30333733DBB2214680 +:108FB40004F801392B46002DEED1FFF7D2FF0BB098 +:108FC40030BD2DE9F84306460C46071D00F107099C +:108FD400002517F8011B0A222046FFF7D2FF236859 +:108FE40080461B682E2120469847B94540440544D5 +:108FF400EFD10A22F1792046FFF7C3FF2844BDE8E8 +:10900400F88300BF0368DA69D20782B003D59B8C6A +:109014009BB2019308E0DA699007F8D4DA69510742 +:10902400F5D4DA691207F2D4019B02B0704710B587 +:1090340048B10649002351F82340A042DAB203D0D4 +:109044000133052BF7D10522104610BD280100205D +:10905400002001F059B9002000F07CBD002001F08F +:1090640053B9002000F076BD002001F04DB9002076 +:1090740000F070BD436D10B5044663B190F8502004 +:10908400012A08D190F82810C1F1200111F0FF0144 +:1090940001D05C3098472046BDE8104000F061BD27 +:1090A40038B583689A42044650D101294FD1002330 +:1090B40080F87C3080F85030836D03B1984794F881 +:1090C400413003F02803282B40D194F87C50002D24 +:1090D4003CD00521204602F067F994F84030012B7A +:1090E40034D0012384F8403094F841302A2B03D142 +:1090F4000221204602F058F9292384F84130202324 +:1091040084F842302346002153F85C2B6164506894 +:1091140020F40040506065856362638D23854FF0C1 +:109124000073E36290692C4B6363C50344BF08205A +:10913400D06184F8401029498B424AD028498B4297 +:109144000CBFB823FA2311680B43136038BD90F8A1 +:10915400413003F028030125282B80F85050F5D125 +:10916400062102F021F994F84030AB42EED094F895 +:10917400413084F84050292B03D12946204602F07F +:1091840013F92A2384F841302025234684F84250D9 +:1091940053F85C2B00216164506820F40040506057 +:1091A40065856362638D23854FF00073E362906984 +:1091B400094B6363C0035CBF0820D06184F840108E +:1091C40006498B4205D006498B420CBFB823FC23C9 +:1091D400B9E7B823B7E700BFFB9F0008ABA10008BD +:1091E4008D9F0008024BD86C08B101F0A1BF7047F5 +:1091F400A0020020024B186C08B101F099BF70471F +:10920400A0020020024B986B08B101F091BF704797 +:10921400A0020020024BD86908B101F089BF704751 +:10922400A0020020024B986908B101F081BF704789 +:10923400A0020020024B586908B101F079BF7047C1 +:10924400A0020020024BD86808B101F071BF70473A +:10925400A0020020024B986808B101F069BF704772 +:10926400A0020020024B586808B101F061BF7047AA +:10927400A002002010B5064C206808B101F058FF88 +:10928400E06B18B1BDE8104001F052BF10BD00BF43 +:10929400A0020020836C2BB1027F012A02D100219D +:1092A40004381847704708B5431C12D0214B984224 +:1092B4003DD019D8A3F59233984230D00BD8B0F1F1 +:1092C400804F28D0A3F58063984226D01A4803F033 +:1092D400A5FC002008BD194B984222D003F58063F9 +:1092E4009842F3D1372008BD154B98421FD009D8B6 +:1092F400A3F58053984216D003F540639842E5D114 +:10930400182008BD0F4B98420FD003F50063984214 +:10931400DCD14E2008BD1C2008BD1D2008BD1E2028 +:1093240008BD362008BD2C2008BD1A2008BD192010 +:1093340008BD00BF002C014046DB000800100040BF +:1093440000440140004801400368594A93428CB0EC +:109354002CD102F56442916941F4006191619269F2 +:1093640002F400620192019A524A1060524A9342F6 +:109374003CD102F5FE32D16941F02001D161D269BC +:1093840002F020020692069A4A4A90614B4A93429E +:109394003BD14B4B9A6942F480129A619B6903F466 +:1093A40080130B930B9B434BD8642EE0B3F1804F97 +:1093B4000CD1434AD16941F00101D161D26902F073 +:1093C40001020292029A3B4A5060CFE73D4A93421F +:1093D4001DD102F50332D16941F00201D161D26994 +:1093E40002F002020392039A324A9060364A934290 +:1093F4001DD1334B9A6942F400529A619B6903F47C +:1094040000530793079B2B4BD8610CB070472F4A2E +:1094140093421CD102F50232D16941F00401D161B9 +:10942400D26902F004020492049A224AD060284AC3 +:1094340093422CD1224B9A6942F480329A619B69FF +:1094440003F480330893089B1A4B9863DDE7214AA1 +:1094540093428BD102F50032D16941F01001D16100 +:10946400D26902F010020592059A124A50611A4A12 +:1094740093428BD1124B9A6942F480229A619B6980 +:1094840003F480230A930A9B0A4B1864BDE7134A2A +:109494009342ECD10A4B9A6942F400329A619B6977 +:1094A40003F400330993099B024BD863ADE700BF73 +:1094B400002C0140A0020020001400400050014094 +:1094C4000010024000040040003401400008004045 +:1094D40000400140001000400048014000440140A9 +:1094E4001549D1F8883043F47003C1F88830134B20 +:1094F4001A6842F001021A605868114A02405A6020 +:109504001A6822F0847222F480321A601A6822F4F3 +:1095140080221A605A6822F4FE025A60DA6A22F043 +:109524000F02DA62186B074A02401A6300229A603B +:10953400054B8B60704700BF00ED00E00010024057 +:109544000CC07FF8CCFC00FF00800008104A936D2B +:10955400996811F4805F114607D0DA68D20541BFDB +:10956400DA68D2B262F4C072DA609A68900407D5FD +:10957400DA68920541BFDA68D2B262F42072DA6026 +:10958400044B4FF400325A61012381F8753070475F +:10959400A002002000040140431C04D00C3111F946 +:1095A4000C3C5A1C01D100207047984201F10C0177 +:1095B400F5D101207047431C04D091F900305A1CA6 +:1095C40001D118467047984201D1886870470C3120 +:1095D400F3E72DE9F04104468AB080B1B94990F926 +:1095E4008800FEF79DFE94F98950B749064628463F +:1095F400FEF796FE00B12EB9B44803F00FFB0AB093 +:10960400BDE8F081864218BF0020206008B9B04848 +:10961400F3E7B04B984240F0BB8003F55843DA6857 +:1096240042F48042DA60DA6822F48042DA609A69AD +:1096340042F480429A619B6903F480430093009B47 +:10964400002384F87430252384F88A30C5F303108A +:10965400FEF79AFE94F989509B4980462846FFF705 +:10966400AAFF012700FA07F300F0070203F0100332 +:109674001343069305F00F05C0F30113C0F306204E +:1096840007FA05F505A907930990032340460893B3 +:10969400059500F099FA94F98800C0F30310FEF7D9 +:1096A40073FE94F98880874905464046FFF783FF97 +:1096B400430000F0070208F00F08C0F3011003F0A4 +:1096C4001003134307FA08F7079005A928460597DE +:1096D400069300F079FA0C2094F874207E4BA06174 +:1096E4000020261DE0612062A062606294F86D0093 +:1096F40043F822602568A16FE26FD4F88070D4F833 +:1097040084306560A160E2602761636100F0FF0E50 +:1097140008B984F86CE0242084F86D00286820F0EF +:1097240001002860D5F800E06C481A4342F00C02AE +:109734000EEA000002432A606B6823F440533B4363 +:109744006B60AB6823F43063AB60624B9D4240F0C6 +:109754008C8003F55843624A1B6B03F00303D35C0C +:10976400082B3FF64CAF01A252F823F08F98000863 +:1097740079990008B3980008039600087D9A0008B8 +:10978400039600080396000803960008839A0008CD +:10979400544B984217D103F5E6331A6942F4003268 +:1097A4001A611A6922F400321A61DA6942F4003249 +:1097B400DA61DB6903F400330193019B012384F82C +:1097C4007430262340E7484B984217D103F5E4331D +:1097D4001A6942F480221A611A6922F480221A61F9 +:1097E400DA6942F48022DA61DB6903F480230293AC +:1097F400029B022384F87430272325E73B4B9842CD +:1098040017D103F5E2331A6942F400221A611A6986 +:1098140022F400221A61DA6942F40022DA61DB6977 +:1098240003F400230393039B032384F87430342349 +:109834000AE72F4B98427FF409AF03F5E0331A6926 +:1098440042F480121A611A6922F480121A61DA69E8 +:1098540042F48012DA61DB6903F480130493049BFD +:10986400042384F874303523EEE61E4B9D4241D127 +:1098740003F5E6331B6B03F44033B3F5803F00F08C +:10988400FB800CD8002B7FF4BAAE01F049FDA3682D +:1098940000EB5300B0FBF3F080B2E86074E0B3F582 +:1098A400003F00F0EC80B3F5403F7FF4A8AE4B08D6 +:1098B40003F5F40303F59053B3FBF1F39BB2EB60B0 +:1098C40062E000BFCCD9000850DA00085EDB000873 +:1098D4008EDB00080038014028010020F369FFEF07 +:1098E40061D700080044004000480040004C00409C +:1098F400005000406A4B9D4210D103F5E4331B6BCA +:1099040003F44023B3F5802F00F0B680BAD9B3F541 +:10991400002F00F0B480B3F5402FC6E7614B9D42A1 +:1099240010D103F5E2331B6B03F44013B3F5801F2E +:1099340000F0A280A6D9B3F5001F00F0A080B3F513 +:10994400401FB2E7584B9D427FF459AE03F5E03314 +:109954001A6B02F44002B2F5800F00F08D8002D839 +:10996400002A92D04BE6B2F5000F00F08880B2F5E1 +:10997400400F9AE701F0BCFCA36800EB5300B0FB76 +:10998400F3F04A4B80B2D860A26A6368002A48D0D8 +:10999400D70705D55968E06A21F4003101435960BD +:1099A400950705D55968206B21F48031014359602E +:1099B400500705D55968606B21F480210143596033 +:1099C400110705D55968A06B21F400410143596082 +:1099D400D70605D59968E06B21F48051014399605D +:1099E400950605D59968206C21F4005101439960CE +:1099F40050060ED55968606C21F480110143B0F50E +:109A0400801F596005D15968A06C21F4C00101433D +:109A14005960110605D55A68E16C22F400220A4304 +:109A24005A605A6822F490425A609A6822F02A02D4 +:109A34009A601A6842F001021A60002222671C4AE6 +:109A440015681B681A071ED463681B685B070AD570 +:109A54006FF07E432A464FF48001304600F066FFE3 +:109A640000287FF4CCAD202384F86D3084F86E3068 +:109A7400002384F86C30C2E501F06AFC07E74B0868 +:109A840003F5004318E76FF07E432A464FF40011B4 +:109A9400304600F04BFF00287FF4B1ADD4E700BF9F +:109AA40000480040004C0040005000400038014095 +:109AB400D807002038B5002580F8425090F841308E +:109AC400292B04460AD1282380F84130212303633B +:109AD400012101F069FC84F8405038BD90F8413010 +:109AE4002A2BFAD1282380F8413022230363022150 +:109AF400EFE7174B10B52022C362002303634363CF +:109B040080F8412080F842304B07044611D5436A5F +:109B14005A1C42620268526A1A70038D4BB1013BAF +:109B24000385438D013B9BB24385436C43F004039F +:109B340043642046072101F037FC23681022DA61D0 +:109B4400002384F840302046BDE81040FFF792BA65 +:109B54000000FFFF0268436B9169126803B1184764 +:109B6400704790F84130202B0BD10268282380F8ED +:109B74004130054B4363136843F0B803136000207E +:109B840070470220704700BFFB9F00080C4B5A69C6 +:109B9400024213D058610023012803F1010202D0CC +:109BA40040081346F8E71422DBB25343054A181D54 +:109BB4001044816811B11A4413691847704700BFF3 +:109BC40000040140780300202DE9F74FD1F800800C +:109BD400DFF89091614A002338FA03F402D103B00C +:109BE400BDE8F08F4FF0010E0EFA03FE18EA0E06E0 +:109BF40000F0A6804C6824F01007022F16D14FEA1B +:109C0400D30A00EB8A0A03F0070BDAF820504FEA74 +:109C14008B0B4FF00F0C0CFA0BFC25EA0C0C0D69A6 +:109C240005FA0BF545EA0C05CAF820504FEA430A39 +:109C34000325D0F800B005FA0AF5ED4304F0030C4F +:109C44000BEA050B0CFA0AFC013F4CEA0B0C012F42 +:109C5400C0F800C011D8876807EA050BCF6807FA77 +:109C64000AFC4CEA0B078760476827EA0E0EC4F328 +:109C740000179F4047EA0E074760C7683D408F685A +:109C840007FA0AF73D43C560E50059D5D9F81850DD +:109C940045F00105C9F81850D9F8185023F0030706 +:109CA40007F1804705F0010507F58037019503F0BA +:109CB400030C019DBD684FEA8C0C4FF00F0E0EFA99 +:109CC4000CFEB0F1904F25EA0E0E3BD0244DA84275 +:109CD4003AD005F58065A84238D005F58065A842DC +:109CE40036D005F58065A84234D005F58065A842D4 +:109CF40032D005F58065A84214BF0725062505FA6C +:109D04000CF545EA0E05BD601568F74314F4803F71 +:109D14000CBF3D4035431560556814F4003F0CBF3B +:109D24003D4035435560956814F4801F0CBF3D4099 +:109D340035439560D568A40254BF3D403543D56092 +:109D4400013349E70025DAE70125D8E70225D6E7FC +:109D54000325D4E70425D2E70525D0E70004014014 +:109D6400000400480010024010B590F82120022A97 +:109D740003D004228263012010BD0268C46B116801 +:109D8400436B21F00E011160116821F00101116093 +:109D9400026C012101FA02F26260002480F82110B1 +:109DA40080F8204013B19847204610BD184610BDD6 +:109DB4000023354A10B580F84230C2624385436CB3 +:109DC4001943416490F84130282B044607D090F899 +:109DD4004130292B03D090F841302A2B22D1032182 +:109DE400204601F0E1FA282384F84130002323635C +:109DF400264B6363236819681A6811F480411ED0E6 +:109E040022F48042A06B1A60214B4363002384F840 +:109E14004030FFF7A9FF002835D0A06B436BBDE8A5 +:109E240010401847072101F0BFFA94F84130602B25 +:109E34001CBF202384F8413000232363D9E712F4A4 +:109E440000420DD01A68E06B22F400421A60104BF5 +:109E5400436384F84010FFF787FFA0B1E06BDDE7B0 +:109E640094F84130602B05D1202384F8413084F8E4 +:109E7400402010BD94F8513084F840201BB920468E +:109E8400BDE810406DE610BD0000FFFFFB9F000819 +:109E9400A9AD000838B5036820220446DA61082217 +:109EA400DA610D46072101F07FFA23685A6842F40B +:109EB40000425A605A6822F0FF7222F48B3222F474 +:109EC400FF7222F001025A60204601F09BFA2268D8 +:109ED4001368580402D41368190409D594F841305E +:109EE400212B0CBFA36BE36B1B685B689BB2638580 +:109EF400638D9BB21BB1636C43F0040363646B0713 +:109F040010D5636A526A591C61621A70238D4BB171 +:109F1400013B2385638D013B9BB26385636C43F0F6 +:109F2400040363640023236384F842306363636C33 +:109F340063B1616C2046FFF73BFF94F84130282B56 +:109F44001FD129462046BDE83840D2E5E26A12F521 +:109F5400803F0BD00B4AE262202284F8412084F82F +:109F640040302046BDE83840FFF784B894F84120DB +:109F740084F84030222A4FF0200284F8412000D196 +:109F840038BD38BD0000FFFF10B590F84030012BFC +:109F940004462ED0012380F84030C80619D5D306D4 +:109FA40017D594F841302268212B0CBFA36BE36BC7 +:109FB4001B685B68B3FA83F310215B09D1611BB1A1 +:109FC400002084F8400010BD636C43F00403636414 +:109FD400F6E7080705D5130703D523680822DA61D5 +:109FE400EEE78806ECD59306EAD52046FFF752FF44 +:109FF400E6E7022010BD2DE9F84390F84030012B2C +:10A0040004460E46174600F0CB800123CD0680F8A7 +:10A0140040303ED5D0063CD5638D9BB28BBBE36A02 +:10A02400B3F1007F03D0E36AB3F1007F14D194F855 +:10A034004130282B10D131462046FFF75AFDB20695 +:10A0440005D5BB0603D531462046FFF723FF002084 +:10A0540084F84000BDE8F883E36A13F5803F236881 +:10A064000CD094F84120292A08D11022DA61204624 +:10A0740001F0C8F92046FFF71DFDE0E71022DA6180 +:10A08400DDE723681022DA61636C43F00403636440 +:10A09400D5E771071AD57A0718D5638D9BB263B1DA +:10A0A400636A5A1C62622268526A1A70238D013BE9 +:10A0B4002385638D013B9BB26385638D9BB2002B2B +:10A0C400BDD1E36A13F5803FD4D1B8E7330746D551 +:10A0D4003D0744D594F8413003F02803282B236826 +:10A0E40037D19A69E1689D6902294FEA1545C2F39F +:10A0F400004805F0FE059A68D3F80C9020D1C2F30D +:10A10400090285EAD21515F006050FD1A16C0131BB +:10A11400A164A16C022992D10821A564D96184F8B3 +:10A12400405041462046FEF7BBFF88E70421204605 +:10A1340001F03AF9002384F8403009F0FE02F0E718 +:10A144000421204601F030F9002384F840302A46E7 +:10A15400E7E70822DA61002384F840306FE7B007AC +:10A164007FF56DAFB9077FF56AAF638D9BB26BB1B5 +:10A17400636A2268591C61621B789362638D013B98 +:10A184009BB26385238D013B238558E7E36AB3F1D2 +:10A19400807F3FF46FAFE36A002B3FF46BAF4EE771 +:10A1A4000220BDE8F88370B590F84030012B0446D6 +:10A1B40000F0A480012380F8403011F010001DD07D +:10A1C400D3061BD522684E491023D361636C43F038 +:10A1D40004036364636B8B420ED04A498B420CBF09 +:10A1E4006023F42311680B431360204601F00AF93D +:10A1F400002084F8400070BD6023F3E70E063ED5CE +:10A2040055063CD523681A6822F040021A60628D14 +:10A2140092B2002A2ED05A68618D89B2FF2999BF63 +:10A22400618D2185FF2121855968208D21F07F7161 +:10A2340021F45831C2F30902C6B221F47F7142EA13 +:10A24400064221F003018CBF4FF080754FF000757A +:10A254000A432A435A60628D121A92B2628594F8B4 +:10A264004120222A1A680CBF42F4004242F4804280 +:10A274001A60BDE740212046FFF79AFDB8E789063A +:10A28400B6D59306B4D523682022DA615A6822F041 +:10A29400FF7222F48B3222F4FF7222F001025A6020 +:10A2A400002222636263A2F58032E26228B11022A6 +:10A2B400DA61636C43F004036364204601F0A2F89E +:10A2C4000321204601F070F8636C1BB994F8412017 +:10A2D400602A01D1616CCEE794F84120212A07D18C +:10A2E400202284F8412094F8422084F842307FE709 +:10A2F40094F84120222A7FF47BAFF1E7022070BD5D +:10A30400ABA100088D9F0008036899691A6810B40E +:10A31400CC0508D5140606D5446C44F00104446405 +:10A324004FF48074DC614C0508D5140606D5446CE2 +:10A3340044F0080444644FF48064DC61890508D562 +:10A34400120606D5426C42F0020242644FF40072D7 +:10A35400DA61436C13F00B0F03D0416C5DF8044BCE +:10A3640026E55DF8044B7047174B30B5DC68C4F341 +:10A374000224C4F10703251D042B28BF0423062D42 +:10A384004FF0010505FA03F303F1FF338CBF033CDF +:10A3940000241940A14005FA04F4013C224000289D +:10A3A40042EA01024FEA0212ADBF00F1604000F040 +:10A3B4000F00064B00F56140B5BFD2B2D2B21A54B9 +:10A3C40080F8002330BD00BF00ED00E014ED00E094 +:10A3D400002808DB4209012300F01F0003FA00F003 +:10A3E400014B43F82200704700E100E0044B4FF4B6 +:10A3F4000012C3F88421034B186900F0ECBC00BFC1 +:10A4040000E100E028010020044B4FF48012C3F85F +:10A414008421034BD86800F0DEBC00BF00E100E0FB +:10A4240028010020044B8022C3F88421034B986840 +:10A4340008B100F0D0BC704700E100E02801002022 +:10A44400034B4022C3F88421024B586800F0C3BC7C +:10A4540000E100E028010020034B2022C3F88421FE +:10A46400024B186800F0B7BC00E100E028010020AE +:10A474000A4AD3695B684FF6FE71D962D96841F024 +:10A484000201D960D96821F00101D9604FF6FF714A +:10A494009963002382F82830536270471803002020 +:10A4A4002DE9F041A44CE36903204FF6FF75022621 +:10A4B400002794F82820092A00F2C881DFE812F066 +:10A4C4000A00A1001900EE003E014E016201920152 +:10A4D400BA01A401E36993F88C30002B00F0898061 +:10A4E4000023636384F85C30FF2263620123A26566 +:10A4F4006365E26992F88C30002B66D1626D1AB103 +:10A50400A36DB3FA83F35B090022626294F85C20C2 +:10A51400002A36D1656B072D22DD002B51D0E2785D +:10A52400207940EA022094F90520002A10DA18B1B3 +:10A53400FEF7AAF8C343DB0F94F8BC2452070CD4EB +:10A54400A56194F8BC349B071BD5CC23636218E047 +:10A55400236AC11A4B424B41EEE793B1A561F4E77C +:10A56400012D2ED194F83830A363CC2B03D0E26AAA +:10A57400236B9A4205DD226C1AB1E36A002B00F3C7 +:10A584006881E369606A5B6841F2E83140F2314214 +:10A59400002808BF0A46DA626FF001021A61DA681D +:10A5A40042F00102DA606FF002021A61DA6842F0E6 +:10A5B4000202DA60002384F85C30032384F8283034 +:10A5C40017E00023A3610C23C0E7536840F2424222 +:10A5D400DA626FF0010100225A621961D96841F010 +:10A5E4000101D960012384F82830A263E264A364E2 +:10A5F4002365E369002283F88C2083F88D20BDE86D +:10A60400F081E56995F88DE0BEF1000F38D16B68F3 +:10A614005F6BE36C03F14402BA4218DA6833A16C4D +:10A62400A06B226DE364002603F1440847459C4671 +:10A6340030DDB1F5807F03F1680324DD26B1C4F871 +:10A644004CC0A164A0632265A36C5B00A364BEF14B +:10A65400000FCED0636D226D1340626B6365172AC1 +:10A6640006DC531CA16B6363A36DA1544B40A3652B +:10A67400022384F828306B684FF4D072DA62B8E7AA +:10A6840040F24247C5E7B2FA82F208435209490050 +:10A694000126C9E7002ED7D0E364D2E793F88C20D3 +:10A6A40012B184F8287004E7636AC3B1E2636822D4 +:10A6B400E3695B689A632332DA626FF001021A611C +:10A6C400DA6842F00102DA606FF002021A61DA68B5 +:10A6D40042F00202DA600023636304236EE7E16A56 +:10A6E400226B914206DD226C1370626C22646364F7 +:10A6F400E362E363236C002B00F0A8801A78C2F3B2 +:10A704008101682201FB0222597901F00F0108310D +:10A71400E163E16A0129CBD1197801F0DF011970F5 +:10A72400E36B206C013BC15C81F02001C154E36AFE +:10A734000133E362BCE700BF1803002093F88C20C8 +:10A7440062B15A68D2F824E0916B0A398E4503D27B +:10A75400956384F82860ACE605232FE7636AF3B1B8 +:10A76400636AA363A36B002101221A424FEA4202E7 +:10A774001CBF83F480730121FF2AF6DD01B1A363BA +:10A784000123A364A06BA26C682310424FEA420128 +:10A794000BD0B2F5807F08DC0A466833F5E7636BBB +:10A7A400226C591C6163D35CDBE7682B0CBF062267 +:10A7B4000722B1F5007FA16484F8282054DD616B81 +:10A7C400E26B914203F59C7302DA626A002A48D074 +:10A7D400092247E0D16821F00201D1606FF0020143 +:10A7E40011614EE0E3695B68596B9A6B453A91429B +:10A7F40008D24FF6FF729A63012384F8283084F854 +:10A804005C30F6E60623D9E6E3695B6841F2504220 +:10A81400DA626FF002021A61DA6842F00202DA6068 +:10A82400636A1BB1002363620823C7E6E36A01334A +:10A83400E362F9E793F88C202AB15A68526BB2F5B7 +:10A84400936F7FF6D6AE84F8280032E6FFF710FE49 +:10A85400CFE600231370626C22646364E362E363F3 +:10A864008FE6052284F8282094F82820072AE26934 +:10A874005268AFD16FF002011161D16841F0020159 +:10A88400D16094F82820092A0BBFE269E169526873 +:10A89400496813BFA3F123024FF6FF7191638A63E2 +:10A8A400E2695268D362A4E690F86A30202B3BD167 +:10A8B400002937D090F86830012B35D0012380F877 +:10A8C4006830A0F85830A0F85A3083684165B3F571 +:10A8D400805F19D10369ABB940F2FF13A0F85C3073 +:10A8E40000232222C36680F86A20026880F8683058 +:10A8F400916841F001019160116841F49071116017 +:10A9040018467047FF23E9E723B90369002BF9D000 +:10A914007F23E3E7B3F1805FE2D10369002BF7D033 +:10A924003F23DBE7012070470220704770B50468BD +:10A93400104EE56931EA050501D1002070BD5D1CAA +:10A94400F7D08BB9236823F4D0732360A36823F072 +:10A954000103A360202380F8693080F86A30002363 +:10A9640080F86830032070BDD6F8C054AD1AAB42ED +:10A97400DFD2E7E71803002010B507490446042096 +:10A98400FDF78DFD30B14FF0904310220CB19A6168 +:10A9940010BD9A6210BD00BFDC0700202DE9F04114 +:10A9A4003B2890B056D8A84C204490F93850681CE5 +:10A9B40050D0A6492846FDF772FD4FEA151805F058 +:10A9C4000F0798B304F174012846FEF7E5FD00284B +:10A9D40043D004F174012846FDF7A2FC0446C8B133 +:10A9E4002846FDF7B1FC30F0100213D12368012191 +:10A9F40001FA00F023EA0000206002238DF804101D +:10AA040093498DF80430CB6923F00053CB61049251 +:10AA14008DF804208D4908F00F00012303FA07F292 +:10AA240051F8203023EA020341F8203008F00F00E7 +:10AA3400FDF7AAFC012303FA07F7032201936946F1 +:10AA44000023009703920293FFF7BEF8814928463A +:10AA5400FDF718FD10B0BDE8F08104F19801284617 +:10AA6400FEF79AFD0028D5D004F198012846FDF799 +:10AA740057FC04460028CDD02846FDF773FC142863 +:10AA8400064600F2DC80744BC340D907C2D5734933 +:10AA94002846FEF790FDC202236A4CD5042202FA2E +:10AAA40006F623EA06062662236A2362226A41F234 +:10AAB40011131A4208D1226A40F244431A4202BFD7 +:10AAC400636C23F400436364226A41F211131A4253 +:10AAD40008D1226A40F244431A4203D1236823F086 +:10AAE4000103236002238DF83D30226A41F21113E1 +:10AAF4001A4208D1226A40F244431A4202BF236830 +:10AB040023F001032360564B9C4253D1504A93696E +:10AB140023F400639361534B9C425CD14C4AD36948 +:10AB240023F02003D361504B9C425CD1484A936983 +:10AB340023F4801356E0012202FA06F623EA0606FD +:10AB44002662236A2362464B9C4213D003F50063BA +:10AB54009C420FD003F540639C420BD003F5806305 +:10AB64009C4207D003F580639C4203D003F5006345 +:10AB74009C420DD1226A41F211131A4208D1226A71 +:10AB840040F244431A4202BF636C23F400436364FB +:10AB9400226A41F211131A4208D1226A40F2444354 +:10ABA4001A4202BF236823F00103236001238DF8B6 +:10ABB4003D3097E7B4F1804F05D1254AD36923F09E +:10ABC4000103D361A7E7294B9C4210D1204AD369E2 +:10ABD40023F00203D361264B9C4210D11C4A936993 +:10ABE40023F40053936100238DF83D3012E7214B89 +:10ABF4009C420CD1164AD36923F00403D3611E4B43 +:10AC04009C4214D1124A936923F48033EAE71B4B24 +:10AC14009C4280D10E4AD36923F01003D361184BB0 +:10AC24009C4280D10A4A936923F48023DAE7154BC6 +:10AC34009C42F4D1064A936923F40033D2E73C28BA +:10AC44007FF4E8AE23E700BF68D70008FC070020C4 +:10AC540000100240DC0700201111110000D8000888 +:10AC6400002C01400014004000500140000400404A +:10AC74000034014000080040004001400010004042 +:10AC84000048014000440140184B1A6A12124270F5 +:10AC94001A6A827030B50022FF24551C92B28A428F +:10ACA40003DA825C54402A46F7E744545A6C002A7B +:10ACB400FDD11A6CC2B9186472B693F8282002F058 +:10ACC400FF017AB90322D96283F82820DB695B6823 +:10ACD4000122DA626FF001021A61DA6842F00102BD +:10ACE400DA60596262B630BD5864E5E718030020A3 +:10ACF40008B5044B2BB10448FEF7B5F808B1AFF31F +:10AD0400008008BD00000000B804002010B590F8D1 +:10AD14006A30222B036833D1998C8368B0F85C20A5 +:10AD2400B3F5805F02EA0102436D0DD1016959B99F +:10AD340023F8022B4365B0F85A40013CA4B2A0F8B2 +:10AD44005A402CB1002010BD591C41651A70F2E71D +:10AD540003681A6822F490721A609A6822F0010259 +:10AD64009A60202380F86A30FEF761F90428E9D854 +:10AD7400064A074B53F8203052F820009847E1E781 +:10AD84009A6942F008029A61022010BD5001002025 +:10AD94003C010020406A0023A0F85A30A0F8523049 +:10ADA400FEF730B9406A0268536843F400435360C5 +:10ADB400826B00235363C26B536390F84130602B62 +:10ADC40003D1202380F84130704790F851300BB9FB +:10ADD400FEF7C7BE70470368DA69196870B512F0E8 +:10ADE4000F05044606D1960604D58E0602D5BDE8A5 +:10ADF40070408BE79868002D68D010F0010502D1EF +:10AE040011F4907F62D0D60707D5C80505D5012077 +:10AE14001862E06E40F00100E066960706D52DB199 +:10AE240002201862E06E40F00400E066500706D588 +:10AE34002DB104201862E06E40F00200E0661607AF +:10AE440007D5880600D425B108201862E36E0343B1 +:10AE5400E366E36E002B00F0A880920604D58B060F +:10AE640002D52046FFF752FFE36E13F0080F236864 +:10AE740003D19D6815F0400523D01A6822F490721E +:10AE84001A609A6822F001029A60202284F86A20EB +:10AE94009A68560610D59A68606E22F040029A604D +:10AEA40050B1424B4363FEF75FFF00287DD0606ED4 +:10AEB400BDE87040436B18472046BDE87040FEF77C +:10AEC400A1B82046FEF79EF8E56670BDD50215D5FB +:10AED400400213D54FF480121A62202384F869309B +:10AEE400204684F86A30FEF7A2F8314B53F820105C +:10AEF40020467531BDE87040FFF7D6BC16062BD549 +:10AF04000D0629D594F86920212A4ED1B4F852208F +:10AF140092B242B91A6822F080021A601A6842F0AA +:10AF240040021A6070BDA268B2F5805FE26C0ED177 +:10AF3400216961B932F8021BC1F308011985E26481 +:10AF4400B4F85230013B9BB2A4F8523070BD511C8E +:10AF5400E16412781A85F3E7500626D54A0624D50B +:10AF64001A6822F040021A60202384F869302046CF +:10AF7400FEF75DF8042818D80E4B53F820400E4B0A +:10AF840053F820302046984701300ED0D4F894303E +:10AF9400B4F89A1094F8742013FA81F1074BBDE8C1 +:10AFA400704053F8220000F04DBA70BD99AD00080E +:10AFB400500100208C02002078020020280100208B +:10AFC400036A23F010030362036A70B5446882695C +:10AFD4000D6822F0807222F4E64242EA05228D686E +:10AFE40023F0200343EA05131A4DA84207D005F5C0 +:10AFF4000065A84203D005F5E055A8420DD1CD68FF +:10B0040023F0800343EA0513124DA84223F04003C2 +:10B0140012D005F50065A8420ED00F4DA8420BD002 +:10B0240005F58065A84207D005F58065A84203D0E0 +:10B0340005F50065A84206D124F440658E694C6983 +:10B04400344345EA8404446082614A68826303624B +:10B0540070BD00BF002C014000400140324A03682B +:10B06400904212D0B0F1804F0FD0A2F594329042AA +:10B074000BD002F58062904207D002F596329042DE +:10B0840003D002F5E052904215D14A6823F07003D0 +:10B094001343254A90421DD0B0F1804F1AD0A2F537 +:10B0A4009432904216D002F58062904212D002F59A +:10B0B400963290420ED01D4A90420BD002F5806227 +:10B0C400904207D002F58062904203D002F50062FC +:10B0D400904203D1CA6823F4407313434A6923F0AE +:10B0E4008003134303608B68C3620B6883620E4B57 +:10B0F400984213D003F5006398420FD003F54063E0 +:10B1040098420BD003F58063984207D003F580631F +:10B11400984203D003F50063984201D10B6903639D +:10B1240001234361704700BF002C014000400140EF +:10B1340003681A69910710B504460ED5DA689207B8 +:10B144000BD56FF002021A61012202779B699B07FB +:10B1540075D0FDF729FE0023237723681A69500769 +:10B1640010D5DA6851070DD56FF004021A61022276 +:10B1740022779B6913F4407F204663D0FDF714FEC9 +:10B184000023237723681A6912070FD5DA6810079A +:10B194000CD56FF008021A6104222277DB69990743 +:10B1A400204652D0FDF700FE0023237723681A6956 +:10B1B400D20610D5DA68D0060DD56FF010021A61E8 +:10B1C40008222277DB6913F4407F204640D0FDF744 +:10B1D400EBFD0023237723681A69D10709D5DA68C0 +:10B1E400D20706D56FF001021A61636C201F03B108 +:10B1F400984723681A69140605D5DA68100644BF0F +:10B204006FF080021A611A69D10505D5DA68120651 +:10B2140044BF6FF480721A611A69540605D5DA685E +:10B22400500644BF6FF040021A611A69910605D5B1 +:10B23400DA68920644BF6FF020021A6110BDFEF76F +:10B2440029F888E7FEF726F89AE7FEF723F8ABE734 +:10B25400FEF720F8BDE70368012202FA01F11A6A39 +:10B2640022EA01021A621A6A1143114A19629342CC +:10B2740013D002F5006293420FD002F5406293426C +:10B284000BD002F58062934207D002F580629342AC +:10B2940003D002F50062934203D15A6C42F4004297 +:10B2A4005A641A6842F001021A600020704700BF15 +:10B2B400002C01400F2303600B4B5A6802F0030279 +:10B2C40042605A6802F0F00282605A6802F4E06256 +:10B2D400C2605B68DB0803F4E0630361034B1B6833 +:10B2E40003F007030B607047001002400020024087 +:10B2F400084B4FF460525B6892FAA2F2B2FA82F2FF +:10B3040003F46053D340044AD35C044A1068D84021 +:10B31400704700BF00100240D4DA0008A4000020E7 +:10B32400084B4FF4E0625B6892FAA2F2B2FA82F23E +:10B3340003F4E063D340044AD35C044A1068D84061 +:10B34400704700BF00100240D4DA0008A4000020B7 +:10B35400124B5A6802F00C01082910B51BD14FF4A6 +:10B36400701191FAA1F10F20B1FA81F1DB6A90FA20 +:10B37400A0F00B4CB0FA80F003F00F0302F470124B +:10B38400C340CA40234422441B7A0648B0FBF3F36B +:10B39400107E584310BD034810BD00BF001002408A +:10B3A400D4DA000800127A0011F0010307D090F8F3 +:10B3B400413003F02803282B0CBF4223F2238A07D1 +:10B3C40009D590F8412002F02802282A0CBF43F046 +:10B3D400440343F0F4034A0701F0110248BF43F069 +:10B3E400B80301F01201112A08BF43F09003122997 +:10B3F40001680A6808BF43F0600322EA03030B6094 +:10B404000020704703689A69910744BF00229A623A +:10B414009A69D2075EBF9A6942F001029A61704745 +:10B42400074938B5A1F5A075A1F11404E3681039F2 +:10B4340013B1032208469847AC422146F4D138BDE3 +:10B44400B804002090F86930202B1AD1B9B190F8D3 +:10B454006830012B15D00123A0F85230C164A0F844 +:10B464005030016800232122C36680F8683080F8D8 +:10B4740069200A6842F080020A6018467047012079 +:10B4840070470220704738B506244FF48065A8B28F +:10B49400FEF77CFB013C4FEA4505F8D138BD38B5D1 +:10B4A40005242025A8B2FEF771FB013C4FEA4505AF +:10B4B400F8D138BD1020FEF769BB0820FEF766BB43 +:10B4C4000220FEF763BB0120FEF760BB01F066BEFD +:10B4D40001F064BE01F062BE01F058BE01F056BE38 +:10B4E40000000000000000000000000001F0FF0167 +:10B4F400102A2BDB10F0070F08D010F8013B013A9B +:10B504008B422DD010F0070F42B3F6D1F0B441EACC +:10B51400012141EA014122F007047FF000070023E2 +:10B52400F0E80256083C85EA010586EA010685FA38 +:10B5340047F5A3FA87F586FA47F6A5FA87F68EB992 +:10B54400EED1F0BC01F0FF0102F0070232B110F8B5 +:10B55400013B013A83EA010313B1F8D1002070479B +:10B5640001387047002D06BF35460338073815F0FB +:10B57400010F07D1013015F4807F02BF013015F4AB +:10B58400C03F0130F0BC0138704700BF034613F8D8 +:10B59400012B002AFBD1181A013870472DE9804885 +:10B5A400C34CC44EA7B00D20FFF7F8F904F194077B +:10B5B4004FF4E1330025C4F80C313846062384F8EF +:10B5C4003031C4F81851C4F81451C4F81051FEF7BE +:10B5D40000F894F8083156F8231091F8692091F88E +:10B5E4006A100A4302F02202222A16D006EB8303D1 +:10B5F400B14A5A612946012294F91E019F62FEF75D +:10B60400B3FE94F91E01FEF7E3FE94F8083104F248 +:10B61400091156F82300FFF747F9A8490420FDF75C +:10B62400BBFF0546002835D0A5490420FCF737FFA9 +:10B6340020BBA3490420FCF725FF01271422002185 +:10B644000FA801F00DF89D490420FCF769FE04469B +:10B65400002800F085830420FCF776FE05460028C8 +:10B6640000F00C83102840F07B83002F40F0088307 +:10B67400002303930394039B143351E30027DDE772 +:10B6840003F47013B3F5701F40F06783636843F0ED +:10B69400020361E38B490420FDF77EFF0446002882 +:10B6A40000F07D8286490420FCF7F9FE20B9844924 +:10B6B4000420FCF7E7FE01254022002116A800F033 +:10B6C400CFFF1C2200210FA800F0CAFF7D490420EF +:10B6D400FCF726FE04461690002800F0418314224D +:10B6E40000210AA800F0BCFF26A9002341F8743DFC +:10B6F4000AA8FFF7DFFD671C00F0C680724B9C426E +:10B7040000F0B58200F2AE80A3F592339C4209D0DA +:10B7140000F2A180B4F1804F04D0A3F580639C4271 +:10B7240040F0AF800D9CFFF7FBFDB4F5A06F04D093 +:10B7340000F2B680B4F5806F00D14000634BB0FBDB +:10B74400F3F3013B1793FE23199300231A931893E1 +:10B754001B938DF895308DB35D4C9DF89530042086 +:10B76400207003F0FF08FBB956498DF89480FDF76B +:10B7740022FF074616A8FDF7E7FD94F90000C0F381 +:10B784000310FCF701FE02230B9303230D9323788C +:10B79400CDF8308003F00F03C7F306279D400AA9B4 +:10B7A4000E970A95FEF710FA022317A916988DF83A +:10B7B4009530FFF753FC01238DF895300420FCF7F6 +:10B7C400D1FD1428044600F2DD84424BC340DD075A +:10B7D40040F1C68260230F930023119313931293B5 +:10B7E400159314936C2310939DF89430012B00F05F +:10B7F400B78201238DF89430082C4FF002038DF8A2 +:10B80400953000F0158171D8002C00F0AD80042C27 +:10B8140000F0FC8001238DF895302A490023042090 +:10B824008DF89430FDF7C7FEC00240F1B381169B3A +:10B83400042202FA04F41A6A22EA04021A621A6A54 +:10B8440014431C625A6C42F400425A641A6842F06F +:10B8540001021A6084E2204B9C423FF463AF03F57B +:10B8640080635CE71D4B9C4200F0018211D8A3F574 +:10B8740080539C4200F0FB8103F540639C4200F03E +:10B88400F681174801F0CAF9164801F0C7F90020FB +:10B8940054E7154B9C4200F0EA8103F50063EDE7A1 +:10B8A400B4F5C06F3FF449AFB4F5E06F44E700BFAF +:10B8B400B8040020280100200D8F0008DCD7000800 +:10B8C400FC07002000D80008002C014018E4030005 +:10B8D4000400002011111100001000400044014038 +:10B8E400FCDA00082CDB000800480140102C00F0B2 +:10B8F400E280142C00F018810C2C8BD1169BB54DD2 +:10B904001A6A22F480521A62196A5868DA6922F0B3 +:10B91400807222F4E642AB4242F4C04221F4005168 +:10B9240013D005F50065AB420FD005F54065AB4279 +:10B934000BD005F58065AB4207D005F58065AB42B9 +:10B9440003D005F50065AB4201D120F48040586076 +:10B95400DA616C221A641962DA6942F40062DA610B +:10B96400DA6922F48062A2E0169B9A4F1A6A22F0E6 +:10B9740001021A621D6A59689A6922F4803222F01F +:10B984007302BB4242F0600225F0020013D007F5B7 +:10B994000067BB420FD007F54067BB420BD007F5E9 +:10B9A4008067BB4207D007F58067BB4203D007F529 +:10B9B4000067BB421AD125F00E00864DAB4213D06E +:10B9C40005F50065AB420FD005F54065AB420BD0E1 +:10B9D40005F58065AB4207D005F58065AB4203D021 +:10B9E40005F50065AB4201D121F4407159609A61BB +:10B9F4006C225A6318629A6942F008029A619A6941 +:10BA040022F004029A619A699A6103E70FA91698D1 +:10BA1400FFF7D6FA169B13999A6942F400629A6169 +:10BA24009A6922F480629A619A6942EA0122EBE7F8 +:10BA3400169B68491A6A22F480721A621F6A58684F +:10BA4400DA6922F4803222F073028B4242F06002FF +:10BA540027F4007507D0DFF880E1734503D00EF5B5 +:10BA6400E05E734506D18B4227F4606511D05A49D4 +:10BA74008B420ED059498B420BD001F580618B4229 +:10BA840007D001F580618B4203D001F500618B4240 +:10BA940001D120F440505860DA616C22DA631D62EF +:10BAA400DA6942F00802DA61DA6922F00402DA6142 +:10BAB400DA69DA61AEE6169B464D1A6A22F48032E0 +:10BAC4001A62196A58685A6D22F4803222F07002A0 +:10BAD400AB4242F0600221F4003113D005F5006559 +:10BAE400AB420FD005F54065AB420BD005F5806540 +:10BAF400AB4207D005F58065AB4203D005F5006580 +:10BB0400AB4201D120F4803058605A656C229A65AA +:10BB140019625A6D42F008025A655A6D22F0040205 +:10BB24005A655A6D5A6575E6169B2A4D1A6A22F4AF +:10BB340080121A62196A58685A6D22F0807222F4CF +:10BB4400E042AB4242F4C04221F4001113D005F5A7 +:10BB54000065AB420FD005F54065AB420BD005F54F +:10BB64008065AB4207D005F58065AB4203D005F58F +:10BB74000065AB4201D120F4802058605A656C22E4 +:10BB8400DA6519625A6D42F400625A655A6D22F4FC +:10BB94008062C5E7214616A8FFF75DFBE0E0302090 +:10BBA400FEF7FCFE2046FEF7E7FED9E0FEE794F838 +:10BBB4008910012940F0A781196841F00101196039 +:10BBC400022384F889309EE1FFF7AAFB4000C2E11A +:10BBD400FEE700BF002C014000340140004001405A +:10BBE4000A223946ACE249462046FDF7BAF9002359 +:10BBF400AB618021BC22112300274FF0010B4FF0D1 +:10BC0400E10A8DF85F1016A808218DF858200192DA +:10BC14008DF8593000938DF85A808DF85B708DF84B +:10BC24005CB08DF85DA08DF85E70FFF72DF8642090 +:10BC3400FCF740FC009B019A8DF8593008218123C0 +:10BC440016A88DF858208DF85F308DF85A808DF83D +:10BC54005B708DF85CB08DF85DA08DF85E70FFF7B9 +:10BC640013F86420FCF726FCFFF742F85AE20E9C16 +:10BC7400FFF73EFB59E5039503944FE0002FFAD0FC +:10BC84000223BD4F8DF84030FB69DFF81C9323F08D +:10BC94000053FB61002313938DF84030042389F88B +:10BCA40000309DF8403003F0FF08CBB94046FCF764 +:10BCB4006BFBFB6943F00053FB61FB69CDF86080CB +:10BCC40003F000530293029B99F8003003F00F0233 +:10BCD40001239340169316A903231793FDF774FFCA +:10BCE4000223002101208DF8403013918DF840008B +:10BCF4008DF84030236840F6FE72AA4023EA02031E +:10BD040023602368C022AA4023EA020323608DF83B +:10BD1400400003910394002D7FF4ADAC039B0833E2 +:10BD24000393039B4FF4D8621A609DF84130012BB2 +:10BD340016D002238DF8403022680123AB40134310 +:10BD440023602368002D7FF49BAC03F03C033C2B61 +:10BD540003D1636843F00103636001238DF840302D +:10BD640086498748874CDFF84492DFF84482824F43 +:10BD7400FDF7F7F884498248FDF7F3F84FF47A7039 +:10BD8400FCF798FB814B824A9A642046FCF7A2FA9E +:10BD94004FF0FF32226486F8492096F8531096F843 +:10BDA40054207C482E23002589F8003142EA0122E0 +:10BDB400012389F862504FF0020A89F80131C8F86A +:10BDC40020200422C8F81850257188F8285084F8D7 +:10BDD4002C506563A562C8F8245086F8485086F84C +:10BDE4003F5086F8415086F8423086F84330C6F812 +:10BDF4003C5186F840A0C8F830207A6984F83C5059 +:10BE040042F4003284F82430C8F84050D8F81C407A +:10BE1400C8F8445088F85C507A617A6902F40032B8 +:10BE24000792079A7A6942F480227A617A690A93BE +:10BE340002F48022032306920AA9069A0B930C9516 +:10BE4400FDF7C2FE08230AA952480C950D950E93DE +:10BE5400CDF828A0CDF82CA0FDF7B6FE40226266EE +:10BE64007F22E26694F889204B4BA56612F0100FEE +:10BE74002567A567C4F884503A4623664F4645466D +:10BE84007FF494AE916941F001019161916901F0EF +:10BE940001010991099994F8891001F0FF0009B989 +:10BEA40084F8880000210891196808910899606F46 +:10BEB40021F0700141F040010891089921F002013C +:10BEC4000891089921F440210891089921F47051AE +:10BED4000891089921F400410891089901430891B7 +:10BEE4000899E06F014308910899D4F88000014350 +:10BEF400089108991960012184F8891094F889102F +:10BF0400C9067FF554AEA046244B48F8043BD369D8 +:10BF140043F00203D361D36903F002030593059B45 +:10BF2400C02300220A930AA902234FF090400B93E6 +:10BF34000C920D920E93FDF747FE194B4FF00052F1 +:10BF44001A6004A90FA8FFF7B5F9129B002B7FF420 +:10BF54003BAEFFF7E5F923686360124BB0FBF3F0E7 +:10BF64000023E3606361A36194F8413001384FF624 +:10BF7400FF72A0601EE000BF0010024018DB000842 +:10BF8400B8040020EC0500201EDB00084406002055 +:10BF94000D8300080004004828000140000400400C +:10BFA40000E100E040420F0004000020D40600201D +:10BFB40018030020226103F0FF0993B9404684F876 +:10BFC4004090FDF7C1F96068FDF76DF94A460F210D +:10BFD40040B2FEF7C9F96068FDF765F940B2FEF7B3 +:10BFE400F7F94FF0020984F8419008F10401606800 +:10BFF400FFF734F8012384F8413094F84030012BE2 +:10C004003FF4E6AD606884F84190036A604923F028 +:10C014000103036288428369026A16D0B0F1804F3B +:10C0240013D0A1F5943188420FD001F58061884284 +:10C034000BD001F59631884207D001F54061884262 +:10C0440003D001F58051884201D123F0030343F06A +:10C05400010323F0F00322F00A0242F0020243F04B +:10C06400400383610262836923F00C038361836963 +:10C0740083614FF00109602384F841904FF0020A74 +:10C0840016934FF0000B4FF6FF731793CDF860A093 +:10C09400CDF868B016A984F8409084F841A0FEF762 +:10C0A4008FFF63689A6942F400629A619A6922F484 +:10C0B40080629A6199691A9A41EA02229A6184F823 +:10C0C400419084F840B09A6922F400629A6184F83D +:10C0D40041A01A6842EA09021A6084F84190DA68B9 +:10C0E40042EA0A02DA601A6A22F001021A621A6A41 +:10C0F40042EA09021A621A6842EA09021A60042131 +:10C104004046FFF7A8F897F800315B0705D597F884 +:10C11400003183F0840387F800311E4CDFF87C90F3 +:10C124004FF00A08D6F840717B1C002FC6F8403146 +:10C13400BFF656AD2D212046FCF79DFE0A2279421A +:10C144002046FCF71EFF49462046FCF70AFFAB6970 +:10C15400002B3FF44EAD1049DFF844A02046FCF715 +:10C1640000FF0027AB69BB427FF73DAD1FB15146CD +:10C174002046FCF7F6FEE95D10222046FCF701FF9D +:10C184000137EFE73C287FF4EBADFFF723BB00BF9B +:10C19400002C0140B804002021DB00081EDB00084D +:10C1A400B4DB00089A499B48CA689B4B70B54FF6AC +:10C1B400FF0422400243CA601A6842F010021A6067 +:10C1C400CB6823400343954CCB6000F0C3F963680C +:10C1D40013F00C0F0AD0636803F00C03082B69D129 +:10C1E400636803F4C033B3F5004F63D1022393FAB9 +:10C1F400A3F2226893FAA3F32368F82292FAA2F234 +:10C204001021B2FA82F223F0F80301FA02F2134386 +:10C214002360636803F00C03082B6AD04FF080732B +:10C2240093FAA3F3B3FA83F303F1845303F584136A +:10C234009B0000221A607A4A16684FF000700121B0 +:10C2440090FAA0F3256890FAA0F3B3FA83F303F00D +:10C254001F0301FA03F32B4240F08580E36A23F0C5 +:10C264000F03E362636823F4761343F4E413636017 +:10C274004FF0807393FAA3F3B3FA83F303F1845377 +:10C2840003F584139B000125196016684FF00071B3 +:10C2940091FAA1F3206891FAA1F3B3FA83F303F0BE +:10C2A4001F0305FA03F3034223D113689B1B022BDC +:10C2B400EED91EE0012292FAA2F3B3FA83F303F15A +:10C2C400845303F584139B0055491A600E680220B9 +:10C2D40090FAA0F3256890FAA0F3B3FA83F303F07D +:10C2E4001F0302FA03F32B4286D10B689B1B022B1C +:10C2F400EED9494A136803F00703012B39D963685F +:10C3040023F0F00363604FF0007393FAA3F2216803 +:10C3140093FAA3F3B3FA83F303F01F03012202FA9F +:10C3240003F30B4231D13F4B4FF47A721B68B3FBDA +:10C33400F2F3013BB3F1807F09D23B4A5360344BA3 +:10C34400F02183F823100023936007231360364AF7 +:10C35400136843F004031360002211464FF0FF30CA +:10C36400FEF702F870BD13689B1B022B7FF668AFC3 +:10C37400BFE7136823F0070343F002031360136855 +:10C3840003F00703022BCED1B9E7636823F003035C +:10C3940043F002036360224B196841F288306268FB +:10C3A40002F00C02082A22D11B4A136803F0070387 +:10C3B400022B21D8636823F4E06343F48063636051 +:10C3C400636823F460536360FEF7C2FF6368F0227E +:10C3D40092FAA2F2B2FA82F203F0F003D340134AC3 +:10C3E400D35CD8400F4B186000F0B4F89BE71A6890 +:10C3F400521A8242D3D996E7136823F0070343F015 +:10C4040002031360136803F00703022B8BD1D1E7F7 +:10C4140000ED00E00003FA05002002400010024095 +:10C42400D8070020A400002010E000E051D7000845 +:10C4340070B5444C444A4549454E062384F860305F +:10C44400072384F87430082384F88830092384F897 +:10C454009C300A23002584F8B030172384F8C430B4 +:10C4640084F8D83084F8EC3084F8003184F814313E +:10C474002846282384F8283184F83C3184F8503144 +:10C4840084F8643184F8783184F88C31E566C4F832 +:10C494008050C4F89450C4F8A850C4F8BC50C4F8F0 +:10C4A400D050C4F8E450C4F8F850C4F80C51C4F89F +:10C4B4002051C4F83451C4F84851C4F85C51C4F84C +:10C4C4007051C4F88451C4F8985100F092F84FF4B4 +:10C4D4007A73C4F8A83106F10803C4F8A031032321 +:10C4E40084F8BD32022384F8BC3204F2B113C4F8D8 +:10C4F400C032C4F8A45104F2F1135822294604F5B9 +:10C504003570A4F8C452C4F8C832A4F8C652A4F8CA +:10C51400CC52A4F8CE5200F0A3F80123C4F8F432AC +:10C52400283604F54B7384F8285084F8F852C4F87C +:10C53400D462C4F8885384F8B85384F8B953E361D7 +:10C5440070BD00BF180300200000002025B40008BF +:10C5540068D700080F4B4FF47A721B68B3FBF2F3F1 +:10C56400013BB3F1807F10B511D20B4A53600B4BE2 +:10C57400F02183F823100024072394600F21136013 +:10C584004FF0FF302246FDF7EFFE204610BD01209C +:10C5940010BD00BFA400002010E000E000ED00E0AA +:10C5A400DFF834D0002103E00C4B5B5843500431D6 +:10C5B4000B480C4B42189A42F6D30B4A02E0002374 +:10C5C40042F8043B094B9A42F9D3FCF789FF00F087 +:10C5D40023F8FEF7E3FFFEE70000012064DC000817 +:10C5E400000000200C0100200C010020C8080020DD +:10C5F400FEE70B460146184600F000B810B5054C9E +:10C6040013462CB10A4601460220AFF3008010BD48 +:10C61400204610BD0000000070B50D4E0D4CA41B4B +:10C62400A4100025A54209D10B4E0C4C01F07CF856 +:10C63400A41BA4100025A54205D170BD56F82530D1 +:10C6440098470135EEE756F8253098470135F2E76B +:10C6540044DC000844DC000844DC000860DC00081A +:10C6640002440346934200D1704703F8011BF9E7E3 +:10C674009368013B002B10B4936009DA9469A342D8 +:10C6840002DBCBB20A2B03D15DF8044B00F0DEBA17 +:10C694001368581C10601970C8B25DF8044B7047D9 +:10C6A400F8B506460F461446D518AC4201D1002011 +:10C6B40007E03A4614F8011B3046FFF7D9FF431C44 +:10C6C400F3D1F8BD2DE9F04F9DB00C461746039306 +:10C6D400064618B183690BB900F06AFC7C4B9C4296 +:10C6E40057D17468A38918075DD52369002B5AD0E4 +:10C6F4000023099320238DF8293030238DF82A3024 +:10C704004FF0010BB846454615F8013B002B55D1B7 +:10C71400B8EB070A0BD053463A4621463046FFF79A +:10C72400BFFF013000F0C480099B5344099398F87B +:10C734000030002B00F0BC8000234FF0FF32049344 +:10C744000793059206938DF853301A93052229789E +:10C754006048FEF7CBFE05F10108049B002831D1A7 +:10C76400D90644BF20228DF853201A0744BF2B2238 +:10C774008DF853202A782A2A2CD0079A00210A20DF +:10C78400A846013598F80030303B092B6DD971B3B8 +:10C7940026E0514B9C4201D1B468A3E74F4B9C4225 +:10C7A40008BFF4689EE72146304600F0A1FA00284D +:10C7B4009ED04FF0FF301DB0BDE8F08F252BA7D0E1 +:10C7C400A846A0E7434A801A0BFA00F018430490E5 +:10C7D4004546BBE7039A111D12680391002A01DB49 +:10C7E400079204E0524243F002030792049398F83C +:10C7F40000302E2B10D198F801302A2B08F10101BA +:10C8040037D1039B1A1D1B680392002BB8BF4FF04E +:10C81400FF3308F102080593314D98F80010032204 +:10C824002846FEF763FE40B14023401B03FA00F0A4 +:10C83400049B0343049308F1010898F80010294865 +:10C844008DF82810062208F10107FEF74FFE002894 +:10C8540034D0254B03BB039B073323F00703083372 +:10C864000393099B4B4409934CE700FB02320121DB +:10C8740086E70023059318460A258846013198F86F +:10C884000020303A092A03D9002BC5D00590C3E70C +:10C8940005FB00200123F0E703AB00932246134B72 +:10C8A40004A93046AFF30080B0F1FF3F8146D8D1F0 +:10C8B400A3895B063FF57DAF09987CE703AB009342 +:10C8C40022460A4B04A9304600F088F8ECE700BF82 +:10C8D40004DC0008B0DB000824DC0008E4DB00080A +:10C8E400B6DB0008BADB000800000000A5C600089B +:10C8F4002DE9F04791461F468A680B69DDF82080D0 +:10C904009342B8BF1346C9F8003091F84320064655 +:10C914000C4612B10133C9F800302368990642BFAE +:10C92400D9F800300233C9F80030256815F006053F +:10C9340007D104F1190AE368D9F800209B1A9D4233 +:10C9440029DB94F843302268003318BF0123920690 +:10C954002ED404F1430239463046C047013021D079 +:10C964002368E568D9F8002003F00603042B08BF08 +:10C97400AD1AA36822690CBF25EAE5750025934228 +:10C98400C4BF9B1AED184FF000091A344D451BD152 +:10C994000020BDE8F0870123524639463046C0479F +:10C9A400013003D14FF0FF30BDE8F0870135C2E715 +:10C9B400E1185A1C302081F84300224494F84510B1 +:10C9C40082F843100233C4E7012322463946304635 +:10C9D400C0470130E6D009F10109D7E72DE9F0435A +:10C9E40017460A7E85B06E2A984606460C460C9B6E +:10C9F40001F1430E00F0BC801AD8632A2ED00AD865 +:10CA0400002A00F0C880582A00F08A8004F1420508 +:10CA140084F842202AE0642A01D0692AF6D12168E8 +:10CA24001A6811F0800F23D0111D1960136827E0D4 +:10CA3400732A00F0B48008D86F2A2AD0702AE5D16E +:10CA44000A6842F020020A6003E0752A21D0782A9D +:10CA5400DCD1782284F845206E4964E01A6801F13B +:10CA64004205111D1960136884F842300123A3E0C4 +:10CA740011F0400F02F104011960D7D0B2F900306F +:10CA8400002B03DA2D225B4284F8432062490A22F8 +:10CA940017E02068196810F0800F03D0081D186093 +:10CAA4000B6806E010F0400F01F104001860F7D0A5 +:10CAB4000B8859496F2A14BF0A220822002084F8DF +:10CAC40043006568A560002DC0F29C80206820F0BA +:10CAD40004002060002B3FD1002D40F09580754666 +:10CAE400082A0BD12368DA0708D5236962689A42B9 +:10CAF400DEBF302305F8013C05F1FF35AEEB05033D +:10CB04002361CDF800803B4603AA21463046FFF757 +:10CB1400EFFE013055D14FF0FF3005B0BDE8F08392 +:10CB240081F845203C492268186812F0800F00F112 +:10CB340004051D6001D0036802E05506FBD5038897 +:10CB4400D00744BF42F0200222601BB9226822F0C1 +:10CB5400200222601022B1E77546B3FBF2F002FB1B +:10CB64001033CB5C05F8013D03460028F5D1B7E747 +:10CB740008681A68496910F0800F04D0101D186005 +:10CB84001368196007E010F0400F02F10400186008 +:10CB94001368F6D01980002323617546B1E71A683B +:10CBA400111D19601568626800212846FEF79EFC75 +:10CBB40008B1401B606063682361002384F843303C +:10CBC4009FE723692A4639463046C0470130A2D040 +:10CBD40023689B0707D5002504F11909E368039A24 +:10CBE4009B1A9D4205DBE068039B9842B8BF184638 +:10CBF40093E701234A4639463046C04701308AD07C +:10CC04000135EBE7002BA7D10B7884F8423004F10F +:10CC1400420565E7D2DB0008C1DB00080FB40A4B0C +:10CC240013B51C6824B1A36913B9204600F0C0F9F8 +:10CC340005AB049AA16801932046FFF743FD02B0B7 +:10CC4400BDE8104004B07047A8000020F8B50E46B7 +:10CC54001446054618B183690BB900F0A9F9214BB4 +:10CC64009C422AD16C68A369A360A3891A072ED5B4 +:10CC7400236963B323692068C01A6369F6B29842D2 +:10CC8400374604DB2146284600F028F928BBA36870 +:10CC9400013BA36023685A1C22601E706369013043 +:10CCA400984204D0A389DB0719D50A2E17D121464F +:10CCB400284600F013F990B10FE00B4B9C4201D1D0 +:10CCC400AC68D0E7094B9C4208BFEC68CBE721462F +:10CCD400284600F00DF80028CCD04FF0FF37384636 +:10CCE400F8BD00BF04DC000824DC0008E4DB000815 +:10CCF400324B70B51D6806460C4625B1AB6913B9B5 +:10CD0400284600F055F92E4B9C420FD16C68B4F9BB +:10CD14000C309AB215072CD4D00611D409223260F3 +:10CD240043F04003A3814FF0FF3070BD254B9C427C +:10CD340001D1AC68EBE7244B9C4208BFEC68E6E702 +:10CD4400510712D5616B41B104F14403994202D0F9 +:10CD5400304600F037FA00236363A38923F02403E9 +:10CD6400A3810023636023692360A38943F008033C +:10CD7400A38123694BB9A38903F42073B3F5007F1E +:10CD840003D02146304600F0C1F9A28912F0010314 +:10CD94000CD00023A36063695B42A361236953B988 +:10CDA400B4F90C3013F08000BAD170BD920758BFAB +:10CDB4006369A360F2E70020F7E700BFA800002042 +:10CDC40004DC000824DC0008E4DB00088A892DE97F +:10CDD400F041054610070C465AD44B68002B05DC7D +:10CDE4000B6C002B02DC0020BDE8F081E66A002E0B +:10CDF400F9D0002312F480522F682B6033D0606D79 +:10CE0400A3895A0705D56368C01A636B0BB1236CF9 +:10CE1400C01A00230246E66A216A2846B047431C2A +:10CE2400A38906D129681D294BD82B4ACA40D607A5 +:10CE340047D500226260D9042269226004D5421CCD +:10CE440001D12B6803B96065616B2F600029CAD0DA +:10CE540004F14403994202D0284600F0B3F90020BB +:10CE64006063BDE8F081216A01232846B047411C74 +:10CE7400C6D12B68002BC3D01D2B01D0162B01D19A +:10CE84002F60B0E7A38943F04003A381BDE8F0819C +:10CE94000F69002FA7D093070E6808BF4B690F6076 +:10CEA40018BF0023A6EB07088B60B8F1000F9ADDCA +:10CEB40043463A46216AA66A2846B047002807DC5A +:10CEC400A38943F04003A3814FF0FF30BDE8F08114 +:10CED4000744A8EB0008E8E70100402038B50B69D7 +:10CEE40005460C46DBB118B183690BB900F060F854 +:10CEF4000C4B9C4209D16C68B4F90C307BB12146CF +:10CF04002846BDE83840FFF761BF074B9C4201D17A +:10CF1400AC68F1E7054B9C4208BFEC68ECE70020E5 +:10CF240038BD00BF04DC000824DC0008E4DB000892 +:10CF3400014900F0A9B800BFE1CE0008002310B5F4 +:10CF4400044603604360836081814366C281036158 +:10CF540043618361194608225C30FFF781FB054B6E +:10CF64006362054BA362054BE362054B24622363B2 +:10CF740010BD00BF41D3000863D300089BD3000851 +:10CF8400BFD3000870B54A1E682555430E4605F107 +:10CF9400740100F065F9044640B1002180E84200C4 +:10CFA4000C30A06005F16802FFF75AFB204670BD03 +:10CFB400836910B5044633BB8364C3640365124BB1 +:10CFC400124A1B688262984204BF0123836100F005 +:10CFD4001FF86060204600F01BF8A060204600F0B7 +:10CFE40017F80022E06004216068FFF7A7FF012220 +:10CFF4000921A068FFF7A2FF02221221E068FFF7CF +:10D004009DFF0123A36110BDACDB000835CF0008F0 +:10D01400F8B51C4B1E68B369074613B93046FFF7D1 +:10D02400C7FF4836B4687368013B03D5336833B12E +:10D034003668F7E7B4F90C506DB16834F4E70421AD +:10D044003846FFF79FFF30600028F1D10C233B6086 +:10D0540004462046F8BD4FF6FF73E3810123A38104 +:10D0640065662560A560656025616561A561082226 +:10D07400294604F15C00FFF7F3FA6563A563A56430 +:10D08400E564E6E7ACDB00082DE9F8438046894611 +:10D0940000F14804002614B93046BDE8F883A568B9 +:10D0A4006768013F01D52468F5E7AB89012B07D9EF +:10D0B400B5F90E30013303D029464046C84706432C +:10D0C4006835EEE770B50E46B1F90E10002990B040 +:10D0D40014461D4607DA00232B60B3891A0610D4C0 +:10D0E4004FF480630EE001AA00F08EF90028F2DB11 +:10D0F400029A02F47042A2F500535A425A412A603D +:10D10400EEE740230020236010B070BD8B8973B517 +:10D114009D0706460C4607D504F1470323602361A7 +:10D124000123636102B070BD01AB6A46FFF7CAFF19 +:10D1340000990546304600F093F848B9B4F90C302C +:10D144009A05EFD423F0030343F00203A381E3E73A +:10D154000D4BB362A389206043F08003A381009B3D +:10D164006361019B20615BB1B4F90E10304600F09D +:10D174005DF928B1A38923F0030343F00103A381DC +:10D18400A3891D43A581CDE735CF0008024B014695 +:10D19400186800F065B800BFA8000020024B0146E3 +:10D1A400186800F00FB800BFA800002010B5431E97 +:10D1B4000A44914200D110BD11F8014B03F8014F0C +:10D1C400F7E7000038B50546002945D051F8043C7E +:10D1D4000C1F002BB8BFE41800F04AF91F4A13686B +:10D1E400104633B9636014602846BDE8384000F047 +:10D1F40040B9A3420CD921686218934204BF1A684B +:10D204005B68636004BF521822600460ECE7134655 +:10D214005A680AB1A242FAD919685818A0420BD127 +:10D2240020680144581882421960DDD110685268A0 +:10D234005A6001441960D7E702D90C232B60D3E765 +:10D24400206821188A4204BF11685268626004BFD2 +:10D25400091821605C60C7E738BD00BF1C080020C6 +:10D2640070B5CD1C25F0030508350C2D38BF0C25F1 +:10D27400002D064601DBA94203D90C2333600020AC +:10D2840070BD00F0F5F8234A1468214691B9224C88 +:10D2940023681BB9304600F041F820602946304627 +:10D2A40000F03CF8431C26D10C233360304600F0D8 +:10D2B400E0F8E4E70B685B1B1AD40B2B0FD90B6067 +:10D2C400CD50CC18304600F0D4F804F10B00231DE7 +:10D2D40020F00700C31A1BD05A42E25070BD8C42A2 +:10D2E4000DBF63684B686360136018BF0C46E9E7C1 +:10D2F4000C464968CAE7C41C24F00304A04205D0C4 +:10D30400211A304600F00AF80130CDD02560D9E763 +:10D3140070BD00BF1C0800202008002038B5064C52 +:10D324000023054608462360FBF794F9431C02D109 +:10D33400236803B12B6038BDC408002010B50C4627 +:10D34400B1F90E1000F096F80028ABBF636DA38905 +:10D354001B1823F48053ACBF6365A38110BD2DE972 +:10D36400F0411F468B89DB0505460C46164605D55C +:10D3740002230022B1F90E1000F068F8A389B4F971 +:10D384000E1023F48053A38132463B462846BDE861 +:10D39400F04100F017B810B50C46B1F90E1000F0CA +:10D3A40055F8431CA38915BF606523F4805343F4E7 +:10D3B4008053A38118BFA38110BDB1F90E1000F0F2 +:10D3C40013B8000038B5074C05460846114600223C +:10D3D40022601A46FBF783F8431C02D1236803B189 +:10D3E4002B6038BDC408002038B5064C0023054620 +:10D3F40008462360FBF72BF9431C02D1236803B1D1 +:10D404002B6038BDC408002038B5074C00230546FE +:10D41400084611462360FBF715F9431C02D1236823 +:10D4240003B12B6038BD00BFC408002038B5064CDA +:10D434000023054608462360FBF702F9431C02D18A +:10D44400236803B12B6038BDC408002038B5074CED +:10D45400054608461146002222601A46FBF7EEF8FC +:10D46400431C02D1236803B12B6038BDC4080020DB +:10D474007047704738B5074C0546084611460022E8 +:10D4840022601A46FBF7D8F8431C02D1236803B183 +:10D494002B6038BDC4080020244B1A68D0075CBF39 +:10D4A40001221A60224B1A68D1075CBF01221A605C +:10D4B400204B1A68D2075CBF01221A601E4B1A68FF +:10D4C400D0075CBF01221A601C4B1A68D1075CBFED +:10D4D40001221A601A4B1A68D2075CBF01221A6033 +:10D4E400184B1A68D0075CBF01221A60164B1A68E1 +:10D4F400D1075CBF01221A60144B1A68D2075CBFC3 +:10D5040001221A60124B1A68D0075CBF01221A600C +:10D51400104B1A68D1075CBF01221A600E4B1A68BF +:10D52400D2075CBF01221A60704700BF5008002078 +:10D534004C0800204808002044080020400800202F +:10D544003C0800203808002034080020300800205F +:10D554002C0800202808002024080020244B1A68E6 +:10D56400D0075CBF01221A60224B1A68D1075CBF46 +:10D5740001221A60204B1A68D2075CBF01221A608C +:10D584001E4B1A68D0075CBF01221A601C4B1A6834 +:10D59400D1075CBF01221A601A4B1A68D2075CBF1C +:10D5A40001221A60184B1A68D0075CBF01221A6066 +:10D5B400164B1A68D1075CBF01221A60144B1A6813 +:10D5C400D2075CBF01221A60124B1A68D0075CBFF5 +:10D5D40001221A60104B1A68D1075CBF01221A603D +:10D5E4000E4B1A68D2075CBF01221A60704700BF55 +:10D5F400800800207C08002078080020740800209F +:10D60400700800206C0800206808002064080020CE +:10D61400600800205C0800205808002054080020FE +:10D62400184B1A68D1075CBF01221A60164B1A689E +:10D63400D2075CBF01221A60144B1A68D0075CBF82 +:10D6440001221A60124B1A68D1075CBF01221A60CA +:10D65400104B1A68D2075CBF01221A600E4B1A687D +:10D66400D0075CBF01221A600C4B1A68D1075CBF5B +:10D6740001221A600A4B1A68D2075CBF01221A60A1 +:10D68400704700BFA00800209C08002098080020D4 +:10D6940094080020900800208C08002088080020AE +:10D6A40084080020184B1A68D1075CBF01221A6055 +:10D6B400164B1A68D2075CBF01221A60144B1A6811 +:10D6C400D0075CBF01221A60124B1A68D1075CBFF5 +:10D6D40001221A60104B1A68D2075CBF01221A603B +:10D6E4000E4B1A68D0075CBF01221A600C4B1A68F3 +:10D6F400D1075CBF01221A600A4B1A68D2075CBFCB +:10D7040001221A60704700BFC0080020BC08002036 +:10D71400B8080020B4080020B0080020AC0800209D +:10D72400A8080020A4080020F8B500BFF8BC08BC75 +:10D734009E467047F8B500BFF8BC08BC9E467047CB +:10D74400010102030406080A0E0F04080C0000007D +:10D754000000000000010203040607080901040890 +:10D764000200000000000000000000007B8E0008A2 +:10D77400C58D00086B8E0008298E0008518E0008A4 +:10D78400158E0008000000000000000085820008DB +:10D79400DD89000825880008D982000803020A13DD +:10D7A40015141A0809271607060519182A2C5B0DE3 +:10D7B4000E0F172D2E2F505122232B3229282625C8 +:10D7C4000C0B1C1B12111F1E1D2400010410212010 +:10D7D40007062223251B12240400000000740040C5 +:10D7E40003800000050000000074004003000100F5 +:10D7F400FF00000000000000000000000100000025 +:10D8040000400140128910000400000000040040A0 +:10D8140012020100060000000044014012810000D1 +:10D8240007000000004801401281000008000000C9 +:10D83400002C01401286000009000000002C014069 +:10D84400120601000A000000002C014012860100AB +:10D854000B000000002C0140120B02000C00000021 +:10D8640000440140128100000D000000004401400A +:10D87400128110000E000000003401401205010066 +:10D884000F0000000034014012820000100000006C +:10D89400002C014012061100130000000034014066 +:10D8A40012841000140000000044014012810000A2 +:10D8B4001500000000480140128A00001600000014 +:10D8C400004401401281100017000000004801408C +:10D8D4001281100018000000004401401281000071 +:10D8E4001900000000480140128100001A000000E5 +:10D8F40000000040128101001D000000002C0140C6 +:10D90400128610001E000000004001401281000039 +:10D914001F0000000040014012010100200000002F +:10D92400002C01401282000021000000002C014064 +:10D934001202010022000000002C014012820100AA +:10D9440023000000002C0140120202002600000007 +:10D954000004004012820000270000000004004080 +:10D96400120201002800000000040040128201009D +:10D974002900000000040040120202002A000000F6 +:10D9840000340140128410002B00000000340140D8 +:10D99400120411002C000000003401401284110014 +:10D9A4002D000000002C01401284100050000000E3 +:10D9B400002C014012861100FF000000000000004E +:10D9C40000000000D40600200200000000440040D3 +:10D9D400120700000900000000380140120700008F +:10D9E4000E00000000440040120700001300000075 +:10D9F40000440040120700001600000000380140F7 +:10DA0400120700001900000000480040120700003F +:10DA14001A00000000480040120700002400000023 +:10DA240000380140120700002A000000004C0040AA +:10DA3400120500002C0000000050004012050000F8 +:10DA4400FF000000000000000000000003000000D0 +:10DA540000440040120700000A00000000380140A2 +:10DA6400120700000F0000000044004012070000ED +:10DA740014000000004400401207000017000000DA +:10DA84000038014012070000180000000048004060 +:10DA9400120700001B0000000048004012070000AD +:10DAA4002500000000380140120700002B00000090 +:10DAB400004C0040120500003200000000500040FD +:10DAC40012050000FF00000000000000000000003C +:10DAD4000000000001020304010203040506070814 +:10DAE400090A0B0C0D0E0F100203040506070809A2 +:10DAF4000A0B0C0D0E0F101054494D3A20556E6B45 +:10DB04006E6F776E2074696D657220696E737461CF +:10DB14006E6365005374617274000D0A0054656C81 +:10DB2400656772616D3A200054494D3A20556E6B19 +:10DB34006E6F776E20636C6F636B20736F757263A7 +:10DB4400650054494D3A20556E6B6E6F776E2074A4 +:10DB5400696D6572204952516E004552524F523AD6 +:10DB6400206174206C65617374206F6E652055416B +:10DB740052542070696E20686173206E6F20706546 +:10DB8400726970686572616C0A004552524F523A6C +:10DB940020552853294152542070696E73206D69B1 +:10DBA400736D617463680A00AC000020232D302B70 +:10DBB4002000686C4C00656667454647003031328A +:10DBC40033343536373839414243444546003031E1 +:10DBD4003233343536373839616263646566000040 +:10DBE4000000000000000000000000000000000031 +:10DBF4000000000000000000000000000000000021 +:10DC04000000000000000000000000000000000010 +:10DC14000000000000000000000000000000000000 +:10DC240000000000000000000000000000000000F0 +:10DC340000000000000000000000000000000000E0 +:10DC4400A9C10008B981000835C400089DD40008A2 +:0CDC540061D5000825D60008A9D60008FC +:04DC600095810008A2 +:10DC640000000000FF0000000000000000000000B1 +:10DC740000000000000000000000000000000000A0 +:10DC84000000000000000000000000000000000090 +:10DC94000000000000000000000000000000000080 +:10DCA4000000000000000000000000000000000070 +:10DCB4000000000000000000000000000000000060 +:10DCC4000000000000000000000000000000000050 +:10DCD400000000000000000000000000050000003B +:10DCE4000000000000000000000000000000000030 +:10DCF4000000000000000000000000000000000020 +:10DD04000000000000127A00AC00002000000000B7 +:10DD140004DC000824DC0008E4DB00080000000048 +:10DD240000000000000000000000000000000000EF +:10DD340000000000000000000000000000000000DF +:10DD440000000000000000000000000000000000CF +:10DD540000000000000000000000000000000000BF +:0CDD6400000000000000000000000000B3 +:040000050800C5A585 +:00000001FF diff --git a/firmware_updater/updater/source/src/main/resources/updater.ino.slto.v2.hex b/firmware_updater/updater/source/src/test/resources/updater.ino.slto.v2.hex similarity index 97% rename from firmware_updater/updater/source/src/main/resources/updater.ino.slto.v2.hex rename to firmware_updater/updater/source/src/test/resources/updater.ino.slto.v2.hex index 752fd44f..7e688d4f 100644 --- a/firmware_updater/updater/source/src/main/resources/updater.ino.slto.v2.hex +++ b/firmware_updater/updater/source/src/test/resources/updater.ino.slto.v2.hex @@ -1,1501 +1,1501 @@ -:020000040800F2 -:1080000000000120B1C5000801C6000801C6000833 -:1080100001C6000801C6000801C6000800000000F3 -:1080200000000000000000000000000001C6000881 -:1080300001C600080000000001C600083F830008D8 -:1080400001C6000801C6000801C6000801C60008F4 -:1080500001C6000801C60008CBB40008C5B400087A -:1080600001C60008BFB40008B9B4000801C6000882 -:1080700001C6000801C6000801C6000801C60008C4 -:1080800001C6000801C6000801C6000801C60008B4 -:1080900001C6000801C6000801C60008A3B4000814 -:1080A0000992000879920008F991000801C60008B9 -:1080B00069920008599200084992000873900008DC -:1080C0006D900008679000086190000801C60008E4 -:1080D00001C600085DA4000845A4000829A4000802 -:1080E0008BB400085195000801C6000801C60008BD -:1080F0001992000801C6000801C6000801C6000860 -:1081000001C60008000000000000000001C60008D1 -:108110000DA40008F1A30008399200082992000874 -:1081200001C6000801C6000801C6000801C6000813 -:1081300001C6000801C600080000000000000000A1 -:1081400001C6000801C6000801C6000800000000C2 -:10815000000000000000000000000000000000001F -:108160005B9000085590000801C6000801C6000891 -:1081700001C6000801C60008E991000801C6000810 -:1081800001C6000801C60008000000000000000051 -:0481900001C600081C -:1081940010B5054C237833B9044B13B10448AFF33D -:1081A40000800123237010BD0C010020000000009A -:1081B40038D700080C4B43B10C480D4910B5AFF348 -:1081C40000800C48036823B910BD0A48036833B91A -:1081D4007047094B002BF7D0BDE810401847064BF9 -:1081E400002BF5D0184700BF0000000038D7000866 -:1081F400100100200C01002000000000074A937CBD -:1082040000EB4000023318448318805C5B7813F061 -:10821400200F0ABF024B8018C01870476401002069 -:10822400D4060020074B9A7C00EB40001044034422 -:108234001A79052AC3BF044B9B1813F8060C0020B7 -:10824400704700BF6401002050D70008704790F8C1 -:10825400242062B1064A916949B1126C3AB9054ABF -:1082640092F80021520702D503685B6818477047EB -:1082740018030020D4060020002380F8243070471F -:1082840010B50446806C18B1036803211B68984735 -:10829400002384F8243010BD4B060D4B1B7803F0EB -:1082A4000C0363F04F0380F82C30836A80F830307D -:1082B40044BF02F03C0211431A12602380F83210CA -:1082C40080F82F2080F8313007212C3002F0DCBCFC -:1082D4001803002010B54FF48072044600210748AB -:1082E40004F0C4F9064A00231370012284F84C20D8 -:1082F400044A23651268626510BD00BF6401002052 -:108304006C020020D8070020044B41685A6891424F -:1083140004BF012283F88D2002F0C2B84406002075 -:10832400431C02D1002070470C3191F900305A1CD3 -:10833400F8D09842F8D148687047024A136801336C -:1083440013607047D807002008B5054901F033F9D8 -:10835400C0F3C43002280CBF1020002008BD00BFA9 -:10836400E8D7000808B5064901F025F9C0F3C43080 -:10837400023802289DBF034BC018807A002008BD34 -:108384000CD8000850D7000888B0072867D8DFE861 -:1083940000F004121E2A36424E5A324B5A6942F4F5 -:1083A40000325A615B6903F400330093009B4FF081 -:1083B400904008B070472B4B2B485A6942F48022F6 -:1083C4005A615B6903F480230193019BF1E7254B18 -:1083D40026485A6942F400225A615B6903F4002377 -:1083E4000293029BE5E71F4B21485A6942F480122D -:1083F4005A615B6903F480130393039BD9E7194B18 -:108404001C485A6942F400125A615B6903F4001370 -:108414000493049BCDE7134B17485A6942F4800236 -:108424005A615B6903F480030593059BC1E70D4B17 -:1084340012485A6942F400025A615B6903F400036A -:108444000693069BB5E7074B0D485A6942F4803200 -:108454005A615B6903F480330793079BA9E7002003 -:10846400A7E700BF001002400004004800080048CD -:10847400000C004800100048001400480018004890 -:10848400001C0048C0F30312012300F00F0003FA9C -:1084940000F051F82230034341F822307047C0F312 -:1084A400031300F00F0051F8233023FA00F000F01A -:1084B40001007047034B19681A68521A8242FBD3B1 -:1084C400704700BFD807002001207047024B1622D6 -:1084D4001A604FF0FF307047C4080020FEE72DE912 -:1084E400F74F504E8A4633684F4901930220154690 -:1084F400FFF716FF20B90025284603B0BDE8F08F2A -:108504004A4A002352F823105FFA83F911B109682B -:10851400884224D00133052BF4D1454C94F87430AF -:10852400042B1AD9434B50E090424DD193F90030BB -:1085340084F88930022384F888304FF41653A367F3 -:108544003B480023C4F88430E367C4F8803001F06A -:1085540040F894F87430042BCDD894F874901FFA32 -:1085640085F8324B53F8294094F86930202B51D1C7 -:10857400BAF1000F4ED0B8F1000F4BD094F8683028 -:10858400012B47D04FF0010384F868300023E366E1 -:10859400212384F86930D6F800B0A4F85080A4F8F8 -:1085A40052805746B4F852309BB2A3B94FF47A7351 -:1085B4005A464021204602F0B9F958BB202384F8DA -:1085C400693084F8680097E70C335A68002AABD105 -:1085D4004FF0FF33ACE7B4F85230013B9BB2A4F840 -:1085E40052305A464FF47A738021204602F09EF9A5 -:1085F40080B9A3682268B3F5805F07D123692BB9DA -:1086040037F8023BC3F308031385CBE73B781385A4 -:108614000137C7E73368019A9B1AB3F57A7FA0D371 -:1086240069E700BFD8070020D8D900082801002036 -:10863400080000205CDA0008002070470020704722 -:10864400012070474FF400534B60002070474FF0F7 -:10865400FF307047094B5968024609B90849596007 -:108664005868694602448A4289BF064B5A600C2204 -:108674004FF0FF3088BF1A60704700BF6C020020C3 -:10868400C8080020C408002030B50B4B0412997DA3 -:10869400C2B21B33012088420ADC13F8025CAC42EC -:1086A40003D113F8015CAA4204D001300233F2E78B -:1086B4004FF0FF3030BD00BF640100202DE9F04FC2 -:1086C400344C637CA57C1F19E35C85B05B0002908D -:1086D4000E4690460193FFF7D7FFB0F1000A56DB30 -:1086E400AB1C07F1010903934FF0030B019BA9EBAA -:1086F400070293424BDB99F80020924532D199F856 -:108704000150039B1BFB05322244802E527801D07A -:10871400402E30D102F01402142A23D12846FFF748 -:108724006DFD01462846FFF77DFD00281DDDC21DB5 -:1087340042440139801A12F801E901F801EFC242FA -:10874400F9D1A27C144B22445278D2180AD015F0E5 -:10875400010F4FEA650508BF0820515D18BF80204E -:108764000143515509F10209C0E798F8072002F0C6 -:108774003F020A70E5E7002EF4D102F00C020C2A45 -:10878400F0D101220299284600F008F8EAE705B082 -:10879400BDE8F08F64010020D4060020F8B5174628 -:1087A40004460D46FFF72AFD06462046FFF73AFD2C -:1087B400194BBC221A710A12DA7119720022411C77 -:1087C400974201F00F019A7261F01F0114BF402219 -:1087D400802204465972DA7258B1013830440B339E -:1087E400211A4218002A09DD10F8012903F8012F83 -:1087F400F7E7317801F03F010A43DA722846802115 -:10880400064AFFF75BFF04F1080189B20348BDE89B -:10881400F84002F039BA00BFEC050020F005002052 -:1088240090F824302DE9F74F0446002B00F0BB806C -:10883400FFF70DFD94F84C30DFF89881002B7DD0C4 -:108844005E4B1B6C002B79D15D4E626D33689B1AB5 -:10885400226D934269D35B4B9A7CD1184F785A4965 -:108864007F1862D0D8F8085093F811E0D15C0191D8 -:1088740005EB45009E44104418440EF101091633DB -:10888400019A954280F289804FEA650C15F0010A3D -:1088940017F80C100A4618BF0A1102F0030BBBF1BB -:1088A400030F21D1BAF1000F14BF6FF0300A6FF03B -:1088B400030A01EA0A0107F80C109EF800C04946B1 -:1088C4000EEB4C0C8C450FD991F801A055450ED1F7 -:1088D40009784900013101EB030A11F803C09AF841 -:1088E400011051EA0C2104D101350330C8E70231EB -:1088F400E8E790F803C01CF0040FF5D012F004026E -:1089040044D03248BC2303710B12C371E123437278 -:10891400002301728372C3720821043002F0B4F997 -:1089240001353368C8F808506365636D3268D21A3C -:10893400B2F5FA6F24BF03F57A736365A36A3BB19A -:108944001F4BA26B1B689B1A41F2707293422DD885 -:1089540098F800303BB3194B93F828201ABB1A6CD3 -:108964000ABB9B69FBB9A36AEBB9194A144910689D -:108974000B68B0B11B1A002B15DDA06C98B103680D -:108984001B68022103B0BDE8F04F18471CF0400FEC -:10899400AAD02846FFF702FFC2E70023C8F8083030 -:1089A400C3E73233136003B0BDE8F08F00228121A6 -:1089B4002046FFF771FC0023A362C9E718030020D7 -:1089C400D807002064010020D4060020EC05002014 -:1089D400640200206C0200202DE9F743B24E96F8A1 -:1089E4000680F2783779F5794FEA082303F4407367 -:1089F4002B4357EA0227044699B22ED1AB4A92F888 -:108A04000021D0070ED5C02B11D13389A84A5BBAF7 -:108A14009BB21912D1751376A64A336201211170E3 -:108A2400A54A17600023B36103B0BDE8F083B1F534 -:108A3400807FF7D13378E77103F00C0363F04F03C1 -:108A44002371E12363720123A37240232772E3722B -:108A54000821201D02F018F9E4E796F90520002A00 -:108A640008F0C30EC0F25981326A9742DAD11EF07F -:108A7400800F7778B27843D042EA072718F0400293 -:108A840024D0BEF1C20F14D190F83C206AB1826A9E -:108A940097420AD1036C08F03C029A4205D0436B1A -:108AA4000264043303F03C034363012384F83C3041 -:108AB400B8E7BEF1C30FF8D1826A9742F5D100221C -:108AC4008121FFF7E9FB0023A362EEE7B8F1800FF1 -:108AD4000ED1826A002AA5D1784987620968816328 -:108AE4004FF0FF33426380F83C200364726299E7DD -:108AF400B8F1810F96D1816A8F4293D18262F5E7F2 -:108B0400806A42EA072282428CD16C4A1268A263CC -:108B140001F470720020B2F5007FA0725AD012D80E -:108B2400B2F5C07F22D0B3F5607F09DB40F2813219 -:108B3400934240F3C78040F2D132934200F0E08088 -:108B4400C32100251FE0B2F5207F43D0B2F5407F5A -:108B5400E9D18A06F4D1632363724323A3724023C9 -:108B6400E37200232373122363730AE06422337ACB -:108B7400627265F03F054122A272E572237360734D -:108B8400A073C221012508F03C022046FFF784FBB4 -:108B9400002D3FF447AF3378617A03F00C0363F0A0 -:108BA4004F032371A36A23721A12A37AE27113F09A -:108BB400400217BF626B84F83C2023F03C0313434C -:108BC40018BFA37201F00F011CBF012384F83C30CD -:108BD40007313EE73789B2F5207F7FBA05F00F05EC -:108BE400BFB240F0C380606C30B1036839469B6803 -:108BF4009847002840F09C80A7F58070FF283B46EA -:108C04000DD82B4B2E492A46184404F0D5FA294B8B -:108C140001221A70284B00221A60C22191E7FF2F0B -:108C2400FBDC602F06D9214825492A46384404F044 -:108C3400C3FAF2E75A195F2AF5D90A35204A1B49C3 -:108C440035449542E9D0602B107814BFC85481F89C -:108C5400000101320133F4E7FF2F72DD606C28B1AB -:108C6400036839469B689847002873D105F163036C -:108C740063724223A37245F040053B12E5722373ED -:108C840067737EE779195F2960D9084912F1FF32C9 -:108C9400E4D3602B14BF11F803E091F800E180F8ED -:108CA40000E001330130F1E718030020D40600206E -:108CB400640100206C02002064020020D807002018 -:108CC40022030020CB0708D5327A737A2AB9FF2B06 -:108CD40002BF4FF080532E4A1A60A06C18B103688B -:108CE40001211B689847BFF34F8F2A492A4BCA6852 -:108CF40002F4E0621343CB60BFF34F8F00BFFDE784 -:108D0400622363724323A372D223E3720023237387 -:108D140037E7002384F82C3084E6BEF1000F7FF49B -:108D240081AE324601F470713846FFF7C7FC79E62C -:108D3400606C1A4A03683946D3F800902B46C8473A -:108D44005AE7602F04F10E002A469BD914492A469B -:108D5400394419E0606C002203680092D3F804904F -:108D640004F10E022B463946C8477FE7B2F5007F6F -:108D7400CFD1A7F58071FF293B463FF66DAF094B74 -:108D84002A46194404F10E0004F016FA66E700BFFF -:108D940055B01F5E00ED00E00400FA052203002038 -:108DA400D406002064010020044B01685A689142F3 -:108DB40004BF012283F88C2001F072BB440600201A -:108DC4002DE9F04106460D4601EB02080C464445E8 -:108DD400A4EB050707D0336814F8011B1B68304661 -:108DE40098470028F3D13846BDE8F081B0F89A30AE -:108DF400013303F03F03A0F89A30B0F89A30B0F88A -:108E040098009BB298424FF0FF3018BF0020704783 -:108E1400037C33B1B0F82C21B0F82E319BB29A42C6 -:108E2400FAD17047B0F82421B0F8263192B29A42B0 -:108E34001FBFD0F82021D25C013303F03F031ABFD7 -:108E4400A0F8263110464FF0FF307047B0F82431B7 -:108E5400B0F826219BB293421ABFD0F82031985C17 -:108E64004FF0FF307047B0F82431B0F82601181ADB -:108E740000F03F00704738B5012303740446B0F88E -:108E84002C01C31803F03F03B4F82E2192B293428D -:108E9400FAD0D4F82821194D115494F80821A4F8D3 -:108EA4002C3155F8221091F8693091F86A100B436F -:108EB40003F02103212B1ED0114B124943F8221039 -:108EC40003EB820304F194025A610021022294F913 -:108ED4001E0101F049FA94F91E0101F079FAB4F87F -:108EE4002E11D4F8282194F8083112FA81F155F89A -:108EF400230002F0A7FA012038BD00BF280100209A -:108F040078020020F18D000838B5044620B390F8AB -:108F14007420124B53F8220090F8693090F86A20BC -:108F2400134303F02203222B16D0214611F8755F58 -:108F340001F0BAFCB4F89030B4F89220013303F095 -:108F44003F03934208D0B4F89020D4F88C1092B226 -:108F54009BB28D54A4F8903038BD00BF2801002086 -:108F6400FEE770B505460C46084649B102F00EFB13 -:108F74002B680246214628465B68BDE870401847C6 -:108F840070BD30B58BB00B4600218DF82410012A3A -:108F9400D8BF0A220DF12304B3FBF2F505FB12330B -:108FA40003F0FF03092B94BF30333733DBB2214680 -:108FB40004F801392B46002DEED1FFF7D2FF0BB098 -:108FC40030BD2DE9F84306460C46071D00F107099C -:108FD400002517F8011B0A222046FFF7D2FF236859 -:108FE40080461B682E2120469847B94540440544D5 -:108FF400EFD10A22F1792046FFF7C3FF2844BDE8E8 -:10900400F88300BF0368DA69D20782B003D59B8C6A -:109014009BB2019308E0DA699007F8D4DA69510742 -:10902400F5D4DA691207F2D4019B02B0704710B587 -:1090340048B10649002351F82340A042DAB203D0D4 -:109044000133052BF7D10522104610BD280100205D -:10905400002001F059B9002000F07CBD002001F08F -:1090640053B9002000F076BD002001F04DB9002076 -:1090740000F070BD436D10B5044663B190F8502004 -:10908400012A08D190F82810C1F1200111F0FF0144 -:1090940001D05C3098472046BDE8104000F061BD27 -:1090A40038B583689A42044650D101294FD1002330 -:1090B40080F87C3080F85030836D03B1984794F881 -:1090C400413003F02803282B40D194F87C50002D24 -:1090D4003CD00521204602F067F994F84030012B7A -:1090E40034D0012384F8403094F841302A2B03D142 -:1090F4000221204602F058F9292384F84130202324 -:1091040084F842302346002153F85C2B6164506894 -:1091140020F40040506065856362638D23854FF0C1 -:109124000073E36290692C4B6363C50344BF08205A -:10913400D06184F8401029498B424AD028498B4297 -:109144000CBFB823FA2311680B43136038BD90F8A1 -:10915400413003F028030125282B80F85050F5D125 -:10916400062102F021F994F84030AB42EED094F895 -:10917400413084F84050292B03D12946204602F07F -:1091840013F92A2384F841302025234684F84250D9 -:1091940053F85C2B00216164506820F40040506057 -:1091A40065856362638D23854FF00073E362906984 -:1091B400094B6363C0035CBF0820D06184F840108E -:1091C40006498B4205D006498B420CBFB823FC23C9 -:1091D400B9E7B823B7E700BFFB9F0008ABA10008BD -:1091E4008D9F0008024BD86C08B101F0A1BF7047F5 -:1091F400A0020020024B186C08B101F099BF70471F -:10920400A0020020024B986B08B101F091BF704797 -:10921400A0020020024BD86908B101F089BF704751 -:10922400A0020020024B986908B101F081BF704789 -:10923400A0020020024B586908B101F079BF7047C1 -:10924400A0020020024BD86808B101F071BF70473A -:10925400A0020020024B986808B101F069BF704772 -:10926400A0020020024B586808B101F061BF7047AA -:10927400A002002010B5064C206808B101F058FF88 -:10928400E06B18B1BDE8104001F052BF10BD00BF43 -:10929400A0020020836C2BB1027F012A02D100219D -:1092A40004381847704708B5431C12D0214B984224 -:1092B4003DD019D8A3F59233984230D00BD8B0F1F1 -:1092C400804F28D0A3F58063984226D01A4803F033 -:1092D400ABFC002008BD194B984222D003F58063F3 -:1092E4009842F3D1372008BD154B98421FD009D8B6 -:1092F400A3F58053984216D003F540639842E5D114 -:10930400182008BD0F4B98420FD003F50063984214 -:10931400DCD14E2008BD1C2008BD1D2008BD1E2028 -:1093240008BD362008BD2C2008BD1A2008BD192010 -:1093340008BD00BF002C014052DB000800100040B3 -:1093440000440140004801400368594A93428CB0EC -:109354002CD102F56442916941F4006191619269F2 -:1093640002F400620192019A524A1060524A9342F6 -:109374003CD102F5FE32D16941F02001D161D269BC -:1093840002F020020692069A4A4A90614B4A93429E -:109394003BD14B4B9A6942F480129A619B6903F466 -:1093A40080130B930B9B434BD8642EE0B3F1804F97 -:1093B4000CD1434AD16941F00101D161D26902F073 -:1093C40001020292029A3B4A5060CFE73D4A93421F -:1093D4001DD102F50332D16941F00201D161D26994 -:1093E40002F002020392039A324A9060364A934290 -:1093F4001DD1334B9A6942F400529A619B6903F47C -:1094040000530793079B2B4BD8610CB070472F4A2E -:1094140093421CD102F50232D16941F00401D161B9 -:10942400D26902F004020492049A224AD060284AC3 -:1094340093422CD1224B9A6942F480329A619B69FF -:1094440003F480330893089B1A4B9863DDE7214AA1 -:1094540093428BD102F50032D16941F01001D16100 -:10946400D26902F010020592059A124A50611A4A12 -:1094740093428BD1124B9A6942F480229A619B6980 -:1094840003F480230A930A9B0A4B1864BDE7134A2A -:109494009342ECD10A4B9A6942F400329A619B6977 -:1094A40003F400330993099B024BD863ADE700BF73 -:1094B400002C0140A0020020001400400050014094 -:1094C4000010024000040040003401400008004045 -:1094D40000400140001000400048014000440140A9 -:1094E4001549D1F8883043F47003C1F88830134B20 -:1094F4001A6842F001021A605868114A02405A6020 -:109504001A6822F0847222F480321A601A6822F4F3 -:1095140080221A605A6822F4FE025A60DA6A22F043 -:109524000F02DA62186B074A02401A6300229A603B -:10953400054B8B60704700BF00ED00E00010024057 -:109544000CC07FF8CCFC00FF00800008104A936D2B -:10955400996811F4805F114607D0DA68D20541BFDB -:10956400DA68D2B262F4C072DA609A68900407D5FD -:10957400DA68920541BFDA68D2B262F42072DA6026 -:10958400044B4FF400325A61012381F8753070475F -:10959400A002002000040140431C04D00C3111F946 -:1095A4000C3C5A1C01D100207047984201F10C0177 -:1095B400F5D101207047431C04D091F900305A1CA6 -:1095C40001D118467047984201D1886870470C3120 -:1095D400F3E72DE9F04104468AB080B1B94990F926 -:1095E4008800FEF79DFE94F98950B749064628463F -:1095F400FEF796FE00B12EB9B44803F015FB0AB08D -:10960400BDE8F081864218BF0020206008B9B04848 -:10961400F3E7B04B984240F0BB8003F55843DA6857 -:1096240042F48042DA60DA6822F48042DA609A69AD -:1096340042F480429A619B6903F480430093009B47 -:10964400002384F87430252384F88A30C5F303108A -:10965400FEF79AFE94F989509B4980462846FFF705 -:10966400AAFF012700FA07F300F0070203F0100332 -:109674001343069305F00F05C0F30113C0F306204E -:1096840007FA05F505A907930990032340460893B3 -:10969400059500F099FA94F98800C0F30310FEF7D9 -:1096A40073FE94F98880874905464046FFF783FF97 -:1096B400430000F0070208F00F08C0F3011003F0A4 -:1096C4001003134307FA08F7079005A928460597DE -:1096D400069300F079FA0C2094F874207E4BA06174 -:1096E4000020261DE0612062A062606294F86D0093 -:1096F40043F822602568A16FE26FD4F88070D4F833 -:1097040084306560A160E2602761636100F0FF0E50 -:1097140008B984F86CE0242084F86D00286820F0EF -:1097240001002860D5F800E06C481A4342F00C02AE -:109734000EEA000002432A606B6823F440533B4363 -:109744006B60AB6823F43063AB60624B9D4240F0C6 -:109754008C8003F55843624A1B6B03F00303D35C0C -:10976400082B3FF64CAF01A252F823F08F98000863 -:1097740079990008B3980008039600087D9A0008B8 -:10978400039600080396000803960008839A0008CD -:10979400544B984217D103F5E6331A6942F4003268 -:1097A4001A611A6922F400321A61DA6942F4003249 -:1097B400DA61DB6903F400330193019B012384F82C -:1097C4007430262340E7484B984217D103F5E4331D -:1097D4001A6942F480221A611A6922F480221A61F9 -:1097E400DA6942F48022DA61DB6903F480230293AC -:1097F400029B022384F87430272325E73B4B9842CD -:1098040017D103F5E2331A6942F400221A611A6986 -:1098140022F400221A61DA6942F40022DA61DB6977 -:1098240003F400230393039B032384F87430342349 -:109834000AE72F4B98427FF409AF03F5E0331A6926 -:1098440042F480121A611A6922F480121A61DA69E8 -:1098540042F48012DA61DB6903F480130493049BFD -:10986400042384F874303523EEE61E4B9D4241D127 -:1098740003F5E6331B6B03F44033B3F5803F00F08C -:10988400FB800CD8002B7FF4BAAE01F049FDA3682D -:1098940000EB5300B0FBF3F080B2E86074E0B3F582 -:1098A400003F00F0EC80B3F5403F7FF4A8AE4B08D6 -:1098B40003F5F40303F59053B3FBF1F39BB2EB60B0 -:1098C40062E000BFD8D900085CDA00086ADB00084F -:1098D4009ADB00080038014028010020F369FFEFFB -:1098E4006DD700080044004000480040004C004090 -:1098F400005000406A4B9D4210D103F5E4331B6BCA -:1099040003F44023B3F5802F00F0B680BAD9B3F541 -:10991400002F00F0B480B3F5402FC6E7614B9D42A1 -:1099240010D103F5E2331B6B03F44013B3F5801F2E -:1099340000F0A280A6D9B3F5001F00F0A080B3F513 -:10994400401FB2E7584B9D427FF459AE03F5E03314 -:109954001A6B02F44002B2F5800F00F08D8002D839 -:10996400002A92D04BE6B2F5000F00F08880B2F5E1 -:10997400400F9AE701F0BCFCA36800EB5300B0FB76 -:10998400F3F04A4B80B2D860A26A6368002A48D0D8 -:10999400D70705D55968E06A21F4003101435960BD -:1099A400950705D55968206B21F48031014359602E -:1099B400500705D55968606B21F480210143596033 -:1099C400110705D55968A06B21F400410143596082 -:1099D400D70605D59968E06B21F48051014399605D -:1099E400950605D59968206C21F4005101439960CE -:1099F40050060ED55968606C21F480110143B0F50E -:109A0400801F596005D15968A06C21F4C00101433D -:109A14005960110605D55A68E16C22F400220A4304 -:109A24005A605A6822F490425A609A6822F02A02D4 -:109A34009A601A6842F001021A60002222671C4AE6 -:109A440015681B681A071ED463681B685B070AD570 -:109A54006FF07E432A464FF48001304600F066FFE3 -:109A640000287FF4CCAD202384F86D3084F86E3068 -:109A7400002384F86C30C2E501F06AFC07E74B0868 -:109A840003F5004318E76FF07E432A464FF40011B4 -:109A9400304600F04BFF00287FF4B1ADD4E700BF9F -:109AA40000480040004C0040005000400038014095 -:109AB400D807002038B5002580F8425090F841308E -:109AC400292B04460AD1282380F84130212303633B -:109AD400012101F069FC84F8405038BD90F8413010 -:109AE4002A2BFAD1282380F8413022230363022150 -:109AF400EFE7174B10B52022C362002303634363CF -:109B040080F8412080F842304B07044611D5436A5F -:109B14005A1C42620268526A1A70038D4BB1013BAF -:109B24000385438D013B9BB24385436C43F004039F -:109B340043642046072101F037FC23681022DA61D0 -:109B4400002384F840302046BDE81040FFF792BA65 -:109B54000000FFFF0268436B9169126803B1184764 -:109B6400704790F84130202B0BD10268282380F8ED -:109B74004130054B4363136843F0B803136000207E -:109B840070470220704700BFFB9F00080C4B5A69C6 -:109B9400024213D058610023012803F1010202D0CC -:109BA40040081346F8E71422DBB25343054A181D54 -:109BB4001044816811B11A4413691847704700BFF3 -:109BC40000040140780300202DE9F74FD1F800800C -:109BD400DFF89091614A002338FA03F402D103B00C -:109BE400BDE8F08F4FF0010E0EFA03FE18EA0E06E0 -:109BF40000F0A6804C6824F01007022F16D14FEA1B -:109C0400D30A00EB8A0A03F0070BDAF820504FEA74 -:109C14008B0B4FF00F0C0CFA0BFC25EA0C0C0D69A6 -:109C240005FA0BF545EA0C05CAF820504FEA430A39 -:109C34000325D0F800B005FA0AF5ED4304F0030C4F -:109C44000BEA050B0CFA0AFC013F4CEA0B0C012F42 -:109C5400C0F800C011D8876807EA050BCF6807FA77 -:109C64000AFC4CEA0B078760476827EA0E0EC4F328 -:109C740000179F4047EA0E074760C7683D408F685A -:109C840007FA0AF73D43C560E50059D5D9F81850DD -:109C940045F00105C9F81850D9F8185023F0030706 -:109CA40007F1804705F0010507F58037019503F0BA -:109CB400030C019DBD684FEA8C0C4FF00F0E0EFA99 -:109CC4000CFEB0F1904F25EA0E0E3BD0244DA84275 -:109CD4003AD005F58065A84238D005F58065A842DC -:109CE40036D005F58065A84234D005F58065A842D4 -:109CF40032D005F58065A84214BF0725062505FA6C -:109D04000CF545EA0E05BD601568F74314F4803F71 -:109D14000CBF3D4035431560556814F4003F0CBF3B -:109D24003D4035435560956814F4801F0CBF3D4099 -:109D340035439560D568A40254BF3D403543D56092 -:109D4400013349E70025DAE70125D8E70225D6E7FC -:109D54000325D4E70425D2E70525D0E70004014014 -:109D6400000400480010024010B590F82120022A97 -:109D740003D004228263012010BD0268C46B116801 -:109D8400436B21F00E011160116821F00101116093 -:109D9400026C012101FA02F26260002480F82110B1 -:109DA40080F8204013B19847204610BD184610BDD6 -:109DB4000023354A10B580F84230C2624385436CB3 -:109DC4001943416490F84130282B044607D090F899 -:109DD4004130292B03D090F841302A2B22D1032182 -:109DE400204601F0E1FA282384F84130002323635C -:109DF400264B6363236819681A6811F480411ED0E6 -:109E040022F48042A06B1A60214B4363002384F840 -:109E14004030FFF7A9FF002835D0A06B436BBDE8A5 -:109E240010401847072101F0BFFA94F84130602B25 -:109E34001CBF202384F8413000232363D9E712F4A4 -:109E440000420DD01A68E06B22F400421A60104BF5 -:109E5400436384F84010FFF787FFA0B1E06BDDE7B0 -:109E640094F84130602B05D1202384F8413084F8E4 -:109E7400402010BD94F8513084F840201BB920468E -:109E8400BDE810406DE610BD0000FFFFFB9F000819 -:109E9400A9AD000838B5036820220446DA61082217 -:109EA400DA610D46072101F07FFA23685A6842F40B -:109EB40000425A605A6822F0FF7222F48B3222F474 -:109EC400FF7222F001025A60204601F09BFA2268D8 -:109ED4001368580402D41368190409D594F841305E -:109EE400212B0CBFA36BE36B1B685B689BB2638580 -:109EF400638D9BB21BB1636C43F0040363646B0713 -:109F040010D5636A526A591C61621A70238D4BB171 -:109F1400013B2385638D013B9BB26385636C43F0F6 -:109F2400040363640023236384F842306363636C33 -:109F340063B1616C2046FFF73BFF94F84130282B56 -:109F44001FD129462046BDE83840D2E5E26A12F521 -:109F5400803F0BD00B4AE262202284F8412084F82F -:109F640040302046BDE83840FFF784B894F84120DB -:109F740084F84030222A4FF0200284F8412000D196 -:109F840038BD38BD0000FFFF10B590F84030012BFC -:109F940004462ED0012380F84030C80619D5D306D4 -:109FA40017D594F841302268212B0CBFA36BE36BC7 -:109FB4001B685B68B3FA83F310215B09D1611BB1A1 -:109FC400002084F8400010BD636C43F00403636414 -:109FD400F6E7080705D5130703D523680822DA61D5 -:109FE400EEE78806ECD59306EAD52046FFF752FF44 -:109FF400E6E7022010BD2DE9F84390F84030012B2C -:10A0040004460E46174600F0CB800123CD0680F8A7 -:10A0140040303ED5D0063CD5638D9BB28BBBE36A02 -:10A02400B3F1007F03D0E36AB3F1007F14D194F855 -:10A034004130282B10D131462046FFF75AFDB20695 -:10A0440005D5BB0603D531462046FFF723FF002084 -:10A0540084F84000BDE8F883E36A13F5803F236881 -:10A064000CD094F84120292A08D11022DA61204624 -:10A0740001F0C8F92046FFF71DFDE0E71022DA6180 -:10A08400DDE723681022DA61636C43F00403636440 -:10A09400D5E771071AD57A0718D5638D9BB263B1DA -:10A0A400636A5A1C62622268526A1A70238D013BE9 -:10A0B4002385638D013B9BB26385638D9BB2002B2B -:10A0C400BDD1E36A13F5803FD4D1B8E7330746D551 -:10A0D4003D0744D594F8413003F02803282B236826 -:10A0E40037D19A69E1689D6902294FEA1545C2F39F -:10A0F400004805F0FE059A68D3F80C9020D1C2F30D -:10A10400090285EAD21515F006050FD1A16C0131BB -:10A11400A164A16C022992D10821A564D96184F8B3 -:10A12400405041462046FEF7BBFF88E70421204605 -:10A1340001F03AF9002384F8403009F0FE02F0E718 -:10A144000421204601F030F9002384F840302A46E7 -:10A15400E7E70822DA61002384F840306FE7B007AC -:10A164007FF56DAFB9077FF56AAF638D9BB26BB1B5 -:10A17400636A2268591C61621B789362638D013B98 -:10A184009BB26385238D013B238558E7E36AB3F1D2 -:10A19400807F3FF46FAFE36A002B3FF46BAF4EE771 -:10A1A4000220BDE8F88370B590F84030012B0446D6 -:10A1B40000F0A480012380F8403011F010001DD07D -:10A1C400D3061BD522684E491023D361636C43F038 -:10A1D40004036364636B8B420ED04A498B420CBF09 -:10A1E4006023F42311680B431360204601F00AF93D -:10A1F400002084F8400070BD6023F3E70E063ED5CE -:10A2040055063CD523681A6822F040021A60628D14 -:10A2140092B2002A2ED05A68618D89B2FF2999BF63 -:10A22400618D2185FF2121855968208D21F07F7161 -:10A2340021F45831C2F30902C6B221F47F7142EA13 -:10A24400064221F003018CBF4FF080754FF000757A -:10A254000A432A435A60628D121A92B2628594F8B4 -:10A264004120222A1A680CBF42F4004242F4804280 -:10A274001A60BDE740212046FFF79AFDB8E789063A -:10A28400B6D59306B4D523682022DA615A6822F041 -:10A29400FF7222F48B3222F4FF7222F001025A6020 -:10A2A400002222636263A2F58032E26228B11022A6 -:10A2B400DA61636C43F004036364204601F0A2F89E -:10A2C4000321204601F070F8636C1BB994F8412017 -:10A2D400602A01D1616CCEE794F84120212A07D18C -:10A2E400202284F8412094F8422084F842307FE709 -:10A2F40094F84120222A7FF47BAFF1E7022070BD5D -:10A30400ABA100088D9F0008036899691A6810B40E -:10A31400CC0508D5140606D5446C44F00104446405 -:10A324004FF48074DC614C0508D5140606D5446CE2 -:10A3340044F0080444644FF48064DC61890508D562 -:10A34400120606D5426C42F0020242644FF40072D7 -:10A35400DA61436C13F00B0F03D0416C5DF8044BCE -:10A3640026E55DF8044B7047174B30B5DC68C4F341 -:10A374000224C4F10703251D042B28BF0423062D42 -:10A384004FF0010505FA03F303F1FF338CBF033CDF -:10A3940000241940A14005FA04F4013C224000289D -:10A3A40042EA01024FEA0212ADBF00F1604000F040 -:10A3B4000F00064B00F56140B5BFD2B2D2B21A54B9 -:10A3C40080F8002330BD00BF00ED00E014ED00E094 -:10A3D400002808DB4209012300F01F0003FA00F003 -:10A3E400014B43F82200704700E100E0044B4FF4B6 -:10A3F4000012C3F88421034B186900F0ECBC00BFC1 -:10A4040000E100E028010020044B4FF48012C3F85F -:10A414008421034BD86800F0DEBC00BF00E100E0FB -:10A4240028010020044B8022C3F88421034B986840 -:10A4340008B100F0D0BC704700E100E02801002022 -:10A44400034B4022C3F88421024B586800F0C3BC7C -:10A4540000E100E028010020034B2022C3F88421FE -:10A46400024B186800F0B7BC00E100E028010020AE -:10A474000A4AD3695B684FF6FE71D962D96841F024 -:10A484000201D960D96821F00101D9604FF6FF714A -:10A494009963002382F82830536270471803002020 -:10A4A4002DE9F041A44CE36903204FF6FF75022621 -:10A4B400002794F82820092A00F2C881DFE812F066 -:10A4C4000A00A1001900EE003E014E016201920152 -:10A4D400BA01A401E36993F88C30002B00F0898061 -:10A4E4000023636384F85C30FF2263620123A26566 -:10A4F4006365E26992F88C30002B66D1626D1AB103 -:10A50400A36DB3FA83F35B090022626294F85C20C2 -:10A51400002A36D1656B072D22DD002B51D0E2785D -:10A52400207940EA022094F90520002A10DA18B1B3 -:10A53400FEF7AAF8C343DB0F94F8BC2452070CD4EB -:10A54400A56194F8BC349B071BD5CC23636218E047 -:10A55400236AC11A4B424B41EEE793B1A561F4E77C -:10A56400012D2ED194F83830A363CC2B03D0E26AAA -:10A57400236B9A4205DD226C1AB1E36A002B00F3C7 -:10A584006881E369606A5B6841F2E83140F2314214 -:10A59400002808BF0A46DA626FF001021A61DA681D -:10A5A40042F00102DA606FF002021A61DA6842F0E6 -:10A5B4000202DA60002384F85C30032384F8283034 -:10A5C40017E00023A3610C23C0E7536840F2424222 -:10A5D400DA626FF0010100225A621961D96841F010 -:10A5E4000101D960012384F82830A263E264A364E2 -:10A5F4002365E369002283F88C2083F88D20BDE86D -:10A60400F081E56995F88DE0BEF1000F38D16B68F3 -:10A614005F6BE36C03F14402BA4218DA6833A16C4D -:10A62400A06B226DE364002603F1440847459C4671 -:10A6340030DDB1F5807F03F1680324DD26B1C4F871 -:10A644004CC0A164A0632265A36C5B00A364BEF14B -:10A65400000FCED0636D226D1340626B6365172AC1 -:10A6640006DC531CA16B6363A36DA1544B40A3652B -:10A67400022384F828306B684FF4D072DA62B8E7AA -:10A6840040F24247C5E7B2FA82F208435209490050 -:10A694000126C9E7002ED7D0E364D2E793F88C20D3 -:10A6A40012B184F8287004E7636AC3B1E2636822D4 -:10A6B400E3695B689A632332DA626FF001021A611C -:10A6C400DA6842F00102DA606FF002021A61DA68B5 -:10A6D40042F00202DA600023636304236EE7E16A56 -:10A6E400226B914206DD226C1370626C22646364F7 -:10A6F400E362E363236C002B00F0A8801A78C2F3B2 -:10A704008101682201FB0222597901F00F0108310D -:10A71400E163E16A0129CBD1197801F0DF011970F5 -:10A72400E36B206C013BC15C81F02001C154E36AFE -:10A734000133E362BCE700BF1803002093F88C20C8 -:10A7440062B15A68D2F824E0916B0A398E4503D27B -:10A75400956384F82860ACE605232FE7636AF3B1B8 -:10A76400636AA363A36B002101221A424FEA4202E7 -:10A774001CBF83F480730121FF2AF6DD01B1A363BA -:10A784000123A364A06BA26C682310424FEA420128 -:10A794000BD0B2F5807F08DC0A466833F5E7636BBB -:10A7A400226C591C6163D35CDBE7682B0CBF062267 -:10A7B4000722B1F5007FA16484F8282054DD616B81 -:10A7C400E26B914203F59C7302DA626A002A48D074 -:10A7D400092247E0D16821F00201D1606FF0020143 -:10A7E40011614EE0E3695B68596B9A6B453A91429B -:10A7F40008D24FF6FF729A63012384F8283084F854 -:10A804005C30F6E60623D9E6E3695B6841F2504220 -:10A81400DA626FF002021A61DA6842F00202DA6068 -:10A82400636A1BB1002363620823C7E6E36A01334A -:10A83400E362F9E793F88C202AB15A68526BB2F5B7 -:10A84400936F7FF6D6AE84F8280032E6FFF710FE49 -:10A85400CFE600231370626C22646364E362E363F3 -:10A864008FE6052284F8282094F82820072AE26934 -:10A874005268AFD16FF002011161D16841F0020159 -:10A88400D16094F82820092A0BBFE269E169526873 -:10A89400496813BFA3F123024FF6FF7191638A63E2 -:10A8A400E2695268D362A4E690F86A30202B3BD167 -:10A8B400002937D090F86830012B35D0012380F877 -:10A8C4006830A0F85830A0F85A3083684165B3F571 -:10A8D400805F19D10369ABB940F2FF13A0F85C3073 -:10A8E40000232222C36680F86A20026880F8683058 -:10A8F400916841F001019160116841F49071116017 -:10A9040018467047FF23E9E723B90369002BF9D000 -:10A914007F23E3E7B3F1805FE2D10369002BF7D033 -:10A924003F23DBE7012070470220704770B50468BD -:10A93400104EE56931EA050501D1002070BD5D1CAA -:10A94400F7D08BB9236823F4D0732360A36823F072 -:10A954000103A360202380F8693080F86A30002363 -:10A9640080F86830032070BDD6F8C054AD1AAB42ED -:10A97400DFD2E7E71803002010B507490446042096 -:10A98400FDF78DFD30B14FF0904310220CB19A6168 -:10A9940010BD9A6210BD00BFDC0700202DE9F04114 -:10A9A4003B2890B056D8A84C204490F93850681CE5 -:10A9B40050D0A6492846FDF772FD4FEA151805F058 -:10A9C4000F0798B304F174012846FEF7E5FD00284B -:10A9D40043D004F174012846FDF7A2FC0446C8B133 -:10A9E4002846FDF7B1FC30F0100213D12368012191 -:10A9F40001FA00F023EA0000206002238DF804101D -:10AA040093498DF80430CB6923F00053CB61049251 -:10AA14008DF804208D4908F00F00012303FA07F292 -:10AA240051F8203023EA020341F8203008F00F00E7 -:10AA3400FDF7AAFC012303FA07F7032201936946F1 -:10AA44000023009703920293FFF7BEF8814928463A -:10AA5400FDF718FD10B0BDE8F08104F19801284617 -:10AA6400FEF79AFD0028D5D004F198012846FDF799 -:10AA740057FC04460028CDD02846FDF773FC142863 -:10AA8400064600F2DC80744BC340D907C2D5734933 -:10AA94002846FEF790FDC202236A4CD5042202FA2E -:10AAA40006F623EA06062662236A2362226A41F234 -:10AAB40011131A4208D1226A40F244431A4202BFD7 -:10AAC400636C23F400436364226A41F211131A4253 -:10AAD40008D1226A40F244431A4203D1236823F086 -:10AAE4000103236002238DF83D30226A41F21113E1 -:10AAF4001A4208D1226A40F244431A4202BF236830 -:10AB040023F001032360564B9C4253D1504A93696E -:10AB140023F400639361534B9C425CD14C4AD36948 -:10AB240023F02003D361504B9C425CD1484A936983 -:10AB340023F4801356E0012202FA06F623EA0606FD -:10AB44002662236A2362464B9C4213D003F50063BA -:10AB54009C420FD003F540639C420BD003F5806305 -:10AB64009C4207D003F580639C4203D003F5006345 -:10AB74009C420DD1226A41F211131A4208D1226A71 -:10AB840040F244431A4202BF636C23F400436364FB -:10AB9400226A41F211131A4208D1226A40F2444354 -:10ABA4001A4202BF236823F00103236001238DF8B6 -:10ABB4003D3097E7B4F1804F05D1254AD36923F09E -:10ABC4000103D361A7E7294B9C4210D1204AD369E2 -:10ABD40023F00203D361264B9C4210D11C4A936993 -:10ABE40023F40053936100238DF83D3012E7214B89 -:10ABF4009C420CD1164AD36923F00403D3611E4B43 -:10AC04009C4214D1124A936923F48033EAE71B4B24 -:10AC14009C4280D10E4AD36923F01003D361184BB0 -:10AC24009C4280D10A4A936923F48023DAE7154BC6 -:10AC34009C42F4D1064A936923F40033D2E73C28BA -:10AC44007FF4E8AE23E700BF74D70008FC070020B8 -:10AC540000100240DC070020111111000CD800087C -:10AC6400002C01400014004000500140000400404A -:10AC74000034014000080040004001400010004042 -:10AC84000048014000440140184B1A6A12124270F5 -:10AC94001A6A827030B50022FF24551C92B28A428F -:10ACA40003DA825C54402A46F7E744545A6C002A7B -:10ACB400FDD11A6CC2B9186472B693F8282002F058 -:10ACC400FF017AB90322D96283F82820DB695B6823 -:10ACD4000122DA626FF001021A61DA6842F00102BD -:10ACE400DA60596262B630BD5864E5E718030020A3 -:10ACF40008B5044B2BB10448FEF7B5F808B1AFF31F -:10AD0400008008BD00000000B804002010B590F8D1 -:10AD14006A30222B036833D1998C8368B0F85C20A5 -:10AD2400B3F5805F02EA0102436D0DD1016959B99F -:10AD340023F8022B4365B0F85A40013CA4B2A0F8B2 -:10AD44005A402CB1002010BD591C41651A70F2E71D -:10AD540003681A6822F490721A609A6822F0010259 -:10AD64009A60202380F86A30FEF761F90428E9D854 -:10AD7400064A074B53F8203052F820009847E1E781 -:10AD84009A6942F008029A61022010BD5001002025 -:10AD94003C010020406A0023A0F85A30A0F8523049 -:10ADA400FEF730B9406A0268536843F400435360C5 -:10ADB400826B00235363C26B536390F84130602B62 -:10ADC40003D1202380F84130704790F851300BB9FB -:10ADD400FEF7C7BE70470368DA69196870B512F0E8 -:10ADE4000F05044606D1960604D58E0602D5BDE8A5 -:10ADF40070408BE79868002D68D010F0010502D1EF -:10AE040011F4907F62D0D60707D5C80505D5012077 -:10AE14001862E06E40F00100E066960706D52DB199 -:10AE240002201862E06E40F00400E066500706D588 -:10AE34002DB104201862E06E40F00200E0661607AF -:10AE440007D5880600D425B108201862E36E0343B1 -:10AE5400E366E36E002B00F0A880920604D58B060F -:10AE640002D52046FFF752FFE36E13F0080F236864 -:10AE740003D19D6815F0400523D01A6822F490721E -:10AE84001A609A6822F001029A60202284F86A20EB -:10AE94009A68560610D59A68606E22F040029A604D -:10AEA40050B1424B4363FEF75FFF00287DD0606ED4 -:10AEB400BDE87040436B18472046BDE87040FEF77C -:10AEC400A1B82046FEF79EF8E56670BDD50215D5FB -:10AED400400213D54FF480121A62202384F869309B -:10AEE400204684F86A30FEF7A2F8314B53F820105C -:10AEF40020467531BDE87040FFF7D6BC16062BD549 -:10AF04000D0629D594F86920212A4ED1B4F852208F -:10AF140092B242B91A6822F080021A601A6842F0AA -:10AF240040021A6070BDA268B2F5805FE26C0ED177 -:10AF3400216961B932F8021BC1F308011985E26481 -:10AF4400B4F85230013B9BB2A4F8523070BD511C8E -:10AF5400E16412781A85F3E7500626D54A0624D50B -:10AF64001A6822F040021A60202384F869302046CF -:10AF7400FEF75DF8042818D80E4B53F820400E4B0A -:10AF840053F820302046984701300ED0D4F894303E -:10AF9400B4F89A1094F8742013FA81F1074BBDE8C1 -:10AFA400704053F8220000F04DBA70BD99AD00080E -:10AFB400500100208C02002078020020280100208B -:10AFC400036A23F010030362036A70B5446882695C -:10AFD4000D6822F0807222F4E64242EA05228D686E -:10AFE40023F0200343EA05131A4DA84207D005F5C0 -:10AFF4000065A84203D005F5E055A8420DD1CD68FF -:10B0040023F0800343EA0513124DA84223F04003C2 -:10B0140012D005F50065A8420ED00F4DA8420BD002 -:10B0240005F58065A84207D005F58065A84203D0E0 -:10B0340005F50065A84206D124F440658E694C6983 -:10B04400344345EA8404446082614A68826303624B -:10B0540070BD00BF002C014000400140324A03682B -:10B06400904212D0B0F1804F0FD0A2F594329042AA -:10B074000BD002F58062904207D002F596329042DE -:10B0840003D002F5E052904215D14A6823F07003D0 -:10B094001343254A90421DD0B0F1804F1AD0A2F537 -:10B0A4009432904216D002F58062904212D002F59A -:10B0B400963290420ED01D4A90420BD002F5806227 -:10B0C400904207D002F58062904203D002F50062FC -:10B0D400904203D1CA6823F4407313434A6923F0AE -:10B0E4008003134303608B68C3620B6883620E4B57 -:10B0F400984213D003F5006398420FD003F54063E0 -:10B1040098420BD003F58063984207D003F580631F -:10B11400984203D003F50063984201D10B6903639D -:10B1240001234361704700BF002C014000400140EF -:10B1340003681A69910710B504460ED5DA689207B8 -:10B144000BD56FF002021A61012202779B699B07FB -:10B1540075D0FDF729FE0023237723681A69500769 -:10B1640010D5DA6851070DD56FF004021A61022276 -:10B1740022779B6913F4407F204663D0FDF714FEC9 -:10B184000023237723681A6912070FD5DA6810079A -:10B194000CD56FF008021A6104222277DB69990743 -:10B1A400204652D0FDF700FE0023237723681A6956 -:10B1B400D20610D5DA68D0060DD56FF010021A61E8 -:10B1C40008222277DB6913F4407F204640D0FDF744 -:10B1D400EBFD0023237723681A69D10709D5DA68C0 -:10B1E400D20706D56FF001021A61636C201F03B108 -:10B1F400984723681A69140605D5DA68100644BF0F -:10B204006FF080021A611A69D10505D5DA68120651 -:10B2140044BF6FF480721A611A69540605D5DA685E -:10B22400500644BF6FF040021A611A69910605D5B1 -:10B23400DA68920644BF6FF020021A6110BDFEF76F -:10B2440029F888E7FEF726F89AE7FEF723F8ABE734 -:10B25400FEF720F8BDE70368012202FA01F11A6A39 -:10B2640022EA01021A621A6A1143114A19629342CC -:10B2740013D002F5006293420FD002F5406293426C -:10B284000BD002F58062934207D002F580629342AC -:10B2940003D002F50062934203D15A6C42F4004297 -:10B2A4005A641A6842F001021A600020704700BF15 -:10B2B400002C01400F2303600B4B5A6802F0030279 -:10B2C40042605A6802F0F00282605A6802F4E06256 -:10B2D400C2605B68DB0803F4E0630361034B1B6833 -:10B2E40003F007030B607047001002400020024087 -:10B2F400084B4FF460525B6892FAA2F2B2FA82F2FF -:10B3040003F46053D340044AD35C044A1068D84021 -:10B31400704700BF00100240E0DA0008A4000020DB -:10B32400084B4FF4E0625B6892FAA2F2B2FA82F23E -:10B3340003F4E063D340044AD35C044A1068D84061 -:10B34400704700BF00100240E0DA0008A4000020AB -:10B35400124B5A6802F00C01082910B51BD14FF4A6 -:10B36400701191FAA1F10F20B1FA81F1DB6A90FA20 -:10B37400A0F00B4CB0FA80F003F00F0302F470124B -:10B38400C340CA40234422441B7A0648B0FBF3F36B -:10B39400107E584310BD034810BD00BF001002408A -:10B3A400E0DA000800127A0011F0010307D090F8E7 -:10B3B400413003F02803282B0CBF4223F2238A07D1 -:10B3C40009D590F8412002F02802282A0CBF43F046 -:10B3D400440343F0F4034A0701F0110248BF43F069 -:10B3E400B80301F01201112A08BF43F09003122997 -:10B3F40001680A6808BF43F0600322EA03030B6094 -:10B404000020704703689A69910744BF00229A623A -:10B414009A69D2075EBF9A6942F001029A61704745 -:10B42400074938B5A1F5A075A1F11404E3681039F2 -:10B4340013B1032208469847AC422146F4D138BDE3 -:10B44400B804002090F86930202B1AD1B9B190F8D3 -:10B454006830012B15D00123A0F85230C164A0F844 -:10B464005030016800232122C36680F8683080F8D8 -:10B4740069200A6842F080020A6018467047012079 -:10B4840070470220704738B506244FF48065A8B28F -:10B49400FEF77CFB013C4FEA4505F8D138BD38B5D1 -:10B4A40005242025A8B2FEF771FB013C4FEA4505AF -:10B4B400F8D138BD1020FEF769BB0820FEF766BB43 -:10B4C4000220FEF763BB0120FEF760BB01F06CBEF7 -:10B4D40001F06ABE01F068BE01F05EBE01F05CBE20 -:10B4E40000000000000000000000000001F0FF0167 -:10B4F400102A2BDB10F0070F08D010F8013B013A9B -:10B504008B422DD010F0070F42B3F6D1F0B441EACC -:10B51400012141EA014122F007047FF000070023E2 -:10B52400F0E80256083C85EA010586EA010685FA38 -:10B5340047F5A3FA87F586FA47F6A5FA87F68EB992 -:10B54400EED1F0BC01F0FF0102F0070232B110F8B5 -:10B55400013B013A83EA010313B1F8D1002070479B -:10B5640001387047002D06BF35460338073815F0FB -:10B57400010F07D1013015F4807F02BF013015F4AB -:10B58400C03F0130F0BC0138704700BF034613F8D8 -:10B59400012B002AFBD1181A013870472DE9804885 -:10B5A400C34CC44EA7B00D20FFF7F8F904F194077B -:10B5B4004FF4E1330025C4F80C313846062384F8EF -:10B5C4003031C4F81851C4F81451C4F81051FEF7BE -:10B5D40000F894F8083156F8231091F8692091F88E -:10B5E4006A100A4302F02202222A16D006EB8303D1 -:10B5F400B14A5A612946012294F91E019F62FEF75D -:10B60400B3FE94F91E01FEF7E3FE94F8083104F248 -:10B61400091156F82300FFF747F9A8490420FDF75C -:10B62400BBFF0546002835D0A5490420FCF737FFA9 -:10B6340020BBA3490420FCF725FF01271422002185 -:10B644000FA801F013F89D490420FCF769FE044695 -:10B65400002800F08B830420FCF776FE05460028C2 -:10B6640000F01283102840F08183002F40F00E83F5 -:10B67400002303930394039B143357E30027DDE76C -:10B6840003F47013B3F5701F40F06D83636843F0E7 -:10B69400020367E38B490420FDF77EFF044600287C -:10B6A40000F07D8286490420FCF7F9FE20B9844924 -:10B6B4000420FCF7E7FE01254022002116A800F033 -:10B6C400D5FF1C2200210FA800F0D0FF7D490420E3 -:10B6D400FCF726FE04461690002800F04783142247 -:10B6E40000210AA800F0C2FF26A9002341F8743DF6 -:10B6F4000AA8FFF7DFFD671C00F0C680724B9C426E -:10B7040000F0BB8200F2AE80A3F592339C4209D0D4 -:10B7140000F2A180B4F1804F04D0A3F580639C4271 -:10B7240040F0AF800D9CFFF7FBFDB4F5A06F04D093 -:10B7340000F2B680B4F5806F00D14000634BB0FBDB -:10B74400F3F3013B1793FE23199300231A931893E1 -:10B754001B938DF895308DB35D4C9DF89530042086 -:10B76400207003F0FF08FBB956498DF89480FDF76B -:10B7740022FF074616A8FDF7E7FD94F90000C0F381 -:10B784000310FCF701FE02230B9303230D9323788C -:10B79400CDF8308003F00F03C7F306279D400AA9B4 -:10B7A4000E970A95FEF710FA022317A916988DF83A -:10B7B4009530FFF753FC01238DF895300420FCF7F6 -:10B7C400D1FD1428044600F2E384424BC340DD0754 -:10B7D40040F1CC8260230F930023119313931293AF -:10B7E400159314936C2310939DF89430012B00F05F -:10B7F400BD8201238DF89430082C4FF002038DF89C -:10B80400953000F0158171D8002C00F0AD80042C27 -:10B8140000F0FC8001238DF895302A490023042090 -:10B824008DF89430FDF7C7FEC00240F1B381169B3A -:10B83400042202FA04F41A6A22EA04021A621A6A54 -:10B8440014431C625A6C42F400425A641A6842F06F -:10B8540001021A608AE2204B9C423FF463AF03F575 -:10B8640080635CE71D4B9C4200F0078211D8A3F56E -:10B8740080539C4200F0018203F540639C4200F037 -:10B88400FC81174801F0D0F9164801F0CDF90020E9 -:10B8940054E7154B9C4200F0F08103F50063EDE79B -:10B8A400B4F5C06F3FF449AFB4F5E06F44E700BFAF -:10B8B400B8040020280100200D8F0008E8D70008F4 -:10B8C400FC0700200CD80008002C014018E40300F9 -:10B8D4000400002011111100001000400044014038 -:10B8E40008DB000838DB000800480140102C00F099 -:10B8F400E280142C00F018810C2C8BD1169BB54DD2 -:10B904001A6A22F480521A62196A5868DA6922F0B3 -:10B91400807222F4E642AB4242F4C04221F4005168 -:10B9240013D005F50065AB420FD005F54065AB4279 -:10B934000BD005F58065AB4207D005F58065AB42B9 -:10B9440003D005F50065AB4201D120F48040586076 -:10B95400DA616C221A641962DA6942F40062DA610B -:10B96400DA6922F48062A2E0169B9A4F1A6A22F0E6 -:10B9740001021A621D6A59689A6922F4803222F01F -:10B984007302BB4242F0600225F0020013D007F5B7 -:10B994000067BB420FD007F54067BB420BD007F5E9 -:10B9A4008067BB4207D007F58067BB4203D007F529 -:10B9B4000067BB421AD125F00E00864DAB4213D06E -:10B9C40005F50065AB420FD005F54065AB420BD0E1 -:10B9D40005F58065AB4207D005F58065AB4203D021 -:10B9E40005F50065AB4201D121F4407159609A61BB -:10B9F4006C225A6318629A6942F008029A619A6941 -:10BA040022F004029A619A699A6103E70FA91698D1 -:10BA1400FFF7D6FA169B13999A6942F400629A6169 -:10BA24009A6922F480629A619A6942EA0122EBE7F8 -:10BA3400169B68491A6A22F480721A621F6A58684F -:10BA4400DA6922F4803222F073028B4242F06002FF -:10BA540027F4007507D0DFF880E1734503D00EF5B5 -:10BA6400E05E734506D18B4227F4606511D05A49D4 -:10BA74008B420ED059498B420BD001F580618B4229 -:10BA840007D001F580618B4203D001F500618B4240 -:10BA940001D120F440505860DA616C22DA631D62EF -:10BAA400DA6942F00802DA61DA6922F00402DA6142 -:10BAB400DA69DA61AEE6169B464D1A6A22F48032E0 -:10BAC4001A62196A58685A6D22F4803222F07002A0 -:10BAD400AB4242F0600221F4003113D005F5006559 -:10BAE400AB420FD005F54065AB420BD005F5806540 -:10BAF400AB4207D005F58065AB4203D005F5006580 -:10BB0400AB4201D120F4803058605A656C229A65AA -:10BB140019625A6D42F008025A655A6D22F0040205 -:10BB24005A655A6D5A6575E6169B2A4D1A6A22F4AF -:10BB340080121A62196A58685A6D22F0807222F4CF -:10BB4400E042AB4242F4C04221F4001113D005F5A7 -:10BB54000065AB420FD005F54065AB420BD005F54F -:10BB64008065AB4207D005F58065AB4203D005F58F -:10BB74000065AB4201D120F4802058605A656C22E4 -:10BB8400DA6519625A6D42F400625A655A6D22F4FC -:10BB94008062C5E7214616A8FFF75DFBE6E030208A -:10BBA400FEF7FCFE2046FEF7E7FEDFE0FEE794F832 -:10BBB4008910012940F0AD81196841F00101196033 -:10BBC400022384F88930A4E1FFF7AAFB4000C8E10E -:10BBD400FEE700BF002C014000340140004001405A -:10BBE4000A223946B2E249462046FDF7BAF9002353 -:10BBF400AB618021BC22112300274FF0010B4FF0D1 -:10BC0400E10A8DF85F1016A808218DF858200192DA -:10BC14008DF8593000938DF85A808DF85B708DF84B -:10BC24005CB08DF85DA08DF85E70FFF72DF8C8202C -:10BC3400FCF740FC009B019A8DF8593008218123C0 -:10BC440016A88DF858208DF85F308DF85A808DF83D -:10BC54005B708DF85CB08DF85DA08DF85E70FFF7B9 -:10BC640013F8C820FCF726FC4FF09042536983F088 -:10BC740020035361FFF73CF85AE20E9CFFF738FBB0 -:10BC840053E5039503944FE0002FFAD00223BD4FF0 -:10BC94008DF84030FB69DFF81C9323F00053FB61FF -:10BCA400002313938DF84030042389F800309DF865 -:10BCB400403003F0FF08CBB94046FCF765FBFB6955 -:10BCC40043F00053FB61FB69CDF8608003F000533F -:10BCD4000293029B99F8003003F00F020123934072 -:10BCE400169316A903231793FDF76EFF0223002171 -:10BCF40001208DF8403013918DF840008DF84030CC -:10BD0400236840F6FE72AA4023EA020323602368F4 -:10BD1400C022AA4023EA020323608DF84000039165 -:10BD24000394002D7FF4A7AC039B08330393039B78 -:10BD34004FF4D8621A609DF84130012B16D00223CB -:10BD44008DF8403022680123AB40134323602368FD -:10BD5400002D7FF495AC03F03C033C2B03D16368C6 -:10BD640043F00103636001238DF84030864987481E -:10BD7400874CDFF84492DFF84482824FFDF7F1F8F4 -:10BD840084498248FDF7EDF84FF47A70FCF792FB92 -:10BD9400814B824A9A642046FCF79CFA4FF0FF32AA -:10BDA400226486F8492096F8531096F854207C486B -:10BDB4002E23002589F8003142EA0122012389F863 -:10BDC40062504FF0020A89F80131C8F82020042299 -:10BDD400C8F81850257188F8285084F82C506563E9 -:10BDE400A562C8F8245086F8485086F83F5086F873 -:10BDF400415086F8423086F84330C6F83C5186F804 -:10BE040040A0C8F830207A6984F83C5042F40032EB -:10BE140084F82430C8F84050D8F81C40C8F844507E -:10BE240088F85C507A617A6902F400320792079AC2 -:10BE34007A6942F480227A617A690A9302F4802250 -:10BE4400032306920AA9069A0B930C95FDF7BCFEF0 -:10BE540008230AA952480C950D950E93CDF828A0F5 -:10BE6400CDF82CA0FDF7B0FE402262667F22E26688 -:10BE740094F889204B4BA56612F0100F2567A5672F -:10BE8400C4F884503A4623664F4645467FF48EAE46 -:10BE9400916941F001019161916901F001010991F8 -:10BEA400099994F8891001F0FF0009B984F8880011 -:10BEB40000210891196808910899606F21F07001B8 -:10BEC40041F040010891089921F002010891089974 -:10BED40021F440210891089921F47051089108999E -:10BEE40021F4004108910899014308910899E06FF1 -:10BEF400014308910899D4F88000014308910899F6 -:10BF04001960012184F8891094F88910C9067FF515 -:10BF14004EAEA046244B48F8043BD36943F00203D9 -:10BF2400D361D36903F002030593059BC023002268 -:10BF34000A930AA902234FF090400B930C920D929E -:10BF44000E93FDF741FE194B4FF000521A6004A9FD -:10BF54000FA8FFF7AFF9129B002B7FF435AEFFF764 -:10BF6400DFF923686360124BB0FBF3F00023E36056 -:10BF74006361A36194F8413001384FF6FF72A06009 -:10BF84001EE000BF0010024024DB0008B8040020BB -:10BF9400EC0500202ADB0008440600200D8300087D -:10BFA40000040048280001400004004000E100E0D3 -:10BFB40040420F0004000020D40600201803002093 -:10BFC400226103F0FF0993B9404684F84090FDF7DD -:10BFD400BBF96068FDF767F94A460F2140B2FEF7E6 -:10BFE400C3F96068FDF75FF940B2FEF7F1F94FF06D -:10BFF400020984F8419008F104016068FFF72EF803 -:10C00400012384F8413094F84030012B3FF4E0AD33 -:10C01400606884F84190036A604923F00103036275 -:10C0240088428369026A16D0B0F1804F13D0A1F51B -:10C03400943188420FD001F5806188420BD001F51C -:10C044009631884207D001F54061884203D001F55A -:10C054008051884201D123F0030343F0010323F00C -:10C06400F00322F00A0242F0020243F0400383612B -:10C074000262836923F00C038361836983614FF057 -:10C084000109602384F841904FF0020A16934FF09F -:10C09400000B4FF6FF731793CDF860A0CDF868B08E -:10C0A40016A984F8409084F841A0FEF789FF6368DC -:10C0B4009A6942F400629A619A6922F480629A61F0 -:10C0C40099691A9A41EA02229A6184F8419084F8A3 -:10C0D40040B09A6922F400629A6184F841A01A6817 -:10C0E40042EA09021A6084F84190DA6842EA0A02D4 -:10C0F400DA601A6A22F001021A621A6A42EA090232 -:10C104001A621A6842EA09021A6004214046FFF7DB -:10C11400A2F897F800315B0705D597F8003183F052 -:10C12400840387F800311E4CDFF87C904FF00A0836 -:10C13400D6F840717B1C002FC6F84031BFF650ADD5 -:10C144002D212046FCF797FE0A2279422046FCF76F -:10C1540018FF49462046FCF704FFAB69002B3FF467 -:10C1640048AD1049DFF844A02046FCF7FAFE00274A -:10C17400AB69BB427FF737AD1FB151462046FCF790 -:10C18400F0FEE95D10222046FCF7FBFE0137EFE7E5 -:10C194003C287FF4EBADFFF71DBB00BF002C014032 -:10C1A400B80400202DDB00082ADB0008C0DB0008EF -:10C1B4009A499B48CA689B4B70B54FF6FF042240CE -:10C1C4000243CA601A6842F010021A60CB68234026 -:10C1D4000343954CCB6000F0C3F9636813F00C0F74 -:10C1E4000AD0636803F00C03082B69D1636803F475 -:10C1F400C033B3F5004F63D1022393FAA3F222684C -:10C2040093FAA3F32368F82292FAA2F21021B2FA65 -:10C2140082F223F0F80301FA02F213432360636805 -:10C2240003F00C03082B6AD04FF0807393FAA3F346 -:10C23400B3FA83F303F1845303F584139B000022C0 -:10C244001A607A4A16684FF00070012190FAA0F340 -:10C25400256890FAA0F3B3FA83F303F01F0301FAFD -:10C2640003F32B4240F08580E36A23F00F03E3627B -:10C27400636823F4761343F4E41363604FF080732C -:10C2840093FAA3F3B3FA83F303F1845303F584130A -:10C294009B000125196016684FF0007191FAA1F313 -:10C2A400206891FAA1F3B3FA83F303F01F0305FAAC -:10C2B40003F3034223D113689B1B022BEED91EE028 -:10C2C400012292FAA2F3B3FA83F303F1845303F540 -:10C2D40084139B0055491A600E68022090FAA0F35B -:10C2E400256890FAA0F3B3FA83F303F01F0302FA6C -:10C2F40003F32B4286D10B689B1B022BEED9494AD0 -:10C30400136803F00703012B39D9636823F0F003A2 -:10C3140063604FF0007393FAA3F2216893FAA3F3D6 -:10C32400B3FA83F303F01F03012202FA03F30B426F -:10C3340031D13F4B4FF47A721B68B3FBF2F3013BEC -:10C34400B3F1807F09D23B4A5360344BF02183F828 -:10C3540023100023936007231360364A136843F0C5 -:10C3640004031360002211464FF0FF30FDF7FCFF79 -:10C3740070BD13689B1B022B7FF668AFBFE7136881 -:10C3840023F0070343F002031360136803F0070369 -:10C39400022BCED1B9E7636823F0030343F0020311 -:10C3A4006360224B196841F28830626802F00C0223 -:10C3B400082A22D11B4A136803F00703022B21D851 -:10C3C400636823F4E06343F480636360636823F485 -:10C3D40060536360FEF7BCFF6368F02292FAA2F236 -:10C3E400B2FA82F203F0F003D340134AD35CD8408C -:10C3F4000F4B186000F0B4F89BE71A68521A824297 -:10C40400D3D996E7136823F0070343F002031360BC -:10C41400136803F00703022B8BD1D1E700ED00E092 -:10C424000003FA050020024000100240D807002053 -:10C43400A400002010E000E05DD7000870B5444C73 -:10C44400444A4549454E062384F86030072384F85E -:10C454007430082384F88830092384F89C300A2334 -:10C46400002584F8B030172384F8C43084F8D83019 -:10C4740084F8EC3084F8003184F8143128462823F9 -:10C4840084F8283184F83C3184F8503184F86431DC -:10C4940084F8783184F88C31E566C4F88050C4F8A7 -:10C4A4009450C4F8A850C4F8BC50C4F8D050C4F890 -:10C4B400E450C4F8F850C4F80C51C4F82051C4F83E -:10C4C4003451C4F84851C4F85C51C4F87051C4F8EC -:10C4D4008451C4F8985100F092F84FF47A73C4F878 -:10C4E400A83106F10803C4F8A031032384F8BD324F -:10C4F400022384F8BC3204F2B113C4F8C032C4F885 -:10C50400A45104F2F1135822294604F53570A4F815 -:10C51400C452C4F8C832A4F8C652A4F8CC52A4F841 -:10C52400CE5200F0A3F80123C4F8F432283604F5FF -:10C534004B7384F8285084F8F852C4F8D462C4F8D1 -:10C54400885384F8B85384F8B953E36170BD00BFCD -:10C55400180300200000002025B4000874D7000848 -:10C564000F4B4FF47A721B68B3FBF2F3013BB3F148 -:10C57400807F10B511D20B4A53600B4BF02183F826 -:10C5840023100024072394600F2113604FF0FF3021 -:10C594002246FDF7E9FE204610BD012010BD00BF74 -:10C5A400A400002010E000E000ED00E0DFF834D04B -:10C5B400002103E00C4B5B58435004310B480C4BF7 -:10C5C40042189A42F6D30B4A02E0002342F8043B95 -:10C5D400094B9A42F9D3FCF783FF00F023F8FEF7E6 -:10C5E400DDFFFEE70000012070DC000800000020F1 -:10C5F4000C0100200C010020C8080020FEE70B46B7 -:10C604000146184600F000B810B5054C13462CB18D -:10C614000A4601460220AFF3008010BD204610BD3B -:10C624000000000070B50D4E0D4CA41BA410002595 -:10C63400A54209D10B4E0C4C01F07CF8A41BA410AC -:10C644000025A54205D170BD56F82530984701351F -:10C65400EEE756F8253098470135F2E750DC00083C -:10C6640050DC000850DC00086CDC0008024403467F -:10C67400934200D1704703F8011BF9E79368013B2B -:10C68400002B10B4936009DA9469A34202DBCBB2A5 -:10C694000A2B03D15DF8044B00F0DEBA1368581C72 -:10C6A40010601970C8B25DF8044B7047F8B50646BF -:10C6B4000F461446D518AC4201D1002007E03A4693 -:10C6C40014F8011B3046FFF7D9FF431CF3D1F8BD22 -:10C6D4002DE9F04F9DB00C4617460393064618B15A -:10C6E40083690BB900F06AFC7C4B9C4257D1746897 -:10C6F400A38918075DD52369002B5AD00023099319 -:10C7040020238DF8293030238DF82A304FF0010B87 -:10C71400B846454615F8013B002B55D1B8EB070A3E -:10C724000BD053463A4621463046FFF7BFFF01304F -:10C7340000F0C480099B5344099398F80030002BFF -:10C7440000F0BC8000234FF0FF320493079305925E -:10C7540006938DF853301A93052229786048FEF722 -:10C76400C5FE05F10108049B002831D1D90644BF58 -:10C7740020228DF853201A0744BF2B228DF8532012 -:10C784002A782A2A2CD0079A00210A20A8460135A3 -:10C7940098F80030303B092B6DD971B326E0514B2A -:10C7A4009C4201D1B468A3E74F4B9C4208BFF46894 -:10C7B4009EE72146304600F0A1FA00289ED04FF0B3 -:10C7C400FF301DB0BDE8F08F252BA7D0A846A0E709 -:10C7D400434A801A0BFA00F0184304904546BBE71D -:10C7E400039A111D12680391002A01DB079204E0E9 -:10C7F400524243F002030792049398F800302E2B20 -:10C8040010D198F801302A2B08F1010137D1039B8C -:10C814001A1D1B680392002BB8BF4FF0FF3308F1B9 -:10C8240002080593314D98F8001003222846FEF7BC -:10C834005DFE40B14023401B03FA00F0049B034318 -:10C84400049308F1010898F8001029488DF828107D -:10C85400062208F10107FEF749FE002834D0254BD3 -:10C8640003BB039B073323F0070308330393099B9C -:10C874004B4409934CE700FB0232012186E7002375 -:10C88400059318460A258846013198F80020303A65 -:10C89400092A03D9002BC5D00590C3E705FB002066 -:10C8A4000123F0E703AB00932246134B04A930465F -:10C8B400AFF30080B0F1FF3F8146D8D1A3895B0676 -:10C8C4003FF57DAF09987CE703AB009322460A4B02 -:10C8D40004A9304600F088F8ECE700BF10DC00083B -:10C8E400BCDB000830DC0008F0DB0008C2DB000819 -:10C8F400C6DB000800000000B1C600082DE9F047BF -:10C9040091461F468A680B69DDF820809342B8BFC0 -:10C914001346C9F8003091F8432006460C4612B17C -:10C924000133C9F800302368990642BFD9F80030B2 -:10C934000233C9F80030256815F0060507D104F163 -:10C94400190AE368D9F800209B1A9D4229DB94F860 -:10C9540043302268003318BF012392062ED404F119 -:10C96400430239463046C047013021D02368E56888 -:10C97400D9F8002003F00603042B08BFAD1AA368FE -:10C9840022690CBF25EAE57500259342C4BF9B1AB2 -:10C99400ED184FF000091A344D451BD10020BDE8B5 -:10C9A400F0870123524639463046C047013003D14F -:10C9B4004FF0FF30BDE8F0870135C2E7E1185A1C9B -:10C9C400302081F84300224494F8451082F8431043 -:10C9D4000233C4E70123224639463046C0470130BA -:10C9E400E6D009F10109D7E72DE9F04317460A7E9D -:10C9F40085B06E2A984606460C460C9B01F1430E00 -:10CA040000F0BC801AD8632A2ED00AD8002A00F07D -:10CA1400C880582A00F08A8004F1420584F8422034 -:10CA24002AE0642A01D0692AF6D121681A6811F033 -:10CA3400800F23D0111D1960136827E0732A00F0BA -:10CA4400B48008D86F2A2AD0702AE5D10A6842F047 -:10CA540020020A6003E0752A21D0782ADCD17822EA -:10CA640084F845206E4964E01A6801F14205111DFD -:10CA74001960136884F842300123A3E011F0400FD9 -:10CA840002F104011960D7D0B2F90030002B03DAA7 -:10CA94002D225B4284F8432062490A2217E0206871 -:10CAA400196810F0800F03D0081D18600B6806E0A9 -:10CAB40010F0400F01F104001860F7D00B885949B9 -:10CAC4006F2A14BF0A220822002084F843006568F4 -:10CAD400A560002DC0F29C80206820F00400206036 -:10CAE400002B3FD1002D40F095807546082A0BD1CC -:10CAF4002368DA0708D5236962689A42DEBF3023C7 -:10CB040005F8013C05F1FF35AEEB05032361CDF8D3 -:10CB140000803B4603AA21463046FFF7EFFE013072 -:10CB240055D14FF0FF3005B0BDE8F08381F84520C2 -:10CB34003C492268186812F0800F00F104051D605A -:10CB440001D0036802E05506FBD50388D00744BF33 -:10CB540042F0200222601BB9226822F020022260E7 -:10CB64001022B1E77546B3FBF2F002FB1033CB5C45 -:10CB740005F8013D03460028F5D1B7E708681A68AF -:10CB8400496910F0800F04D0101D186013681960F3 -:10CB940007E010F0400F02F1040018601368F6D0AB -:10CBA4001980002323617546B1E71A68111D1960C5 -:10CBB4001568626800212846FEF798FC08B1401BFE -:10CBC400606063682361002384F843309FE723692E -:10CBD4002A4639463046C0470130A2D023689B0715 -:10CBE40007D5002504F11909E368039A9B1A9D42AD -:10CBF40005DBE068039B9842B8BF184693E701231E -:10CC04004A4639463046C04701308AD00135EBE701 -:10CC1400002BA7D10B7884F8423004F1420565E774 -:10CC2400DEDB0008CDDB00080FB40A4B13B51C682B -:10CC340024B1A36913B9204600F0C0F905AB049AE6 -:10CC4400A16801932046FFF743FD02B0BDE8104000 -:10CC540004B07047A8000020F8B50E4614460546F7 -:10CC640018B183690BB900F0A9F9214B9C422AD170 -:10CC74006C68A369A360A3891A072ED5236963B3DB -:10CC840023692068C01A6369F6B29842374604DB08 -:10CC94002146284600F028F928BBA368013BA3607D -:10CCA40023685A1C22601E7063690130984204D0C4 -:10CCB400A389DB0719D50A2E17D12146284600F08F -:10CCC40013F990B10FE00B4B9C4201D1AC68D0E753 -:10CCD400094B9C4208BFEC68CBE72146284600F08C -:10CCE4000DF80028CCD04FF0FF373846F8BD00BF10 -:10CCF40010DC000830DC0008F0DB0008324B70B5B3 -:10CD04001D6806460C4625B1AB6913B9284600F0E8 -:10CD140055F92E4B9C420FD16C68B4F90C309AB281 -:10CD240015072CD4D00611D40922326043F04003F5 -:10CD3400A3814FF0FF3070BD254B9C4201D1AC68FC -:10CD4400EBE7244B9C4208BFEC68E6E7510712D599 -:10CD5400616B41B104F14403994202D0304600F0C2 -:10CD640037FA00236363A38923F02403A3810023F8 -:10CD7400636023692360A38943F00803A3812369C3 -:10CD84004BB9A38903F42073B3F5007F03D0214684 -:10CD9400304600F0C1F9A28912F001030CD000233F -:10CDA400A36063695B42A361236953B9B4F90C308E -:10CDB40013F08000BAD170BD920758BF6369A360B5 -:10CDC400F2E70020F7E700BFA800002010DC00080D -:10CDD40030DC0008F0DB00088A892DE9F0410546C3 -:10CDE40010070C465AD44B68002B05DC0B6C002B47 -:10CDF40002DC0020BDE8F081E66A002EF9D00023B1 -:10CE040012F480522F682B6033D0606DA3895A07C7 -:10CE140005D56368C01A636B0BB1236CC01A002379 -:10CE24000246E66A216A2846B047431CA38906D114 -:10CE340029681D294BD82B4ACA40D60747D500225A -:10CE44006260D9042269226004D5421C01D12B6896 -:10CE540003B96065616B2F600029CAD004F14403F3 -:10CE6400994202D0284600F0B3F900206063BDE87F -:10CE7400F081216A01232846B047411CC6D12B68A2 -:10CE8400002BC3D01D2B01D0162B01D12F60B0E78E -:10CE9400A38943F04003A381BDE8F0810F69002F0B -:10CEA400A7D093070E6808BF4B690F6018BF002313 -:10CEB400A6EB07088B60B8F1000F9ADD43463A46AB -:10CEC400216AA66A2846B047002807DCA38943F0F4 -:10CED4004003A3814FF0FF30BDE8F0810744A8EB85 -:10CEE4000008E8E70100402038B50B6905460C4608 -:10CEF400DBB118B183690BB900F060F80C4B9C42AC -:10CF040009D16C68B4F90C307BB121462846BDE8E0 -:10CF14003840FFF761BF074B9C4201D1AC68F1E791 -:10CF2400054B9C4208BFEC68ECE7002038BD00BF0D -:10CF340010DC000830DC0008F0DB0008014900F0D8 -:10CF4400A9B800BFEDCE0008002310B50446036065 -:10CF54004360836081814366C2810361436183616D -:10CF6400194608225C30FFF781FB054B6362054BD1 -:10CF7400A362054BE362054B2462236310BD00BF2B -:10CF84004DD300086FD30008A7D30008CBD3000803 -:10CF940070B54A1E682555430E4605F1740100F02C -:10CFA40065F9044640B1002180E842000C30A060DD -:10CFB40005F16802FFF75AFB204670BD836910B57E -:10CFC400044633BB8364C3640365124B124A1B6873 -:10CFD4008262984204BF0123836100F01FF86060FD -:10CFE400204600F01BF8A060204600F017F800224D -:10CFF400E06004216068FFF7A7FF01220921A0680F -:10D00400FFF7A2FF02221221E068FFF79DFF012330 -:10D01400A36110BDB8DB000841CF0008F8B51C4B74 -:10D024001E68B369074613B93046FFF7C7FF483691 -:10D03400B4687368013B03D5336833B13668F7E7E6 -:10D04400B4F90C506DB16834F4E704213846FFF7A5 -:10D054009FFF30600028F1D10C233B60044620463A -:10D06400F8BD4FF6FF73E3810123A3816566256054 -:10D07400A560656025616561A5610822294604F102 -:10D084005C00FFF7F3FA6563A563A564E564E6E76E -:10D09400B8DB00082DE9F8438046894600F14804CE -:10D0A400002614B93046BDE8F883A5686768013FD7 -:10D0B40001D52468F5E7AB89012B07D9B5F90E3002 -:10D0C400013303D029464046C84706436835EEE796 -:10D0D40070B50E46B1F90E10002990B014461D46E5 -:10D0E40007DA00232B60B3891A0610D44FF4806347 -:10D0F4000EE001AA00F08EF90028F2DB029A02F495 -:10D104007042A2F500535A425A412A60EEE7402386 -:10D114000020236010B070BD8B8973B59D0706464F -:10D124000C4607D504F1470323602361012363619F -:10D1340002B070BD01AB6A46FFF7CAFF009905460D -:10D14400304600F093F848B9B4F90C309A05EFD49E -:10D1540023F0030343F00203A381E3E70D4BB3621F -:10D16400A389206043F08003A381009B6361019B3A -:10D1740020615BB1B4F90E10304600F05DF928B1BE -:10D18400A38923F0030343F00103A381A3891D436F -:10D19400A581CDE741CF0008024B0146186800F095 -:10D1A40065B800BFA8000020024B0146186800F0D3 -:10D1B4000FB800BFA800002010B5431E0A449142D6 -:10D1C40000D110BD11F8014B03F8014FF7E700003F -:10D1D40038B50546002945D051F8043C0C1F002BF6 -:10D1E400B8BFE41800F04AF91F4A1368104633B96F -:10D1F400636014602846BDE8384000F040B9A3429B -:10D204000CD921686218934204BF1A685B68636092 -:10D2140004BF521822600460ECE713465A680AB14E -:10D22400A242FAD919685818A0420BD120680144C7 -:10D23400581882421960DDD1106852685A6001445E -:10D244001960D7E702D90C232B60D3E72068211893 -:10D254008A4204BF11685268626004BF09182160E1 -:10D264005C60C7E738BD00BF1C08002070B5CD1C4A -:10D2740025F0030508350C2D38BF0C25002D064676 -:10D2840001DBA94203D90C233360002070BD00F0F8 -:10D29400F5F8234A1468214691B9224C23681BB936 -:10D2A400304600F041F820602946304600F03CF852 -:10D2B400431C26D10C233360304600F0E0F8E4E749 -:10D2C4000B685B1B1AD40B2B0FD90B60CD50CC18F9 -:10D2D400304600F0D4F804F10B00231D20F00700C1 -:10D2E400C31A1BD05A42E25070BD8C420DBF636812 -:10D2F4004B686360136018BF0C46E9E70C46496845 -:10D30400CAE7C41C24F00304A04205D0211A304605 -:10D3140000F00AF80130CDD02560D9E770BD00BF18 -:10D324001C0800202008002038B5064C00230546C0 -:10D3340008462360FBF78EF9431C02D1236803B12E -:10D344002B6038BDC408002010B50C46B1F90E108E -:10D3540000F096F80028ABBF636DA3891B1823F473 -:10D364008053ACBF6365A38110BD2DE9F0411F4616 -:10D374008B89DB0505460C46164605D5022300229B -:10D38400B1F90E1000F068F8A389B4F90E1023F473 -:10D394008053A38132463B462846BDE8F04100F065 -:10D3A40017B810B50C46B1F90E1000F055F8431C2F -:10D3B400A38915BF606523F4805343F48053A3818C -:10D3C40018BFA38110BDB1F90E1000F013B800000E -:10D3D40038B5074C054608461146002222601A4615 -:10D3E400FBF77DF8431C02D1236803B12B6038BDE1 -:10D3F400C408002038B5064C0023054608462360BF -:10D40400FBF725F9431C02D1236803B12B6038BD17 -:10D41400C408002038B5074C0023054608461146C9 -:10D424002360FBF70FF9431C02D1236803B12B607F -:10D4340038BD00BFC408002038B5064C002305469B -:10D4440008462360FBF7FCF8431C02D1236803B1B0 -:10D454002B6038BDC408002038B5074C0546084683 -:10D464001146002222601A46FBF7E8F8431C02D159 -:10D47400236803B12B6038BDC4080020704770478F -:10D4840038B5074C054608461146002222601A4664 -:10D49400FBF7D2F8431C02D1236803B12B6038BDDB -:10D4A400C4080020244B1A68D0075CBF01221A600C -:10D4B400224B1A68D1075CBF01221A60204B1A68FC -:10D4C400D2075CBF01221A601E4B1A68D0075CBFEA -:10D4D40001221A601C4B1A68D1075CBF01221A6032 -:10D4E4001A4B1A68D2075CBF01221A60184B1A68DB -:10D4F400D0075CBF01221A60164B1A68D1075CBFC3 -:10D5040001221A60144B1A68D2075CBF01221A6008 -:10D51400124B1A68D0075CBF01221A60104B1A68BC -:10D52400D1075CBF01221A600E4B1A68D2075CBF98 -:10D5340001221A60704700BF500800204C080020E8 -:10D544004808002044080020400800203C0800202F -:10D554003808002034080020300800202C0800205F -:10D564002808002024080020244B1A68D0075CBF38 -:10D5740001221A60224B1A68D1075CBF01221A608B -:10D58400204B1A68D2075CBF01221A601E4B1A682E -:10D59400D0075CBF01221A601C4B1A68D1075CBF1C -:10D5A40001221A601A4B1A68D2075CBF01221A6062 -:10D5B400184B1A68D0075CBF01221A60164B1A6810 -:10D5C400D1075CBF01221A60144B1A68D2075CBFF2 -:10D5D40001221A60124B1A68D0075CBF01221A603C -:10D5E400104B1A68D1075CBF01221A600E4B1A68EF -:10D5F400D2075CBF01221A60704700BF8008002078 -:10D604007C0800207808002074080020700800209E -:10D614006C080020680800206408002060080020CE -:10D624005C0800205808002054080020184B1A6891 -:10D63400D1075CBF01221A60164B1A68D2075CBF7F -:10D6440001221A60144B1A68D0075CBF01221A60C9 -:10D65400124B1A68D1075CBF01221A60104B1A687A -:10D66400D2075CBF01221A600E4B1A68D0075CBF58 -:10D6740001221A600C4B1A68D1075CBF01221A60A0 -:10D684000A4B1A68D2075CBF01221A60704700BFB8 -:10D69400A00800209C08002098080020940800207E -:10D6A400900800208C0800208808002084080020AE -:10D6B400184B1A68D1075CBF01221A60164B1A680E -:10D6C400D2075CBF01221A60144B1A68D0075CBFF2 -:10D6D40001221A60124B1A68D1075CBF01221A603A -:10D6E400104B1A68D2075CBF01221A600E4B1A68ED -:10D6F400D0075CBF01221A600C4B1A68D1075CBFCB -:10D7040001221A600A4B1A68D2075CBF01221A6010 -:10D71400704700BFC0080020BC080020B8080020E3 -:10D72400B4080020B0080020AC080020A80800209D -:10D73400A4080020F8B500BFF8BC08BC9E4670479A -:0CD74400F8B500BFF8BC08BC9E4670475A -:10D75000010102030406080A0E0F04080C00000071 -:10D760000000000000010203040607080901040884 -:10D770000200000000000000000000007B8E000896 -:10D78000C58D00086B8E0008298E0008518E000898 -:10D79000158E0008000000000000000085820008CF -:10D7A000DD89000825880008D982000803020A13D1 -:10D7B00015141A0809271607060519182A2C5B0DD7 -:10D7C0000E0F172D2E2F505122232B3229282625BC -:10D7D0000C0B1C1B12111F1E1D2400010410212004 -:10D7E00007062223251B12240400000000740040B9 -:10D7F00003800000050000000074004003000100E9 -:10D80000FF00000000000000000000000100000018 -:10D810000040014012891000040000000004004094 -:10D8200012020100060000000044014012810000C5 -:10D8300007000000004801401281000008000000BD -:10D84000002C01401286000009000000002C01405D -:10D85000120601000A000000002C0140128601009F -:10D860000B000000002C0140120B02000C00000015 -:10D8700000440140128100000D00000000440140FE -:10D88000128110000E00000000340140120501005A -:10D890000F00000000340140128200001000000060 -:10D8A000002C01401206110013000000003401405A -:10D8B0001284100014000000004401401281000096 -:10D8C0001500000000480140128A00001600000008 -:10D8D0000044014012811000170000000048014080 -:10D8E0001281100018000000004401401281000065 -:10D8F0001900000000480140128100001A000000D9 -:10D9000000000040128101001D000000002C0140B9 -:10D91000128610001E00000000400140128100002D -:10D920001F00000000400140120101002000000023 -:10D93000002C01401282000021000000002C014058 -:10D940001202010022000000002C0140128201009E -:10D9500023000000002C01401202020026000000FB -:10D960000004004012820000270000000004004074 -:10D970001202010028000000000400401282010091 -:10D980002900000000040040120202002A000000EA -:10D9900000340140128410002B00000000340140CC -:10D9A000120411002C000000003401401284110008 -:10D9B0002D000000002C01401284100050000000D7 -:10D9C000002C014012861100FF0000000000000042 -:10D9D00000000000D40600200200000000440040C7 -:10D9E0001207000009000000003801401207000083 -:10D9F0000E00000000440040120700001300000069 -:10DA000000440040120700001600000000380140EA -:10DA10001207000019000000004800401207000033 -:10DA20001A00000000480040120700002400000017 -:10DA300000380140120700002A000000004C00409E -:10DA4000120500002C0000000050004012050000EC -:10DA5000FF000000000000000000000003000000C4 -:10DA600000440040120700000A0000000038014096 -:10DA7000120700000F0000000044004012070000E1 -:10DA800014000000004400401207000017000000CE -:10DA90000038014012070000180000000048004054 -:10DAA000120700001B0000000048004012070000A1 -:10DAB0002500000000380140120700002B00000084 -:10DAC000004C0040120500003200000000500040F1 -:10DAD00012050000FF000000000000000000000030 -:10DAE0000000000001020304010203040506070808 -:10DAF000090A0B0C0D0E0F10020304050607080996 -:10DB00000A0B0C0D0E0F101054494D3A20556E6B38 -:10DB10006E6F776E2074696D657220696E737461C3 -:10DB20006E6365005374617274000D0A0054656C75 -:10DB3000656772616D3A200054494D3A20556E6B0D -:10DB40006E6F776E20636C6F636B20736F7572639B -:10DB5000650054494D3A20556E6B6E6F776E207498 -:10DB6000696D6572204952516E004552524F523ACA -:10DB7000206174206C65617374206F6E652055415F -:10DB800052542070696E20686173206E6F2070653A -:10DB9000726970686572616C0A004552524F523A60 -:10DBA00020552853294152542070696E73206D69A5 -:10DBB000736D617463680A00AC000020232D302B64 -:10DBC0002000686C4C00656667454647003031327E -:10DBD00033343536373839414243444546003031D5 -:10DBE0003233343536373839616263646566000034 -:10DBF0000000000000000000000000000000000025 -:10DC00000000000000000000000000000000000014 -:10DC10000000000000000000000000000000000004 -:10DC200000000000000000000000000000000000F4 -:10DC300000000000000000000000000000000000E4 -:10DC400000000000000000000000000000000000D4 -:10DC5000B5C10008B981000841C40008A9D4000872 -:0CDC60006DD5000831D60008B5D60008CC -:04DC6C009581000896 -:10DC700000000000FF0000000000000000000000A5 -:10DC80000000000000000000000000000000000094 -:10DC90000000000000000000000000000000000084 -:10DCA0000000000000000000000000000000000074 -:10DCB0000000000000000000000000000000000064 -:10DCC0000000000000000000000000000000000054 -:10DCD0000000000000000000000000000000000044 -:10DCE000000000000000000000000000050000002F -:10DCF0000000000000000000000000000000000024 -:10DD00000000000000000000000000000000000013 -:10DD10000000000000127A00AC00002000000000AB -:10DD200010DC000830DC0008F0DB00080000000018 -:10DD300000000000000000000000000000000000E3 -:10DD400000000000000000000000000000000000D3 -:10DD500000000000000000000000000000000000C3 -:10DD600000000000000000000000000000000000B3 -:0CDD7000000000000000000000000000A7 -:040000050800C5B179 -:00000001FF +:020000040800F2 +:1080000000000120B1C5000801C6000801C6000833 +:1080100001C6000801C6000801C6000800000000F3 +:1080200000000000000000000000000001C6000881 +:1080300001C600080000000001C600083F830008D8 +:1080400001C6000801C6000801C6000801C60008F4 +:1080500001C6000801C60008CBB40008C5B400087A +:1080600001C60008BFB40008B9B4000801C6000882 +:1080700001C6000801C6000801C6000801C60008C4 +:1080800001C6000801C6000801C6000801C60008B4 +:1080900001C6000801C6000801C60008A3B4000814 +:1080A0000992000879920008F991000801C60008B9 +:1080B00069920008599200084992000873900008DC +:1080C0006D900008679000086190000801C60008E4 +:1080D00001C600085DA4000845A4000829A4000802 +:1080E0008BB400085195000801C6000801C60008BD +:1080F0001992000801C6000801C6000801C6000860 +:1081000001C60008000000000000000001C60008D1 +:108110000DA40008F1A30008399200082992000874 +:1081200001C6000801C6000801C6000801C6000813 +:1081300001C6000801C600080000000000000000A1 +:1081400001C6000801C6000801C6000800000000C2 +:10815000000000000000000000000000000000001F +:108160005B9000085590000801C6000801C6000891 +:1081700001C6000801C60008E991000801C6000810 +:1081800001C6000801C60008000000000000000051 +:0481900001C600081C +:1081940010B5054C237833B9044B13B10448AFF33D +:1081A40000800123237010BD0C010020000000009A +:1081B40038D700080C4B43B10C480D4910B5AFF348 +:1081C40000800C48036823B910BD0A48036833B91A +:1081D4007047094B002BF7D0BDE810401847064BF9 +:1081E400002BF5D0184700BF0000000038D7000866 +:1081F400100100200C01002000000000074A937CBD +:1082040000EB4000023318448318805C5B7813F061 +:10821400200F0ABF024B8018C01870476401002069 +:10822400D4060020074B9A7C00EB40001044034422 +:108234001A79052AC3BF044B9B1813F8060C0020B7 +:10824400704700BF6401002050D70008704790F8C1 +:10825400242062B1064A916949B1126C3AB9054ABF +:1082640092F80021520702D503685B6818477047EB +:1082740018030020D4060020002380F8243070471F +:1082840010B50446806C18B1036803211B68984735 +:10829400002384F8243010BD4B060D4B1B7803F0EB +:1082A4000C0363F04F0380F82C30836A80F830307D +:1082B40044BF02F03C0211431A12602380F83210CA +:1082C40080F82F2080F8313007212C3002F0DCBCFC +:1082D4001803002010B54FF48072044600210748AB +:1082E40004F0C4F9064A00231370012284F84C20D8 +:1082F400044A23651268626510BD00BF6401002052 +:108304006C020020D8070020044B41685A6891424F +:1083140004BF012283F88D2002F0C2B84406002075 +:10832400431C02D1002070470C3191F900305A1CD3 +:10833400F8D09842F8D148687047024A136801336C +:1083440013607047D807002008B5054901F033F9D8 +:10835400C0F3C43002280CBF1020002008BD00BFA9 +:10836400E8D7000808B5064901F025F9C0F3C43080 +:10837400023802289DBF034BC018807A002008BD34 +:108384000CD8000850D7000888B0072867D8DFE861 +:1083940000F004121E2A36424E5A324B5A6942F4F5 +:1083A40000325A615B6903F400330093009B4FF081 +:1083B400904008B070472B4B2B485A6942F48022F6 +:1083C4005A615B6903F480230193019BF1E7254B18 +:1083D40026485A6942F400225A615B6903F4002377 +:1083E4000293029BE5E71F4B21485A6942F480122D +:1083F4005A615B6903F480130393039BD9E7194B18 +:108404001C485A6942F400125A615B6903F4001370 +:108414000493049BCDE7134B17485A6942F4800236 +:108424005A615B6903F480030593059BC1E70D4B17 +:1084340012485A6942F400025A615B6903F400036A +:108444000693069BB5E7074B0D485A6942F4803200 +:108454005A615B6903F480330793079BA9E7002003 +:10846400A7E700BF001002400004004800080048CD +:10847400000C004800100048001400480018004890 +:10848400001C0048C0F30312012300F00F0003FA9C +:1084940000F051F82230034341F822307047C0F312 +:1084A400031300F00F0051F8233023FA00F000F01A +:1084B40001007047034B19681A68521A8242FBD3B1 +:1084C400704700BFD807002001207047024B1622D6 +:1084D4001A604FF0FF307047C4080020FEE72DE912 +:1084E400F74F504E8A4633684F4901930220154690 +:1084F400FFF716FF20B90025284603B0BDE8F08F2A +:108504004A4A002352F823105FFA83F911B109682B +:10851400884224D00133052BF4D1454C94F87430AF +:10852400042B1AD9434B50E090424DD193F90030BB +:1085340084F88930022384F888304FF41653A367F3 +:108544003B480023C4F88430E367C4F8803001F06A +:1085540040F894F87430042BCDD894F874901FFA32 +:1085640085F8324B53F8294094F86930202B51D1C7 +:10857400BAF1000F4ED0B8F1000F4BD094F8683028 +:10858400012B47D04FF0010384F868300023E366E1 +:10859400212384F86930D6F800B0A4F85080A4F8F8 +:1085A40052805746B4F852309BB2A3B94FF47A7351 +:1085B4005A464021204602F0B9F958BB202384F8DA +:1085C400693084F8680097E70C335A68002AABD105 +:1085D4004FF0FF33ACE7B4F85230013B9BB2A4F840 +:1085E40052305A464FF47A738021204602F09EF9A5 +:1085F40080B9A3682268B3F5805F07D123692BB9DA +:1086040037F8023BC3F308031385CBE73B781385A4 +:108614000137C7E73368019A9B1AB3F57A7FA0D371 +:1086240069E700BFD8070020D8D900082801002036 +:10863400080000205CDA0008002070470020704722 +:10864400012070474FF400534B60002070474FF0F7 +:10865400FF307047094B5968024609B90849596007 +:108664005868694602448A4289BF064B5A600C2204 +:108674004FF0FF3088BF1A60704700BF6C020020C3 +:10868400C8080020C408002030B50B4B0412997DA3 +:10869400C2B21B33012088420ADC13F8025CAC42EC +:1086A40003D113F8015CAA4204D001300233F2E78B +:1086B4004FF0FF3030BD00BF640100202DE9F04FC2 +:1086C400344C637CA57C1F19E35C85B05B0002908D +:1086D4000E4690460193FFF7D7FFB0F1000A56DB30 +:1086E400AB1C07F1010903934FF0030B019BA9EBAA +:1086F400070293424BDB99F80020924532D199F856 +:108704000150039B1BFB05322244802E527801D07A +:10871400402E30D102F01402142A23D12846FFF748 +:108724006DFD01462846FFF77DFD00281DDDC21DB5 +:1087340042440139801A12F801E901F801EFC242FA +:10874400F9D1A27C144B22445278D2180AD015F0E5 +:10875400010F4FEA650508BF0820515D18BF80204E +:108764000143515509F10209C0E798F8072002F0C6 +:108774003F020A70E5E7002EF4D102F00C020C2A45 +:10878400F0D101220299284600F008F8EAE705B082 +:10879400BDE8F08F64010020D4060020F8B5174628 +:1087A40004460D46FFF72AFD06462046FFF73AFD2C +:1087B400194BBC221A710A12DA7119720022411C77 +:1087C400974201F00F019A7261F01F0114BF402219 +:1087D400802204465972DA7258B1013830440B339E +:1087E400211A4218002A09DD10F8012903F8012F83 +:1087F400F7E7317801F03F010A43DA722846802115 +:10880400064AFFF75BFF04F1080189B20348BDE89B +:10881400F84002F039BA00BFEC050020F005002052 +:1088240090F824302DE9F74F0446002B00F0BB806C +:10883400FFF70DFD94F84C30DFF89881002B7DD0C4 +:108844005E4B1B6C002B79D15D4E626D33689B1AB5 +:10885400226D934269D35B4B9A7CD1184F785A4965 +:108864007F1862D0D8F8085093F811E0D15C0191D8 +:1088740005EB45009E44104418440EF101091633DB +:10888400019A954280F289804FEA650C15F0010A3D +:1088940017F80C100A4618BF0A1102F0030BBBF1BB +:1088A400030F21D1BAF1000F14BF6FF0300A6FF03B +:1088B400030A01EA0A0107F80C109EF800C04946B1 +:1088C4000EEB4C0C8C450FD991F801A055450ED1F7 +:1088D40009784900013101EB030A11F803C09AF841 +:1088E400011051EA0C2104D101350330C8E70231EB +:1088F400E8E790F803C01CF0040FF5D012F004026E +:1089040044D03248BC2303710B12C371E123437278 +:10891400002301728372C3720821043002F0B4F997 +:1089240001353368C8F808506365636D3268D21A3C +:10893400B2F5FA6F24BF03F57A736365A36A3BB19A +:108944001F4BA26B1B689B1A41F2707293422DD885 +:1089540098F800303BB3194B93F828201ABB1A6CD3 +:108964000ABB9B69FBB9A36AEBB9194A144910689D +:108974000B68B0B11B1A002B15DDA06C98B103680D +:108984001B68022103B0BDE8F04F18471CF0400FEC +:10899400AAD02846FFF702FFC2E70023C8F8083030 +:1089A400C3E73233136003B0BDE8F08F00228121A6 +:1089B4002046FFF771FC0023A362C9E718030020D7 +:1089C400D807002064010020D4060020EC05002014 +:1089D400640200206C0200202DE9F743B24E96F8A1 +:1089E4000680F2783779F5794FEA082303F4407367 +:1089F4002B4357EA0227044699B22ED1AB4A92F888 +:108A04000021D0070ED5C02B11D13389A84A5BBAF7 +:108A14009BB21912D1751376A64A336201211170E3 +:108A2400A54A17600023B36103B0BDE8F083B1F534 +:108A3400807FF7D13378E77103F00C0363F04F03C1 +:108A44002371E12363720123A37240232772E3722B +:108A54000821201D02F018F9E4E796F90520002A00 +:108A640008F0C30EC0F25981326A9742DAD11EF07F +:108A7400800F7778B27843D042EA072718F0400293 +:108A840024D0BEF1C20F14D190F83C206AB1826A9E +:108A940097420AD1036C08F03C029A4205D0436B1A +:108AA4000264043303F03C034363012384F83C3041 +:108AB400B8E7BEF1C30FF8D1826A9742F5D100221C +:108AC4008121FFF7E9FB0023A362EEE7B8F1800FF1 +:108AD4000ED1826A002AA5D1784987620968816328 +:108AE4004FF0FF33426380F83C200364726299E7DD +:108AF400B8F1810F96D1816A8F4293D18262F5E7F2 +:108B0400806A42EA072282428CD16C4A1268A263CC +:108B140001F470720020B2F5007FA0725AD012D80E +:108B2400B2F5C07F22D0B3F5607F09DB40F2813219 +:108B3400934240F3C78040F2D132934200F0E08088 +:108B4400C32100251FE0B2F5207F43D0B2F5407F5A +:108B5400E9D18A06F4D1632363724323A3724023C9 +:108B6400E37200232373122363730AE06422337ACB +:108B7400627265F03F054122A272E572237360734D +:108B8400A073C221012508F03C022046FFF784FBB4 +:108B9400002D3FF447AF3378617A03F00C0363F0A0 +:108BA4004F032371A36A23721A12A37AE27113F09A +:108BB400400217BF626B84F83C2023F03C0313434C +:108BC40018BFA37201F00F011CBF012384F83C30CD +:108BD40007313EE73789B2F5207F7FBA05F00F05EC +:108BE400BFB240F0C380606C30B1036839469B6803 +:108BF4009847002840F09C80A7F58070FF283B46EA +:108C04000DD82B4B2E492A46184404F0D5FA294B8B +:108C140001221A70284B00221A60C22191E7FF2F0B +:108C2400FBDC602F06D9214825492A46384404F044 +:108C3400C3FAF2E75A195F2AF5D90A35204A1B49C3 +:108C440035449542E9D0602B107814BFC85481F89C +:108C5400000101320133F4E7FF2F72DD606C28B1AB +:108C6400036839469B689847002873D105F163036C +:108C740063724223A37245F040053B12E5722373ED +:108C840067737EE779195F2960D9084912F1FF32C9 +:108C9400E4D3602B14BF11F803E091F800E180F8ED +:108CA40000E001330130F1E718030020D40600206E +:108CB400640100206C02002064020020D807002018 +:108CC40022030020CB0708D5327A737A2AB9FF2B06 +:108CD40002BF4FF080532E4A1A60A06C18B103688B +:108CE40001211B689847BFF34F8F2A492A4BCA6852 +:108CF40002F4E0621343CB60BFF34F8F00BFFDE784 +:108D0400622363724323A372D223E3720023237387 +:108D140037E7002384F82C3084E6BEF1000F7FF49B +:108D240081AE324601F470713846FFF7C7FC79E62C +:108D3400606C1A4A03683946D3F800902B46C8473A +:108D44005AE7602F04F10E002A469BD914492A469B +:108D5400394419E0606C002203680092D3F804904F +:108D640004F10E022B463946C8477FE7B2F5007F6F +:108D7400CFD1A7F58071FF293B463FF66DAF094B74 +:108D84002A46194404F10E0004F016FA66E700BFFF +:108D940055B01F5E00ED00E00400FA052203002038 +:108DA400D406002064010020044B01685A689142F3 +:108DB40004BF012283F88C2001F072BB440600201A +:108DC4002DE9F04106460D4601EB02080C464445E8 +:108DD400A4EB050707D0336814F8011B1B68304661 +:108DE40098470028F3D13846BDE8F081B0F89A30AE +:108DF400013303F03F03A0F89A30B0F89A30B0F88A +:108E040098009BB298424FF0FF3018BF0020704783 +:108E1400037C33B1B0F82C21B0F82E319BB29A42C6 +:108E2400FAD17047B0F82421B0F8263192B29A42B0 +:108E34001FBFD0F82021D25C013303F03F031ABFD7 +:108E4400A0F8263110464FF0FF307047B0F82431B7 +:108E5400B0F826219BB293421ABFD0F82031985C17 +:108E64004FF0FF307047B0F82431B0F82601181ADB +:108E740000F03F00704738B5012303740446B0F88E +:108E84002C01C31803F03F03B4F82E2192B293428D +:108E9400FAD0D4F82821194D115494F80821A4F8D3 +:108EA4002C3155F8221091F8693091F86A100B436F +:108EB40003F02103212B1ED0114B124943F8221039 +:108EC40003EB820304F194025A610021022294F913 +:108ED4001E0101F049FA94F91E0101F079FAB4F87F +:108EE4002E11D4F8282194F8083112FA81F155F89A +:108EF400230002F0A7FA012038BD00BF280100209A +:108F040078020020F18D000838B5044620B390F8AB +:108F14007420124B53F8220090F8693090F86A20BC +:108F2400134303F02203222B16D0214611F8755F58 +:108F340001F0BAFCB4F89030B4F89220013303F095 +:108F44003F03934208D0B4F89020D4F88C1092B226 +:108F54009BB28D54A4F8903038BD00BF2801002086 +:108F6400FEE770B505460C46084649B102F00EFB13 +:108F74002B680246214628465B68BDE870401847C6 +:108F840070BD30B58BB00B4600218DF82410012A3A +:108F9400D8BF0A220DF12304B3FBF2F505FB12330B +:108FA40003F0FF03092B94BF30333733DBB2214680 +:108FB40004F801392B46002DEED1FFF7D2FF0BB098 +:108FC40030BD2DE9F84306460C46071D00F107099C +:108FD400002517F8011B0A222046FFF7D2FF236859 +:108FE40080461B682E2120469847B94540440544D5 +:108FF400EFD10A22F1792046FFF7C3FF2844BDE8E8 +:10900400F88300BF0368DA69D20782B003D59B8C6A +:109014009BB2019308E0DA699007F8D4DA69510742 +:10902400F5D4DA691207F2D4019B02B0704710B587 +:1090340048B10649002351F82340A042DAB203D0D4 +:109044000133052BF7D10522104610BD280100205D +:10905400002001F059B9002000F07CBD002001F08F +:1090640053B9002000F076BD002001F04DB9002076 +:1090740000F070BD436D10B5044663B190F8502004 +:10908400012A08D190F82810C1F1200111F0FF0144 +:1090940001D05C3098472046BDE8104000F061BD27 +:1090A40038B583689A42044650D101294FD1002330 +:1090B40080F87C3080F85030836D03B1984794F881 +:1090C400413003F02803282B40D194F87C50002D24 +:1090D4003CD00521204602F067F994F84030012B7A +:1090E40034D0012384F8403094F841302A2B03D142 +:1090F4000221204602F058F9292384F84130202324 +:1091040084F842302346002153F85C2B6164506894 +:1091140020F40040506065856362638D23854FF0C1 +:109124000073E36290692C4B6363C50344BF08205A +:10913400D06184F8401029498B424AD028498B4297 +:109144000CBFB823FA2311680B43136038BD90F8A1 +:10915400413003F028030125282B80F85050F5D125 +:10916400062102F021F994F84030AB42EED094F895 +:10917400413084F84050292B03D12946204602F07F +:1091840013F92A2384F841302025234684F84250D9 +:1091940053F85C2B00216164506820F40040506057 +:1091A40065856362638D23854FF00073E362906984 +:1091B400094B6363C0035CBF0820D06184F840108E +:1091C40006498B4205D006498B420CBFB823FC23C9 +:1091D400B9E7B823B7E700BFFB9F0008ABA10008BD +:1091E4008D9F0008024BD86C08B101F0A1BF7047F5 +:1091F400A0020020024B186C08B101F099BF70471F +:10920400A0020020024B986B08B101F091BF704797 +:10921400A0020020024BD86908B101F089BF704751 +:10922400A0020020024B986908B101F081BF704789 +:10923400A0020020024B586908B101F079BF7047C1 +:10924400A0020020024BD86808B101F071BF70473A +:10925400A0020020024B986808B101F069BF704772 +:10926400A0020020024B586808B101F061BF7047AA +:10927400A002002010B5064C206808B101F058FF88 +:10928400E06B18B1BDE8104001F052BF10BD00BF43 +:10929400A0020020836C2BB1027F012A02D100219D +:1092A40004381847704708B5431C12D0214B984224 +:1092B4003DD019D8A3F59233984230D00BD8B0F1F1 +:1092C400804F28D0A3F58063984226D01A4803F033 +:1092D400ABFC002008BD194B984222D003F58063F3 +:1092E4009842F3D1372008BD154B98421FD009D8B6 +:1092F400A3F58053984216D003F540639842E5D114 +:10930400182008BD0F4B98420FD003F50063984214 +:10931400DCD14E2008BD1C2008BD1D2008BD1E2028 +:1093240008BD362008BD2C2008BD1A2008BD192010 +:1093340008BD00BF002C014052DB000800100040B3 +:1093440000440140004801400368594A93428CB0EC +:109354002CD102F56442916941F4006191619269F2 +:1093640002F400620192019A524A1060524A9342F6 +:109374003CD102F5FE32D16941F02001D161D269BC +:1093840002F020020692069A4A4A90614B4A93429E +:109394003BD14B4B9A6942F480129A619B6903F466 +:1093A40080130B930B9B434BD8642EE0B3F1804F97 +:1093B4000CD1434AD16941F00101D161D26902F073 +:1093C40001020292029A3B4A5060CFE73D4A93421F +:1093D4001DD102F50332D16941F00201D161D26994 +:1093E40002F002020392039A324A9060364A934290 +:1093F4001DD1334B9A6942F400529A619B6903F47C +:1094040000530793079B2B4BD8610CB070472F4A2E +:1094140093421CD102F50232D16941F00401D161B9 +:10942400D26902F004020492049A224AD060284AC3 +:1094340093422CD1224B9A6942F480329A619B69FF +:1094440003F480330893089B1A4B9863DDE7214AA1 +:1094540093428BD102F50032D16941F01001D16100 +:10946400D26902F010020592059A124A50611A4A12 +:1094740093428BD1124B9A6942F480229A619B6980 +:1094840003F480230A930A9B0A4B1864BDE7134A2A +:109494009342ECD10A4B9A6942F400329A619B6977 +:1094A40003F400330993099B024BD863ADE700BF73 +:1094B400002C0140A0020020001400400050014094 +:1094C4000010024000040040003401400008004045 +:1094D40000400140001000400048014000440140A9 +:1094E4001549D1F8883043F47003C1F88830134B20 +:1094F4001A6842F001021A605868114A02405A6020 +:109504001A6822F0847222F480321A601A6822F4F3 +:1095140080221A605A6822F4FE025A60DA6A22F043 +:109524000F02DA62186B074A02401A6300229A603B +:10953400054B8B60704700BF00ED00E00010024057 +:109544000CC07FF8CCFC00FF00800008104A936D2B +:10955400996811F4805F114607D0DA68D20541BFDB +:10956400DA68D2B262F4C072DA609A68900407D5FD +:10957400DA68920541BFDA68D2B262F42072DA6026 +:10958400044B4FF400325A61012381F8753070475F +:10959400A002002000040140431C04D00C3111F946 +:1095A4000C3C5A1C01D100207047984201F10C0177 +:1095B400F5D101207047431C04D091F900305A1CA6 +:1095C40001D118467047984201D1886870470C3120 +:1095D400F3E72DE9F04104468AB080B1B94990F926 +:1095E4008800FEF79DFE94F98950B749064628463F +:1095F400FEF796FE00B12EB9B44803F015FB0AB08D +:10960400BDE8F081864218BF0020206008B9B04848 +:10961400F3E7B04B984240F0BB8003F55843DA6857 +:1096240042F48042DA60DA6822F48042DA609A69AD +:1096340042F480429A619B6903F480430093009B47 +:10964400002384F87430252384F88A30C5F303108A +:10965400FEF79AFE94F989509B4980462846FFF705 +:10966400AAFF012700FA07F300F0070203F0100332 +:109674001343069305F00F05C0F30113C0F306204E +:1096840007FA05F505A907930990032340460893B3 +:10969400059500F099FA94F98800C0F30310FEF7D9 +:1096A40073FE94F98880874905464046FFF783FF97 +:1096B400430000F0070208F00F08C0F3011003F0A4 +:1096C4001003134307FA08F7079005A928460597DE +:1096D400069300F079FA0C2094F874207E4BA06174 +:1096E4000020261DE0612062A062606294F86D0093 +:1096F40043F822602568A16FE26FD4F88070D4F833 +:1097040084306560A160E2602761636100F0FF0E50 +:1097140008B984F86CE0242084F86D00286820F0EF +:1097240001002860D5F800E06C481A4342F00C02AE +:109734000EEA000002432A606B6823F440533B4363 +:109744006B60AB6823F43063AB60624B9D4240F0C6 +:109754008C8003F55843624A1B6B03F00303D35C0C +:10976400082B3FF64CAF01A252F823F08F98000863 +:1097740079990008B3980008039600087D9A0008B8 +:10978400039600080396000803960008839A0008CD +:10979400544B984217D103F5E6331A6942F4003268 +:1097A4001A611A6922F400321A61DA6942F4003249 +:1097B400DA61DB6903F400330193019B012384F82C +:1097C4007430262340E7484B984217D103F5E4331D +:1097D4001A6942F480221A611A6922F480221A61F9 +:1097E400DA6942F48022DA61DB6903F480230293AC +:1097F400029B022384F87430272325E73B4B9842CD +:1098040017D103F5E2331A6942F400221A611A6986 +:1098140022F400221A61DA6942F40022DA61DB6977 +:1098240003F400230393039B032384F87430342349 +:109834000AE72F4B98427FF409AF03F5E0331A6926 +:1098440042F480121A611A6922F480121A61DA69E8 +:1098540042F48012DA61DB6903F480130493049BFD +:10986400042384F874303523EEE61E4B9D4241D127 +:1098740003F5E6331B6B03F44033B3F5803F00F08C +:10988400FB800CD8002B7FF4BAAE01F049FDA3682D +:1098940000EB5300B0FBF3F080B2E86074E0B3F582 +:1098A400003F00F0EC80B3F5403F7FF4A8AE4B08D6 +:1098B40003F5F40303F59053B3FBF1F39BB2EB60B0 +:1098C40062E000BFD8D900085CDA00086ADB00084F +:1098D4009ADB00080038014028010020F369FFEFFB +:1098E4006DD700080044004000480040004C004090 +:1098F400005000406A4B9D4210D103F5E4331B6BCA +:1099040003F44023B3F5802F00F0B680BAD9B3F541 +:10991400002F00F0B480B3F5402FC6E7614B9D42A1 +:1099240010D103F5E2331B6B03F44013B3F5801F2E +:1099340000F0A280A6D9B3F5001F00F0A080B3F513 +:10994400401FB2E7584B9D427FF459AE03F5E03314 +:109954001A6B02F44002B2F5800F00F08D8002D839 +:10996400002A92D04BE6B2F5000F00F08880B2F5E1 +:10997400400F9AE701F0BCFCA36800EB5300B0FB76 +:10998400F3F04A4B80B2D860A26A6368002A48D0D8 +:10999400D70705D55968E06A21F4003101435960BD +:1099A400950705D55968206B21F48031014359602E +:1099B400500705D55968606B21F480210143596033 +:1099C400110705D55968A06B21F400410143596082 +:1099D400D70605D59968E06B21F48051014399605D +:1099E400950605D59968206C21F4005101439960CE +:1099F40050060ED55968606C21F480110143B0F50E +:109A0400801F596005D15968A06C21F4C00101433D +:109A14005960110605D55A68E16C22F400220A4304 +:109A24005A605A6822F490425A609A6822F02A02D4 +:109A34009A601A6842F001021A60002222671C4AE6 +:109A440015681B681A071ED463681B685B070AD570 +:109A54006FF07E432A464FF48001304600F066FFE3 +:109A640000287FF4CCAD202384F86D3084F86E3068 +:109A7400002384F86C30C2E501F06AFC07E74B0868 +:109A840003F5004318E76FF07E432A464FF40011B4 +:109A9400304600F04BFF00287FF4B1ADD4E700BF9F +:109AA40000480040004C0040005000400038014095 +:109AB400D807002038B5002580F8425090F841308E +:109AC400292B04460AD1282380F84130212303633B +:109AD400012101F069FC84F8405038BD90F8413010 +:109AE4002A2BFAD1282380F8413022230363022150 +:109AF400EFE7174B10B52022C362002303634363CF +:109B040080F8412080F842304B07044611D5436A5F +:109B14005A1C42620268526A1A70038D4BB1013BAF +:109B24000385438D013B9BB24385436C43F004039F +:109B340043642046072101F037FC23681022DA61D0 +:109B4400002384F840302046BDE81040FFF792BA65 +:109B54000000FFFF0268436B9169126803B1184764 +:109B6400704790F84130202B0BD10268282380F8ED +:109B74004130054B4363136843F0B803136000207E +:109B840070470220704700BFFB9F00080C4B5A69C6 +:109B9400024213D058610023012803F1010202D0CC +:109BA40040081346F8E71422DBB25343054A181D54 +:109BB4001044816811B11A4413691847704700BFF3 +:109BC40000040140780300202DE9F74FD1F800800C +:109BD400DFF89091614A002338FA03F402D103B00C +:109BE400BDE8F08F4FF0010E0EFA03FE18EA0E06E0 +:109BF40000F0A6804C6824F01007022F16D14FEA1B +:109C0400D30A00EB8A0A03F0070BDAF820504FEA74 +:109C14008B0B4FF00F0C0CFA0BFC25EA0C0C0D69A6 +:109C240005FA0BF545EA0C05CAF820504FEA430A39 +:109C34000325D0F800B005FA0AF5ED4304F0030C4F +:109C44000BEA050B0CFA0AFC013F4CEA0B0C012F42 +:109C5400C0F800C011D8876807EA050BCF6807FA77 +:109C64000AFC4CEA0B078760476827EA0E0EC4F328 +:109C740000179F4047EA0E074760C7683D408F685A +:109C840007FA0AF73D43C560E50059D5D9F81850DD +:109C940045F00105C9F81850D9F8185023F0030706 +:109CA40007F1804705F0010507F58037019503F0BA +:109CB400030C019DBD684FEA8C0C4FF00F0E0EFA99 +:109CC4000CFEB0F1904F25EA0E0E3BD0244DA84275 +:109CD4003AD005F58065A84238D005F58065A842DC +:109CE40036D005F58065A84234D005F58065A842D4 +:109CF40032D005F58065A84214BF0725062505FA6C +:109D04000CF545EA0E05BD601568F74314F4803F71 +:109D14000CBF3D4035431560556814F4003F0CBF3B +:109D24003D4035435560956814F4801F0CBF3D4099 +:109D340035439560D568A40254BF3D403543D56092 +:109D4400013349E70025DAE70125D8E70225D6E7FC +:109D54000325D4E70425D2E70525D0E70004014014 +:109D6400000400480010024010B590F82120022A97 +:109D740003D004228263012010BD0268C46B116801 +:109D8400436B21F00E011160116821F00101116093 +:109D9400026C012101FA02F26260002480F82110B1 +:109DA40080F8204013B19847204610BD184610BDD6 +:109DB4000023354A10B580F84230C2624385436CB3 +:109DC4001943416490F84130282B044607D090F899 +:109DD4004130292B03D090F841302A2B22D1032182 +:109DE400204601F0E1FA282384F84130002323635C +:109DF400264B6363236819681A6811F480411ED0E6 +:109E040022F48042A06B1A60214B4363002384F840 +:109E14004030FFF7A9FF002835D0A06B436BBDE8A5 +:109E240010401847072101F0BFFA94F84130602B25 +:109E34001CBF202384F8413000232363D9E712F4A4 +:109E440000420DD01A68E06B22F400421A60104BF5 +:109E5400436384F84010FFF787FFA0B1E06BDDE7B0 +:109E640094F84130602B05D1202384F8413084F8E4 +:109E7400402010BD94F8513084F840201BB920468E +:109E8400BDE810406DE610BD0000FFFFFB9F000819 +:109E9400A9AD000838B5036820220446DA61082217 +:109EA400DA610D46072101F07FFA23685A6842F40B +:109EB40000425A605A6822F0FF7222F48B3222F474 +:109EC400FF7222F001025A60204601F09BFA2268D8 +:109ED4001368580402D41368190409D594F841305E +:109EE400212B0CBFA36BE36B1B685B689BB2638580 +:109EF400638D9BB21BB1636C43F0040363646B0713 +:109F040010D5636A526A591C61621A70238D4BB171 +:109F1400013B2385638D013B9BB26385636C43F0F6 +:109F2400040363640023236384F842306363636C33 +:109F340063B1616C2046FFF73BFF94F84130282B56 +:109F44001FD129462046BDE83840D2E5E26A12F521 +:109F5400803F0BD00B4AE262202284F8412084F82F +:109F640040302046BDE83840FFF784B894F84120DB +:109F740084F84030222A4FF0200284F8412000D196 +:109F840038BD38BD0000FFFF10B590F84030012BFC +:109F940004462ED0012380F84030C80619D5D306D4 +:109FA40017D594F841302268212B0CBFA36BE36BC7 +:109FB4001B685B68B3FA83F310215B09D1611BB1A1 +:109FC400002084F8400010BD636C43F00403636414 +:109FD400F6E7080705D5130703D523680822DA61D5 +:109FE400EEE78806ECD59306EAD52046FFF752FF44 +:109FF400E6E7022010BD2DE9F84390F84030012B2C +:10A0040004460E46174600F0CB800123CD0680F8A7 +:10A0140040303ED5D0063CD5638D9BB28BBBE36A02 +:10A02400B3F1007F03D0E36AB3F1007F14D194F855 +:10A034004130282B10D131462046FFF75AFDB20695 +:10A0440005D5BB0603D531462046FFF723FF002084 +:10A0540084F84000BDE8F883E36A13F5803F236881 +:10A064000CD094F84120292A08D11022DA61204624 +:10A0740001F0C8F92046FFF71DFDE0E71022DA6180 +:10A08400DDE723681022DA61636C43F00403636440 +:10A09400D5E771071AD57A0718D5638D9BB263B1DA +:10A0A400636A5A1C62622268526A1A70238D013BE9 +:10A0B4002385638D013B9BB26385638D9BB2002B2B +:10A0C400BDD1E36A13F5803FD4D1B8E7330746D551 +:10A0D4003D0744D594F8413003F02803282B236826 +:10A0E40037D19A69E1689D6902294FEA1545C2F39F +:10A0F400004805F0FE059A68D3F80C9020D1C2F30D +:10A10400090285EAD21515F006050FD1A16C0131BB +:10A11400A164A16C022992D10821A564D96184F8B3 +:10A12400405041462046FEF7BBFF88E70421204605 +:10A1340001F03AF9002384F8403009F0FE02F0E718 +:10A144000421204601F030F9002384F840302A46E7 +:10A15400E7E70822DA61002384F840306FE7B007AC +:10A164007FF56DAFB9077FF56AAF638D9BB26BB1B5 +:10A17400636A2268591C61621B789362638D013B98 +:10A184009BB26385238D013B238558E7E36AB3F1D2 +:10A19400807F3FF46FAFE36A002B3FF46BAF4EE771 +:10A1A4000220BDE8F88370B590F84030012B0446D6 +:10A1B40000F0A480012380F8403011F010001DD07D +:10A1C400D3061BD522684E491023D361636C43F038 +:10A1D40004036364636B8B420ED04A498B420CBF09 +:10A1E4006023F42311680B431360204601F00AF93D +:10A1F400002084F8400070BD6023F3E70E063ED5CE +:10A2040055063CD523681A6822F040021A60628D14 +:10A2140092B2002A2ED05A68618D89B2FF2999BF63 +:10A22400618D2185FF2121855968208D21F07F7161 +:10A2340021F45831C2F30902C6B221F47F7142EA13 +:10A24400064221F003018CBF4FF080754FF000757A +:10A254000A432A435A60628D121A92B2628594F8B4 +:10A264004120222A1A680CBF42F4004242F4804280 +:10A274001A60BDE740212046FFF79AFDB8E789063A +:10A28400B6D59306B4D523682022DA615A6822F041 +:10A29400FF7222F48B3222F4FF7222F001025A6020 +:10A2A400002222636263A2F58032E26228B11022A6 +:10A2B400DA61636C43F004036364204601F0A2F89E +:10A2C4000321204601F070F8636C1BB994F8412017 +:10A2D400602A01D1616CCEE794F84120212A07D18C +:10A2E400202284F8412094F8422084F842307FE709 +:10A2F40094F84120222A7FF47BAFF1E7022070BD5D +:10A30400ABA100088D9F0008036899691A6810B40E +:10A31400CC0508D5140606D5446C44F00104446405 +:10A324004FF48074DC614C0508D5140606D5446CE2 +:10A3340044F0080444644FF48064DC61890508D562 +:10A34400120606D5426C42F0020242644FF40072D7 +:10A35400DA61436C13F00B0F03D0416C5DF8044BCE +:10A3640026E55DF8044B7047174B30B5DC68C4F341 +:10A374000224C4F10703251D042B28BF0423062D42 +:10A384004FF0010505FA03F303F1FF338CBF033CDF +:10A3940000241940A14005FA04F4013C224000289D +:10A3A40042EA01024FEA0212ADBF00F1604000F040 +:10A3B4000F00064B00F56140B5BFD2B2D2B21A54B9 +:10A3C40080F8002330BD00BF00ED00E014ED00E094 +:10A3D400002808DB4209012300F01F0003FA00F003 +:10A3E400014B43F82200704700E100E0044B4FF4B6 +:10A3F4000012C3F88421034B186900F0ECBC00BFC1 +:10A4040000E100E028010020044B4FF48012C3F85F +:10A414008421034BD86800F0DEBC00BF00E100E0FB +:10A4240028010020044B8022C3F88421034B986840 +:10A4340008B100F0D0BC704700E100E02801002022 +:10A44400034B4022C3F88421024B586800F0C3BC7C +:10A4540000E100E028010020034B2022C3F88421FE +:10A46400024B186800F0B7BC00E100E028010020AE +:10A474000A4AD3695B684FF6FE71D962D96841F024 +:10A484000201D960D96821F00101D9604FF6FF714A +:10A494009963002382F82830536270471803002020 +:10A4A4002DE9F041A44CE36903204FF6FF75022621 +:10A4B400002794F82820092A00F2C881DFE812F066 +:10A4C4000A00A1001900EE003E014E016201920152 +:10A4D400BA01A401E36993F88C30002B00F0898061 +:10A4E4000023636384F85C30FF2263620123A26566 +:10A4F4006365E26992F88C30002B66D1626D1AB103 +:10A50400A36DB3FA83F35B090022626294F85C20C2 +:10A51400002A36D1656B072D22DD002B51D0E2785D +:10A52400207940EA022094F90520002A10DA18B1B3 +:10A53400FEF7AAF8C343DB0F94F8BC2452070CD4EB +:10A54400A56194F8BC349B071BD5CC23636218E047 +:10A55400236AC11A4B424B41EEE793B1A561F4E77C +:10A56400012D2ED194F83830A363CC2B03D0E26AAA +:10A57400236B9A4205DD226C1AB1E36A002B00F3C7 +:10A584006881E369606A5B6841F2E83140F2314214 +:10A59400002808BF0A46DA626FF001021A61DA681D +:10A5A40042F00102DA606FF002021A61DA6842F0E6 +:10A5B4000202DA60002384F85C30032384F8283034 +:10A5C40017E00023A3610C23C0E7536840F2424222 +:10A5D400DA626FF0010100225A621961D96841F010 +:10A5E4000101D960012384F82830A263E264A364E2 +:10A5F4002365E369002283F88C2083F88D20BDE86D +:10A60400F081E56995F88DE0BEF1000F38D16B68F3 +:10A614005F6BE36C03F14402BA4218DA6833A16C4D +:10A62400A06B226DE364002603F1440847459C4671 +:10A6340030DDB1F5807F03F1680324DD26B1C4F871 +:10A644004CC0A164A0632265A36C5B00A364BEF14B +:10A65400000FCED0636D226D1340626B6365172AC1 +:10A6640006DC531CA16B6363A36DA1544B40A3652B +:10A67400022384F828306B684FF4D072DA62B8E7AA +:10A6840040F24247C5E7B2FA82F208435209490050 +:10A694000126C9E7002ED7D0E364D2E793F88C20D3 +:10A6A40012B184F8287004E7636AC3B1E2636822D4 +:10A6B400E3695B689A632332DA626FF001021A611C +:10A6C400DA6842F00102DA606FF002021A61DA68B5 +:10A6D40042F00202DA600023636304236EE7E16A56 +:10A6E400226B914206DD226C1370626C22646364F7 +:10A6F400E362E363236C002B00F0A8801A78C2F3B2 +:10A704008101682201FB0222597901F00F0108310D +:10A71400E163E16A0129CBD1197801F0DF011970F5 +:10A72400E36B206C013BC15C81F02001C154E36AFE +:10A734000133E362BCE700BF1803002093F88C20C8 +:10A7440062B15A68D2F824E0916B0A398E4503D27B +:10A75400956384F82860ACE605232FE7636AF3B1B8 +:10A76400636AA363A36B002101221A424FEA4202E7 +:10A774001CBF83F480730121FF2AF6DD01B1A363BA +:10A784000123A364A06BA26C682310424FEA420128 +:10A794000BD0B2F5807F08DC0A466833F5E7636BBB +:10A7A400226C591C6163D35CDBE7682B0CBF062267 +:10A7B4000722B1F5007FA16484F8282054DD616B81 +:10A7C400E26B914203F59C7302DA626A002A48D074 +:10A7D400092247E0D16821F00201D1606FF0020143 +:10A7E40011614EE0E3695B68596B9A6B453A91429B +:10A7F40008D24FF6FF729A63012384F8283084F854 +:10A804005C30F6E60623D9E6E3695B6841F2504220 +:10A81400DA626FF002021A61DA6842F00202DA6068 +:10A82400636A1BB1002363620823C7E6E36A01334A +:10A83400E362F9E793F88C202AB15A68526BB2F5B7 +:10A84400936F7FF6D6AE84F8280032E6FFF710FE49 +:10A85400CFE600231370626C22646364E362E363F3 +:10A864008FE6052284F8282094F82820072AE26934 +:10A874005268AFD16FF002011161D16841F0020159 +:10A88400D16094F82820092A0BBFE269E169526873 +:10A89400496813BFA3F123024FF6FF7191638A63E2 +:10A8A400E2695268D362A4E690F86A30202B3BD167 +:10A8B400002937D090F86830012B35D0012380F877 +:10A8C4006830A0F85830A0F85A3083684165B3F571 +:10A8D400805F19D10369ABB940F2FF13A0F85C3073 +:10A8E40000232222C36680F86A20026880F8683058 +:10A8F400916841F001019160116841F49071116017 +:10A9040018467047FF23E9E723B90369002BF9D000 +:10A914007F23E3E7B3F1805FE2D10369002BF7D033 +:10A924003F23DBE7012070470220704770B50468BD +:10A93400104EE56931EA050501D1002070BD5D1CAA +:10A94400F7D08BB9236823F4D0732360A36823F072 +:10A954000103A360202380F8693080F86A30002363 +:10A9640080F86830032070BDD6F8C054AD1AAB42ED +:10A97400DFD2E7E71803002010B507490446042096 +:10A98400FDF78DFD30B14FF0904310220CB19A6168 +:10A9940010BD9A6210BD00BFDC0700202DE9F04114 +:10A9A4003B2890B056D8A84C204490F93850681CE5 +:10A9B40050D0A6492846FDF772FD4FEA151805F058 +:10A9C4000F0798B304F174012846FEF7E5FD00284B +:10A9D40043D004F174012846FDF7A2FC0446C8B133 +:10A9E4002846FDF7B1FC30F0100213D12368012191 +:10A9F40001FA00F023EA0000206002238DF804101D +:10AA040093498DF80430CB6923F00053CB61049251 +:10AA14008DF804208D4908F00F00012303FA07F292 +:10AA240051F8203023EA020341F8203008F00F00E7 +:10AA3400FDF7AAFC012303FA07F7032201936946F1 +:10AA44000023009703920293FFF7BEF8814928463A +:10AA5400FDF718FD10B0BDE8F08104F19801284617 +:10AA6400FEF79AFD0028D5D004F198012846FDF799 +:10AA740057FC04460028CDD02846FDF773FC142863 +:10AA8400064600F2DC80744BC340D907C2D5734933 +:10AA94002846FEF790FDC202236A4CD5042202FA2E +:10AAA40006F623EA06062662236A2362226A41F234 +:10AAB40011131A4208D1226A40F244431A4202BFD7 +:10AAC400636C23F400436364226A41F211131A4253 +:10AAD40008D1226A40F244431A4203D1236823F086 +:10AAE4000103236002238DF83D30226A41F21113E1 +:10AAF4001A4208D1226A40F244431A4202BF236830 +:10AB040023F001032360564B9C4253D1504A93696E +:10AB140023F400639361534B9C425CD14C4AD36948 +:10AB240023F02003D361504B9C425CD1484A936983 +:10AB340023F4801356E0012202FA06F623EA0606FD +:10AB44002662236A2362464B9C4213D003F50063BA +:10AB54009C420FD003F540639C420BD003F5806305 +:10AB64009C4207D003F580639C4203D003F5006345 +:10AB74009C420DD1226A41F211131A4208D1226A71 +:10AB840040F244431A4202BF636C23F400436364FB +:10AB9400226A41F211131A4208D1226A40F2444354 +:10ABA4001A4202BF236823F00103236001238DF8B6 +:10ABB4003D3097E7B4F1804F05D1254AD36923F09E +:10ABC4000103D361A7E7294B9C4210D1204AD369E2 +:10ABD40023F00203D361264B9C4210D11C4A936993 +:10ABE40023F40053936100238DF83D3012E7214B89 +:10ABF4009C420CD1164AD36923F00403D3611E4B43 +:10AC04009C4214D1124A936923F48033EAE71B4B24 +:10AC14009C4280D10E4AD36923F01003D361184BB0 +:10AC24009C4280D10A4A936923F48023DAE7154BC6 +:10AC34009C42F4D1064A936923F40033D2E73C28BA +:10AC44007FF4E8AE23E700BF74D70008FC070020B8 +:10AC540000100240DC070020111111000CD800087C +:10AC6400002C01400014004000500140000400404A +:10AC74000034014000080040004001400010004042 +:10AC84000048014000440140184B1A6A12124270F5 +:10AC94001A6A827030B50022FF24551C92B28A428F +:10ACA40003DA825C54402A46F7E744545A6C002A7B +:10ACB400FDD11A6CC2B9186472B693F8282002F058 +:10ACC400FF017AB90322D96283F82820DB695B6823 +:10ACD4000122DA626FF001021A61DA6842F00102BD +:10ACE400DA60596262B630BD5864E5E718030020A3 +:10ACF40008B5044B2BB10448FEF7B5F808B1AFF31F +:10AD0400008008BD00000000B804002010B590F8D1 +:10AD14006A30222B036833D1998C8368B0F85C20A5 +:10AD2400B3F5805F02EA0102436D0DD1016959B99F +:10AD340023F8022B4365B0F85A40013CA4B2A0F8B2 +:10AD44005A402CB1002010BD591C41651A70F2E71D +:10AD540003681A6822F490721A609A6822F0010259 +:10AD64009A60202380F86A30FEF761F90428E9D854 +:10AD7400064A074B53F8203052F820009847E1E781 +:10AD84009A6942F008029A61022010BD5001002025 +:10AD94003C010020406A0023A0F85A30A0F8523049 +:10ADA400FEF730B9406A0268536843F400435360C5 +:10ADB400826B00235363C26B536390F84130602B62 +:10ADC40003D1202380F84130704790F851300BB9FB +:10ADD400FEF7C7BE70470368DA69196870B512F0E8 +:10ADE4000F05044606D1960604D58E0602D5BDE8A5 +:10ADF40070408BE79868002D68D010F0010502D1EF +:10AE040011F4907F62D0D60707D5C80505D5012077 +:10AE14001862E06E40F00100E066960706D52DB199 +:10AE240002201862E06E40F00400E066500706D588 +:10AE34002DB104201862E06E40F00200E0661607AF +:10AE440007D5880600D425B108201862E36E0343B1 +:10AE5400E366E36E002B00F0A880920604D58B060F +:10AE640002D52046FFF752FFE36E13F0080F236864 +:10AE740003D19D6815F0400523D01A6822F490721E +:10AE84001A609A6822F001029A60202284F86A20EB +:10AE94009A68560610D59A68606E22F040029A604D +:10AEA40050B1424B4363FEF75FFF00287DD0606ED4 +:10AEB400BDE87040436B18472046BDE87040FEF77C +:10AEC400A1B82046FEF79EF8E56670BDD50215D5FB +:10AED400400213D54FF480121A62202384F869309B +:10AEE400204684F86A30FEF7A2F8314B53F820105C +:10AEF40020467531BDE87040FFF7D6BC16062BD549 +:10AF04000D0629D594F86920212A4ED1B4F852208F +:10AF140092B242B91A6822F080021A601A6842F0AA +:10AF240040021A6070BDA268B2F5805FE26C0ED177 +:10AF3400216961B932F8021BC1F308011985E26481 +:10AF4400B4F85230013B9BB2A4F8523070BD511C8E +:10AF5400E16412781A85F3E7500626D54A0624D50B +:10AF64001A6822F040021A60202384F869302046CF +:10AF7400FEF75DF8042818D80E4B53F820400E4B0A +:10AF840053F820302046984701300ED0D4F894303E +:10AF9400B4F89A1094F8742013FA81F1074BBDE8C1 +:10AFA400704053F8220000F04DBA70BD99AD00080E +:10AFB400500100208C02002078020020280100208B +:10AFC400036A23F010030362036A70B5446882695C +:10AFD4000D6822F0807222F4E64242EA05228D686E +:10AFE40023F0200343EA05131A4DA84207D005F5C0 +:10AFF4000065A84203D005F5E055A8420DD1CD68FF +:10B0040023F0800343EA0513124DA84223F04003C2 +:10B0140012D005F50065A8420ED00F4DA8420BD002 +:10B0240005F58065A84207D005F58065A84203D0E0 +:10B0340005F50065A84206D124F440658E694C6983 +:10B04400344345EA8404446082614A68826303624B +:10B0540070BD00BF002C014000400140324A03682B +:10B06400904212D0B0F1804F0FD0A2F594329042AA +:10B074000BD002F58062904207D002F596329042DE +:10B0840003D002F5E052904215D14A6823F07003D0 +:10B094001343254A90421DD0B0F1804F1AD0A2F537 +:10B0A4009432904216D002F58062904212D002F59A +:10B0B400963290420ED01D4A90420BD002F5806227 +:10B0C400904207D002F58062904203D002F50062FC +:10B0D400904203D1CA6823F4407313434A6923F0AE +:10B0E4008003134303608B68C3620B6883620E4B57 +:10B0F400984213D003F5006398420FD003F54063E0 +:10B1040098420BD003F58063984207D003F580631F +:10B11400984203D003F50063984201D10B6903639D +:10B1240001234361704700BF002C014000400140EF +:10B1340003681A69910710B504460ED5DA689207B8 +:10B144000BD56FF002021A61012202779B699B07FB +:10B1540075D0FDF729FE0023237723681A69500769 +:10B1640010D5DA6851070DD56FF004021A61022276 +:10B1740022779B6913F4407F204663D0FDF714FEC9 +:10B184000023237723681A6912070FD5DA6810079A +:10B194000CD56FF008021A6104222277DB69990743 +:10B1A400204652D0FDF700FE0023237723681A6956 +:10B1B400D20610D5DA68D0060DD56FF010021A61E8 +:10B1C40008222277DB6913F4407F204640D0FDF744 +:10B1D400EBFD0023237723681A69D10709D5DA68C0 +:10B1E400D20706D56FF001021A61636C201F03B108 +:10B1F400984723681A69140605D5DA68100644BF0F +:10B204006FF080021A611A69D10505D5DA68120651 +:10B2140044BF6FF480721A611A69540605D5DA685E +:10B22400500644BF6FF040021A611A69910605D5B1 +:10B23400DA68920644BF6FF020021A6110BDFEF76F +:10B2440029F888E7FEF726F89AE7FEF723F8ABE734 +:10B25400FEF720F8BDE70368012202FA01F11A6A39 +:10B2640022EA01021A621A6A1143114A19629342CC +:10B2740013D002F5006293420FD002F5406293426C +:10B284000BD002F58062934207D002F580629342AC +:10B2940003D002F50062934203D15A6C42F4004297 +:10B2A4005A641A6842F001021A600020704700BF15 +:10B2B400002C01400F2303600B4B5A6802F0030279 +:10B2C40042605A6802F0F00282605A6802F4E06256 +:10B2D400C2605B68DB0803F4E0630361034B1B6833 +:10B2E40003F007030B607047001002400020024087 +:10B2F400084B4FF460525B6892FAA2F2B2FA82F2FF +:10B3040003F46053D340044AD35C044A1068D84021 +:10B31400704700BF00100240E0DA0008A4000020DB +:10B32400084B4FF4E0625B6892FAA2F2B2FA82F23E +:10B3340003F4E063D340044AD35C044A1068D84061 +:10B34400704700BF00100240E0DA0008A4000020AB +:10B35400124B5A6802F00C01082910B51BD14FF4A6 +:10B36400701191FAA1F10F20B1FA81F1DB6A90FA20 +:10B37400A0F00B4CB0FA80F003F00F0302F470124B +:10B38400C340CA40234422441B7A0648B0FBF3F36B +:10B39400107E584310BD034810BD00BF001002408A +:10B3A400E0DA000800127A0011F0010307D090F8E7 +:10B3B400413003F02803282B0CBF4223F2238A07D1 +:10B3C40009D590F8412002F02802282A0CBF43F046 +:10B3D400440343F0F4034A0701F0110248BF43F069 +:10B3E400B80301F01201112A08BF43F09003122997 +:10B3F40001680A6808BF43F0600322EA03030B6094 +:10B404000020704703689A69910744BF00229A623A +:10B414009A69D2075EBF9A6942F001029A61704745 +:10B42400074938B5A1F5A075A1F11404E3681039F2 +:10B4340013B1032208469847AC422146F4D138BDE3 +:10B44400B804002090F86930202B1AD1B9B190F8D3 +:10B454006830012B15D00123A0F85230C164A0F844 +:10B464005030016800232122C36680F8683080F8D8 +:10B4740069200A6842F080020A6018467047012079 +:10B4840070470220704738B506244FF48065A8B28F +:10B49400FEF77CFB013C4FEA4505F8D138BD38B5D1 +:10B4A40005242025A8B2FEF771FB013C4FEA4505AF +:10B4B400F8D138BD1020FEF769BB0820FEF766BB43 +:10B4C4000220FEF763BB0120FEF760BB01F06CBEF7 +:10B4D40001F06ABE01F068BE01F05EBE01F05CBE20 +:10B4E40000000000000000000000000001F0FF0167 +:10B4F400102A2BDB10F0070F08D010F8013B013A9B +:10B504008B422DD010F0070F42B3F6D1F0B441EACC +:10B51400012141EA014122F007047FF000070023E2 +:10B52400F0E80256083C85EA010586EA010685FA38 +:10B5340047F5A3FA87F586FA47F6A5FA87F68EB992 +:10B54400EED1F0BC01F0FF0102F0070232B110F8B5 +:10B55400013B013A83EA010313B1F8D1002070479B +:10B5640001387047002D06BF35460338073815F0FB +:10B57400010F07D1013015F4807F02BF013015F4AB +:10B58400C03F0130F0BC0138704700BF034613F8D8 +:10B59400012B002AFBD1181A013870472DE9804885 +:10B5A400C34CC44EA7B00D20FFF7F8F904F194077B +:10B5B4004FF4E1330025C4F80C313846062384F8EF +:10B5C4003031C4F81851C4F81451C4F81051FEF7BE +:10B5D40000F894F8083156F8231091F8692091F88E +:10B5E4006A100A4302F02202222A16D006EB8303D1 +:10B5F400B14A5A612946012294F91E019F62FEF75D +:10B60400B3FE94F91E01FEF7E3FE94F8083104F248 +:10B61400091156F82300FFF747F9A8490420FDF75C +:10B62400BBFF0546002835D0A5490420FCF737FFA9 +:10B6340020BBA3490420FCF725FF01271422002185 +:10B644000FA801F013F89D490420FCF769FE044695 +:10B65400002800F08B830420FCF776FE05460028C2 +:10B6640000F01283102840F08183002F40F00E83F5 +:10B67400002303930394039B143357E30027DDE76C +:10B6840003F47013B3F5701F40F06D83636843F0E7 +:10B69400020367E38B490420FDF77EFF044600287C +:10B6A40000F07D8286490420FCF7F9FE20B9844924 +:10B6B4000420FCF7E7FE01254022002116A800F033 +:10B6C400D5FF1C2200210FA800F0D0FF7D490420E3 +:10B6D400FCF726FE04461690002800F04783142247 +:10B6E40000210AA800F0C2FF26A9002341F8743DF6 +:10B6F4000AA8FFF7DFFD671C00F0C680724B9C426E +:10B7040000F0BB8200F2AE80A3F592339C4209D0D4 +:10B7140000F2A180B4F1804F04D0A3F580639C4271 +:10B7240040F0AF800D9CFFF7FBFDB4F5A06F04D093 +:10B7340000F2B680B4F5806F00D14000634BB0FBDB +:10B74400F3F3013B1793FE23199300231A931893E1 +:10B754001B938DF895308DB35D4C9DF89530042086 +:10B76400207003F0FF08FBB956498DF89480FDF76B +:10B7740022FF074616A8FDF7E7FD94F90000C0F381 +:10B784000310FCF701FE02230B9303230D9323788C +:10B79400CDF8308003F00F03C7F306279D400AA9B4 +:10B7A4000E970A95FEF710FA022317A916988DF83A +:10B7B4009530FFF753FC01238DF895300420FCF7F6 +:10B7C400D1FD1428044600F2E384424BC340DD0754 +:10B7D40040F1CC8260230F930023119313931293AF +:10B7E400159314936C2310939DF89430012B00F05F +:10B7F400BD8201238DF89430082C4FF002038DF89C +:10B80400953000F0158171D8002C00F0AD80042C27 +:10B8140000F0FC8001238DF895302A490023042090 +:10B824008DF89430FDF7C7FEC00240F1B381169B3A +:10B83400042202FA04F41A6A22EA04021A621A6A54 +:10B8440014431C625A6C42F400425A641A6842F06F +:10B8540001021A608AE2204B9C423FF463AF03F575 +:10B8640080635CE71D4B9C4200F0078211D8A3F56E +:10B8740080539C4200F0018203F540639C4200F037 +:10B88400FC81174801F0D0F9164801F0CDF90020E9 +:10B8940054E7154B9C4200F0F08103F50063EDE79B +:10B8A400B4F5C06F3FF449AFB4F5E06F44E700BFAF +:10B8B400B8040020280100200D8F0008E8D70008F4 +:10B8C400FC0700200CD80008002C014018E40300F9 +:10B8D4000400002011111100001000400044014038 +:10B8E40008DB000838DB000800480140102C00F099 +:10B8F400E280142C00F018810C2C8BD1169BB54DD2 +:10B904001A6A22F480521A62196A5868DA6922F0B3 +:10B91400807222F4E642AB4242F4C04221F4005168 +:10B9240013D005F50065AB420FD005F54065AB4279 +:10B934000BD005F58065AB4207D005F58065AB42B9 +:10B9440003D005F50065AB4201D120F48040586076 +:10B95400DA616C221A641962DA6942F40062DA610B +:10B96400DA6922F48062A2E0169B9A4F1A6A22F0E6 +:10B9740001021A621D6A59689A6922F4803222F01F +:10B984007302BB4242F0600225F0020013D007F5B7 +:10B994000067BB420FD007F54067BB420BD007F5E9 +:10B9A4008067BB4207D007F58067BB4203D007F529 +:10B9B4000067BB421AD125F00E00864DAB4213D06E +:10B9C40005F50065AB420FD005F54065AB420BD0E1 +:10B9D40005F58065AB4207D005F58065AB4203D021 +:10B9E40005F50065AB4201D121F4407159609A61BB +:10B9F4006C225A6318629A6942F008029A619A6941 +:10BA040022F004029A619A699A6103E70FA91698D1 +:10BA1400FFF7D6FA169B13999A6942F400629A6169 +:10BA24009A6922F480629A619A6942EA0122EBE7F8 +:10BA3400169B68491A6A22F480721A621F6A58684F +:10BA4400DA6922F4803222F073028B4242F06002FF +:10BA540027F4007507D0DFF880E1734503D00EF5B5 +:10BA6400E05E734506D18B4227F4606511D05A49D4 +:10BA74008B420ED059498B420BD001F580618B4229 +:10BA840007D001F580618B4203D001F500618B4240 +:10BA940001D120F440505860DA616C22DA631D62EF +:10BAA400DA6942F00802DA61DA6922F00402DA6142 +:10BAB400DA69DA61AEE6169B464D1A6A22F48032E0 +:10BAC4001A62196A58685A6D22F4803222F07002A0 +:10BAD400AB4242F0600221F4003113D005F5006559 +:10BAE400AB420FD005F54065AB420BD005F5806540 +:10BAF400AB4207D005F58065AB4203D005F5006580 +:10BB0400AB4201D120F4803058605A656C229A65AA +:10BB140019625A6D42F008025A655A6D22F0040205 +:10BB24005A655A6D5A6575E6169B2A4D1A6A22F4AF +:10BB340080121A62196A58685A6D22F0807222F4CF +:10BB4400E042AB4242F4C04221F4001113D005F5A7 +:10BB54000065AB420FD005F54065AB420BD005F54F +:10BB64008065AB4207D005F58065AB4203D005F58F +:10BB74000065AB4201D120F4802058605A656C22E4 +:10BB8400DA6519625A6D42F400625A655A6D22F4FC +:10BB94008062C5E7214616A8FFF75DFBE6E030208A +:10BBA400FEF7FCFE2046FEF7E7FEDFE0FEE794F832 +:10BBB4008910012940F0AD81196841F00101196033 +:10BBC400022384F88930A4E1FFF7AAFB4000C8E10E +:10BBD400FEE700BF002C014000340140004001405A +:10BBE4000A223946B2E249462046FDF7BAF9002353 +:10BBF400AB618021BC22112300274FF0010B4FF0D1 +:10BC0400E10A8DF85F1016A808218DF858200192DA +:10BC14008DF8593000938DF85A808DF85B708DF84B +:10BC24005CB08DF85DA08DF85E70FFF72DF8C8202C +:10BC3400FCF740FC009B019A8DF8593008218123C0 +:10BC440016A88DF858208DF85F308DF85A808DF83D +:10BC54005B708DF85CB08DF85DA08DF85E70FFF7B9 +:10BC640013F8C820FCF726FC4FF09042536983F088 +:10BC740020035361FFF73CF85AE20E9CFFF738FBB0 +:10BC840053E5039503944FE0002FFAD00223BD4FF0 +:10BC94008DF84030FB69DFF81C9323F00053FB61FF +:10BCA400002313938DF84030042389F800309DF865 +:10BCB400403003F0FF08CBB94046FCF765FBFB6955 +:10BCC40043F00053FB61FB69CDF8608003F000533F +:10BCD4000293029B99F8003003F00F020123934072 +:10BCE400169316A903231793FDF76EFF0223002171 +:10BCF40001208DF8403013918DF840008DF84030CC +:10BD0400236840F6FE72AA4023EA020323602368F4 +:10BD1400C022AA4023EA020323608DF84000039165 +:10BD24000394002D7FF4A7AC039B08330393039B78 +:10BD34004FF4D8621A609DF84130012B16D00223CB +:10BD44008DF8403022680123AB40134323602368FD +:10BD5400002D7FF495AC03F03C033C2B03D16368C6 +:10BD640043F00103636001238DF84030864987481E +:10BD7400874CDFF84492DFF84482824FFDF7F1F8F4 +:10BD840084498248FDF7EDF84FF47A70FCF792FB92 +:10BD9400814B824A9A642046FCF79CFA4FF0FF32AA +:10BDA400226486F8492096F8531096F854207C486B +:10BDB4002E23002589F8003142EA0122012389F863 +:10BDC40062504FF0020A89F80131C8F82020042299 +:10BDD400C8F81850257188F8285084F82C506563E9 +:10BDE400A562C8F8245086F8485086F83F5086F873 +:10BDF400415086F8423086F84330C6F83C5186F804 +:10BE040040A0C8F830207A6984F83C5042F40032EB +:10BE140084F82430C8F84050D8F81C40C8F844507E +:10BE240088F85C507A617A6902F400320792079AC2 +:10BE34007A6942F480227A617A690A9302F4802250 +:10BE4400032306920AA9069A0B930C95FDF7BCFEF0 +:10BE540008230AA952480C950D950E93CDF828A0F5 +:10BE6400CDF82CA0FDF7B0FE402262667F22E26688 +:10BE740094F889204B4BA56612F0100F2567A5672F +:10BE8400C4F884503A4623664F4645467FF48EAE46 +:10BE9400916941F001019161916901F001010991F8 +:10BEA400099994F8891001F0FF0009B984F8880011 +:10BEB40000210891196808910899606F21F07001B8 +:10BEC40041F040010891089921F002010891089974 +:10BED40021F440210891089921F47051089108999E +:10BEE40021F4004108910899014308910899E06FF1 +:10BEF400014308910899D4F88000014308910899F6 +:10BF04001960012184F8891094F88910C9067FF515 +:10BF14004EAEA046244B48F8043BD36943F00203D9 +:10BF2400D361D36903F002030593059BC023002268 +:10BF34000A930AA902234FF090400B930C920D929E +:10BF44000E93FDF741FE194B4FF000521A6004A9FD +:10BF54000FA8FFF7AFF9129B002B7FF435AEFFF764 +:10BF6400DFF923686360124BB0FBF3F00023E36056 +:10BF74006361A36194F8413001384FF6FF72A06009 +:10BF84001EE000BF0010024024DB0008B8040020BB +:10BF9400EC0500202ADB0008440600200D8300087D +:10BFA40000040048280001400004004000E100E0D3 +:10BFB40040420F0004000020D40600201803002093 +:10BFC400226103F0FF0993B9404684F84090FDF7DD +:10BFD400BBF96068FDF767F94A460F2140B2FEF7E6 +:10BFE400C3F96068FDF75FF940B2FEF7F1F94FF06D +:10BFF400020984F8419008F104016068FFF72EF803 +:10C00400012384F8413094F84030012B3FF4E0AD33 +:10C01400606884F84190036A604923F00103036275 +:10C0240088428369026A16D0B0F1804F13D0A1F51B +:10C03400943188420FD001F5806188420BD001F51C +:10C044009631884207D001F54061884203D001F55A +:10C054008051884201D123F0030343F0010323F00C +:10C06400F00322F00A0242F0020243F0400383612B +:10C074000262836923F00C038361836983614FF057 +:10C084000109602384F841904FF0020A16934FF09F +:10C09400000B4FF6FF731793CDF860A0CDF868B08E +:10C0A40016A984F8409084F841A0FEF789FF6368DC +:10C0B4009A6942F400629A619A6922F480629A61F0 +:10C0C40099691A9A41EA02229A6184F8419084F8A3 +:10C0D40040B09A6922F400629A6184F841A01A6817 +:10C0E40042EA09021A6084F84190DA6842EA0A02D4 +:10C0F400DA601A6A22F001021A621A6A42EA090232 +:10C104001A621A6842EA09021A6004214046FFF7DB +:10C11400A2F897F800315B0705D597F8003183F052 +:10C12400840387F800311E4CDFF87C904FF00A0836 +:10C13400D6F840717B1C002FC6F84031BFF650ADD5 +:10C144002D212046FCF797FE0A2279422046FCF76F +:10C1540018FF49462046FCF704FFAB69002B3FF467 +:10C1640048AD1049DFF844A02046FCF7FAFE00274A +:10C17400AB69BB427FF737AD1FB151462046FCF790 +:10C18400F0FEE95D10222046FCF7FBFE0137EFE7E5 +:10C194003C287FF4EBADFFF71DBB00BF002C014032 +:10C1A400B80400202DDB00082ADB0008C0DB0008EF +:10C1B4009A499B48CA689B4B70B54FF6FF042240CE +:10C1C4000243CA601A6842F010021A60CB68234026 +:10C1D4000343954CCB6000F0C3F9636813F00C0F74 +:10C1E4000AD0636803F00C03082B69D1636803F475 +:10C1F400C033B3F5004F63D1022393FAA3F222684C +:10C2040093FAA3F32368F82292FAA2F21021B2FA65 +:10C2140082F223F0F80301FA02F213432360636805 +:10C2240003F00C03082B6AD04FF0807393FAA3F346 +:10C23400B3FA83F303F1845303F584139B000022C0 +:10C244001A607A4A16684FF00070012190FAA0F340 +:10C25400256890FAA0F3B3FA83F303F01F0301FAFD +:10C2640003F32B4240F08580E36A23F00F03E3627B +:10C27400636823F4761343F4E41363604FF080732C +:10C2840093FAA3F3B3FA83F303F1845303F584130A +:10C294009B000125196016684FF0007191FAA1F313 +:10C2A400206891FAA1F3B3FA83F303F01F0305FAAC +:10C2B40003F3034223D113689B1B022BEED91EE028 +:10C2C400012292FAA2F3B3FA83F303F1845303F540 +:10C2D40084139B0055491A600E68022090FAA0F35B +:10C2E400256890FAA0F3B3FA83F303F01F0302FA6C +:10C2F40003F32B4286D10B689B1B022BEED9494AD0 +:10C30400136803F00703012B39D9636823F0F003A2 +:10C3140063604FF0007393FAA3F2216893FAA3F3D6 +:10C32400B3FA83F303F01F03012202FA03F30B426F +:10C3340031D13F4B4FF47A721B68B3FBF2F3013BEC +:10C34400B3F1807F09D23B4A5360344BF02183F828 +:10C3540023100023936007231360364A136843F0C5 +:10C3640004031360002211464FF0FF30FDF7FCFF79 +:10C3740070BD13689B1B022B7FF668AFBFE7136881 +:10C3840023F0070343F002031360136803F0070369 +:10C39400022BCED1B9E7636823F0030343F0020311 +:10C3A4006360224B196841F28830626802F00C0223 +:10C3B400082A22D11B4A136803F00703022B21D851 +:10C3C400636823F4E06343F480636360636823F485 +:10C3D40060536360FEF7BCFF6368F02292FAA2F236 +:10C3E400B2FA82F203F0F003D340134AD35CD8408C +:10C3F4000F4B186000F0B4F89BE71A68521A824297 +:10C40400D3D996E7136823F0070343F002031360BC +:10C41400136803F00703022B8BD1D1E700ED00E092 +:10C424000003FA050020024000100240D807002053 +:10C43400A400002010E000E05DD7000870B5444C73 +:10C44400444A4549454E062384F86030072384F85E +:10C454007430082384F88830092384F89C300A2334 +:10C46400002584F8B030172384F8C43084F8D83019 +:10C4740084F8EC3084F8003184F8143128462823F9 +:10C4840084F8283184F83C3184F8503184F86431DC +:10C4940084F8783184F88C31E566C4F88050C4F8A7 +:10C4A4009450C4F8A850C4F8BC50C4F8D050C4F890 +:10C4B400E450C4F8F850C4F80C51C4F82051C4F83E +:10C4C4003451C4F84851C4F85C51C4F87051C4F8EC +:10C4D4008451C4F8985100F092F84FF47A73C4F878 +:10C4E400A83106F10803C4F8A031032384F8BD324F +:10C4F400022384F8BC3204F2B113C4F8C032C4F885 +:10C50400A45104F2F1135822294604F53570A4F815 +:10C51400C452C4F8C832A4F8C652A4F8CC52A4F841 +:10C52400CE5200F0A3F80123C4F8F432283604F5FF +:10C534004B7384F8285084F8F852C4F8D462C4F8D1 +:10C54400885384F8B85384F8B953E36170BD00BFCD +:10C55400180300200000002025B4000874D7000848 +:10C564000F4B4FF47A721B68B3FBF2F3013BB3F148 +:10C57400807F10B511D20B4A53600B4BF02183F826 +:10C5840023100024072394600F2113604FF0FF3021 +:10C594002246FDF7E9FE204610BD012010BD00BF74 +:10C5A400A400002010E000E000ED00E0DFF834D04B +:10C5B400002103E00C4B5B58435004310B480C4BF7 +:10C5C40042189A42F6D30B4A02E0002342F8043B95 +:10C5D400094B9A42F9D3FCF783FF00F023F8FEF7E6 +:10C5E400DDFFFEE70000012070DC000800000020F1 +:10C5F4000C0100200C010020C8080020FEE70B46B7 +:10C604000146184600F000B810B5054C13462CB18D +:10C614000A4601460220AFF3008010BD204610BD3B +:10C624000000000070B50D4E0D4CA41BA410002595 +:10C63400A54209D10B4E0C4C01F07CF8A41BA410AC +:10C644000025A54205D170BD56F82530984701351F +:10C65400EEE756F8253098470135F2E750DC00083C +:10C6640050DC000850DC00086CDC0008024403467F +:10C67400934200D1704703F8011BF9E79368013B2B +:10C68400002B10B4936009DA9469A34202DBCBB2A5 +:10C694000A2B03D15DF8044B00F0DEBA1368581C72 +:10C6A40010601970C8B25DF8044B7047F8B50646BF +:10C6B4000F461446D518AC4201D1002007E03A4693 +:10C6C40014F8011B3046FFF7D9FF431CF3D1F8BD22 +:10C6D4002DE9F04F9DB00C4617460393064618B15A +:10C6E40083690BB900F06AFC7C4B9C4257D1746897 +:10C6F400A38918075DD52369002B5AD00023099319 +:10C7040020238DF8293030238DF82A304FF0010B87 +:10C71400B846454615F8013B002B55D1B8EB070A3E +:10C724000BD053463A4621463046FFF7BFFF01304F +:10C7340000F0C480099B5344099398F80030002BFF +:10C7440000F0BC8000234FF0FF320493079305925E +:10C7540006938DF853301A93052229786048FEF722 +:10C76400C5FE05F10108049B002831D1D90644BF58 +:10C7740020228DF853201A0744BF2B228DF8532012 +:10C784002A782A2A2CD0079A00210A20A8460135A3 +:10C7940098F80030303B092B6DD971B326E0514B2A +:10C7A4009C4201D1B468A3E74F4B9C4208BFF46894 +:10C7B4009EE72146304600F0A1FA00289ED04FF0B3 +:10C7C400FF301DB0BDE8F08F252BA7D0A846A0E709 +:10C7D400434A801A0BFA00F0184304904546BBE71D +:10C7E400039A111D12680391002A01DB079204E0E9 +:10C7F400524243F002030792049398F800302E2B20 +:10C8040010D198F801302A2B08F1010137D1039B8C +:10C814001A1D1B680392002BB8BF4FF0FF3308F1B9 +:10C8240002080593314D98F8001003222846FEF7BC +:10C834005DFE40B14023401B03FA00F0049B034318 +:10C84400049308F1010898F8001029488DF828107D +:10C85400062208F10107FEF749FE002834D0254BD3 +:10C8640003BB039B073323F0070308330393099B9C +:10C874004B4409934CE700FB0232012186E7002375 +:10C88400059318460A258846013198F80020303A65 +:10C89400092A03D9002BC5D00590C3E705FB002066 +:10C8A4000123F0E703AB00932246134B04A930465F +:10C8B400AFF30080B0F1FF3F8146D8D1A3895B0676 +:10C8C4003FF57DAF09987CE703AB009322460A4B02 +:10C8D40004A9304600F088F8ECE700BF10DC00083B +:10C8E400BCDB000830DC0008F0DB0008C2DB000819 +:10C8F400C6DB000800000000B1C600082DE9F047BF +:10C9040091461F468A680B69DDF820809342B8BFC0 +:10C914001346C9F8003091F8432006460C4612B17C +:10C924000133C9F800302368990642BFD9F80030B2 +:10C934000233C9F80030256815F0060507D104F163 +:10C94400190AE368D9F800209B1A9D4229DB94F860 +:10C9540043302268003318BF012392062ED404F119 +:10C96400430239463046C047013021D02368E56888 +:10C97400D9F8002003F00603042B08BFAD1AA368FE +:10C9840022690CBF25EAE57500259342C4BF9B1AB2 +:10C99400ED184FF000091A344D451BD10020BDE8B5 +:10C9A400F0870123524639463046C047013003D14F +:10C9B4004FF0FF30BDE8F0870135C2E7E1185A1C9B +:10C9C400302081F84300224494F8451082F8431043 +:10C9D4000233C4E70123224639463046C0470130BA +:10C9E400E6D009F10109D7E72DE9F04317460A7E9D +:10C9F40085B06E2A984606460C460C9B01F1430E00 +:10CA040000F0BC801AD8632A2ED00AD8002A00F07D +:10CA1400C880582A00F08A8004F1420584F8422034 +:10CA24002AE0642A01D0692AF6D121681A6811F033 +:10CA3400800F23D0111D1960136827E0732A00F0BA +:10CA4400B48008D86F2A2AD0702AE5D10A6842F047 +:10CA540020020A6003E0752A21D0782ADCD17822EA +:10CA640084F845206E4964E01A6801F14205111DFD +:10CA74001960136884F842300123A3E011F0400FD9 +:10CA840002F104011960D7D0B2F90030002B03DAA7 +:10CA94002D225B4284F8432062490A2217E0206871 +:10CAA400196810F0800F03D0081D18600B6806E0A9 +:10CAB40010F0400F01F104001860F7D00B885949B9 +:10CAC4006F2A14BF0A220822002084F843006568F4 +:10CAD400A560002DC0F29C80206820F00400206036 +:10CAE400002B3FD1002D40F095807546082A0BD1CC +:10CAF4002368DA0708D5236962689A42DEBF3023C7 +:10CB040005F8013C05F1FF35AEEB05032361CDF8D3 +:10CB140000803B4603AA21463046FFF7EFFE013072 +:10CB240055D14FF0FF3005B0BDE8F08381F84520C2 +:10CB34003C492268186812F0800F00F104051D605A +:10CB440001D0036802E05506FBD50388D00744BF33 +:10CB540042F0200222601BB9226822F020022260E7 +:10CB64001022B1E77546B3FBF2F002FB1033CB5C45 +:10CB740005F8013D03460028F5D1B7E708681A68AF +:10CB8400496910F0800F04D0101D186013681960F3 +:10CB940007E010F0400F02F1040018601368F6D0AB +:10CBA4001980002323617546B1E71A68111D1960C5 +:10CBB4001568626800212846FEF798FC08B1401BFE +:10CBC400606063682361002384F843309FE723692E +:10CBD4002A4639463046C0470130A2D023689B0715 +:10CBE40007D5002504F11909E368039A9B1A9D42AD +:10CBF40005DBE068039B9842B8BF184693E701231E +:10CC04004A4639463046C04701308AD00135EBE701 +:10CC1400002BA7D10B7884F8423004F1420565E774 +:10CC2400DEDB0008CDDB00080FB40A4B13B51C682B +:10CC340024B1A36913B9204600F0C0F905AB049AE6 +:10CC4400A16801932046FFF743FD02B0BDE8104000 +:10CC540004B07047A8000020F8B50E4614460546F7 +:10CC640018B183690BB900F0A9F9214B9C422AD170 +:10CC74006C68A369A360A3891A072ED5236963B3DB +:10CC840023692068C01A6369F6B29842374604DB08 +:10CC94002146284600F028F928BBA368013BA3607D +:10CCA40023685A1C22601E7063690130984204D0C4 +:10CCB400A389DB0719D50A2E17D12146284600F08F +:10CCC40013F990B10FE00B4B9C4201D1AC68D0E753 +:10CCD400094B9C4208BFEC68CBE72146284600F08C +:10CCE4000DF80028CCD04FF0FF373846F8BD00BF10 +:10CCF40010DC000830DC0008F0DB0008324B70B5B3 +:10CD04001D6806460C4625B1AB6913B9284600F0E8 +:10CD140055F92E4B9C420FD16C68B4F90C309AB281 +:10CD240015072CD4D00611D40922326043F04003F5 +:10CD3400A3814FF0FF3070BD254B9C4201D1AC68FC +:10CD4400EBE7244B9C4208BFEC68E6E7510712D599 +:10CD5400616B41B104F14403994202D0304600F0C2 +:10CD640037FA00236363A38923F02403A3810023F8 +:10CD7400636023692360A38943F00803A3812369C3 +:10CD84004BB9A38903F42073B3F5007F03D0214684 +:10CD9400304600F0C1F9A28912F001030CD000233F +:10CDA400A36063695B42A361236953B9B4F90C308E +:10CDB40013F08000BAD170BD920758BF6369A360B5 +:10CDC400F2E70020F7E700BFA800002010DC00080D +:10CDD40030DC0008F0DB00088A892DE9F0410546C3 +:10CDE40010070C465AD44B68002B05DC0B6C002B47 +:10CDF40002DC0020BDE8F081E66A002EF9D00023B1 +:10CE040012F480522F682B6033D0606DA3895A07C7 +:10CE140005D56368C01A636B0BB1236CC01A002379 +:10CE24000246E66A216A2846B047431CA38906D114 +:10CE340029681D294BD82B4ACA40D60747D500225A +:10CE44006260D9042269226004D5421C01D12B6896 +:10CE540003B96065616B2F600029CAD004F14403F3 +:10CE6400994202D0284600F0B3F900206063BDE87F +:10CE7400F081216A01232846B047411CC6D12B68A2 +:10CE8400002BC3D01D2B01D0162B01D12F60B0E78E +:10CE9400A38943F04003A381BDE8F0810F69002F0B +:10CEA400A7D093070E6808BF4B690F6018BF002313 +:10CEB400A6EB07088B60B8F1000F9ADD43463A46AB +:10CEC400216AA66A2846B047002807DCA38943F0F4 +:10CED4004003A3814FF0FF30BDE8F0810744A8EB85 +:10CEE4000008E8E70100402038B50B6905460C4608 +:10CEF400DBB118B183690BB900F060F80C4B9C42AC +:10CF040009D16C68B4F90C307BB121462846BDE8E0 +:10CF14003840FFF761BF074B9C4201D1AC68F1E791 +:10CF2400054B9C4208BFEC68ECE7002038BD00BF0D +:10CF340010DC000830DC0008F0DB0008014900F0D8 +:10CF4400A9B800BFEDCE0008002310B50446036065 +:10CF54004360836081814366C2810361436183616D +:10CF6400194608225C30FFF781FB054B6362054BD1 +:10CF7400A362054BE362054B2462236310BD00BF2B +:10CF84004DD300086FD30008A7D30008CBD3000803 +:10CF940070B54A1E682555430E4605F1740100F02C +:10CFA40065F9044640B1002180E842000C30A060DD +:10CFB40005F16802FFF75AFB204670BD836910B57E +:10CFC400044633BB8364C3640365124B124A1B6873 +:10CFD4008262984204BF0123836100F01FF86060FD +:10CFE400204600F01BF8A060204600F017F800224D +:10CFF400E06004216068FFF7A7FF01220921A0680F +:10D00400FFF7A2FF02221221E068FFF79DFF012330 +:10D01400A36110BDB8DB000841CF0008F8B51C4B74 +:10D024001E68B369074613B93046FFF7C7FF483691 +:10D03400B4687368013B03D5336833B13668F7E7E6 +:10D04400B4F90C506DB16834F4E704213846FFF7A5 +:10D054009FFF30600028F1D10C233B60044620463A +:10D06400F8BD4FF6FF73E3810123A3816566256054 +:10D07400A560656025616561A5610822294604F102 +:10D084005C00FFF7F3FA6563A563A564E564E6E76E +:10D09400B8DB00082DE9F8438046894600F14804CE +:10D0A400002614B93046BDE8F883A5686768013FD7 +:10D0B40001D52468F5E7AB89012B07D9B5F90E3002 +:10D0C400013303D029464046C84706436835EEE796 +:10D0D40070B50E46B1F90E10002990B014461D46E5 +:10D0E40007DA00232B60B3891A0610D44FF4806347 +:10D0F4000EE001AA00F08EF90028F2DB029A02F495 +:10D104007042A2F500535A425A412A60EEE7402386 +:10D114000020236010B070BD8B8973B59D0706464F +:10D124000C4607D504F1470323602361012363619F +:10D1340002B070BD01AB6A46FFF7CAFF009905460D +:10D14400304600F093F848B9B4F90C309A05EFD49E +:10D1540023F0030343F00203A381E3E70D4BB3621F +:10D16400A389206043F08003A381009B6361019B3A +:10D1740020615BB1B4F90E10304600F05DF928B1BE +:10D18400A38923F0030343F00103A381A3891D436F +:10D19400A581CDE741CF0008024B0146186800F095 +:10D1A40065B800BFA8000020024B0146186800F0D3 +:10D1B4000FB800BFA800002010B5431E0A449142D6 +:10D1C40000D110BD11F8014B03F8014FF7E700003F +:10D1D40038B50546002945D051F8043C0C1F002BF6 +:10D1E400B8BFE41800F04AF91F4A1368104633B96F +:10D1F400636014602846BDE8384000F040B9A3429B +:10D204000CD921686218934204BF1A685B68636092 +:10D2140004BF521822600460ECE713465A680AB14E +:10D22400A242FAD919685818A0420BD120680144C7 +:10D23400581882421960DDD1106852685A6001445E +:10D244001960D7E702D90C232B60D3E72068211893 +:10D254008A4204BF11685268626004BF09182160E1 +:10D264005C60C7E738BD00BF1C08002070B5CD1C4A +:10D2740025F0030508350C2D38BF0C25002D064676 +:10D2840001DBA94203D90C233360002070BD00F0F8 +:10D29400F5F8234A1468214691B9224C23681BB936 +:10D2A400304600F041F820602946304600F03CF852 +:10D2B400431C26D10C233360304600F0E0F8E4E749 +:10D2C4000B685B1B1AD40B2B0FD90B60CD50CC18F9 +:10D2D400304600F0D4F804F10B00231D20F00700C1 +:10D2E400C31A1BD05A42E25070BD8C420DBF636812 +:10D2F4004B686360136018BF0C46E9E70C46496845 +:10D30400CAE7C41C24F00304A04205D0211A304605 +:10D3140000F00AF80130CDD02560D9E770BD00BF18 +:10D324001C0800202008002038B5064C00230546C0 +:10D3340008462360FBF78EF9431C02D1236803B12E +:10D344002B6038BDC408002010B50C46B1F90E108E +:10D3540000F096F80028ABBF636DA3891B1823F473 +:10D364008053ACBF6365A38110BD2DE9F0411F4616 +:10D374008B89DB0505460C46164605D5022300229B +:10D38400B1F90E1000F068F8A389B4F90E1023F473 +:10D394008053A38132463B462846BDE8F04100F065 +:10D3A40017B810B50C46B1F90E1000F055F8431C2F +:10D3B400A38915BF606523F4805343F48053A3818C +:10D3C40018BFA38110BDB1F90E1000F013B800000E +:10D3D40038B5074C054608461146002222601A4615 +:10D3E400FBF77DF8431C02D1236803B12B6038BDE1 +:10D3F400C408002038B5064C0023054608462360BF +:10D40400FBF725F9431C02D1236803B12B6038BD17 +:10D41400C408002038B5074C0023054608461146C9 +:10D424002360FBF70FF9431C02D1236803B12B607F +:10D4340038BD00BFC408002038B5064C002305469B +:10D4440008462360FBF7FCF8431C02D1236803B1B0 +:10D454002B6038BDC408002038B5074C0546084683 +:10D464001146002222601A46FBF7E8F8431C02D159 +:10D47400236803B12B6038BDC4080020704770478F +:10D4840038B5074C054608461146002222601A4664 +:10D49400FBF7D2F8431C02D1236803B12B6038BDDB +:10D4A400C4080020244B1A68D0075CBF01221A600C +:10D4B400224B1A68D1075CBF01221A60204B1A68FC +:10D4C400D2075CBF01221A601E4B1A68D0075CBFEA +:10D4D40001221A601C4B1A68D1075CBF01221A6032 +:10D4E4001A4B1A68D2075CBF01221A60184B1A68DB +:10D4F400D0075CBF01221A60164B1A68D1075CBFC3 +:10D5040001221A60144B1A68D2075CBF01221A6008 +:10D51400124B1A68D0075CBF01221A60104B1A68BC +:10D52400D1075CBF01221A600E4B1A68D2075CBF98 +:10D5340001221A60704700BF500800204C080020E8 +:10D544004808002044080020400800203C0800202F +:10D554003808002034080020300800202C0800205F +:10D564002808002024080020244B1A68D0075CBF38 +:10D5740001221A60224B1A68D1075CBF01221A608B +:10D58400204B1A68D2075CBF01221A601E4B1A682E +:10D59400D0075CBF01221A601C4B1A68D1075CBF1C +:10D5A40001221A601A4B1A68D2075CBF01221A6062 +:10D5B400184B1A68D0075CBF01221A60164B1A6810 +:10D5C400D1075CBF01221A60144B1A68D2075CBFF2 +:10D5D40001221A60124B1A68D0075CBF01221A603C +:10D5E400104B1A68D1075CBF01221A600E4B1A68EF +:10D5F400D2075CBF01221A60704700BF8008002078 +:10D604007C0800207808002074080020700800209E +:10D614006C080020680800206408002060080020CE +:10D624005C0800205808002054080020184B1A6891 +:10D63400D1075CBF01221A60164B1A68D2075CBF7F +:10D6440001221A60144B1A68D0075CBF01221A60C9 +:10D65400124B1A68D1075CBF01221A60104B1A687A +:10D66400D2075CBF01221A600E4B1A68D0075CBF58 +:10D6740001221A600C4B1A68D1075CBF01221A60A0 +:10D684000A4B1A68D2075CBF01221A60704700BFB8 +:10D69400A00800209C08002098080020940800207E +:10D6A400900800208C0800208808002084080020AE +:10D6B400184B1A68D1075CBF01221A60164B1A680E +:10D6C400D2075CBF01221A60144B1A68D0075CBFF2 +:10D6D40001221A60124B1A68D1075CBF01221A603A +:10D6E400104B1A68D2075CBF01221A600E4B1A68ED +:10D6F400D0075CBF01221A600C4B1A68D1075CBFCB +:10D7040001221A600A4B1A68D2075CBF01221A6010 +:10D71400704700BFC0080020BC080020B8080020E3 +:10D72400B4080020B0080020AC080020A80800209D +:10D73400A4080020F8B500BFF8BC08BC9E4670479A +:0CD74400F8B500BFF8BC08BC9E4670475A +:10D75000010102030406080A0E0F04080C00000071 +:10D760000000000000010203040607080901040884 +:10D770000200000000000000000000007B8E000896 +:10D78000C58D00086B8E0008298E0008518E000898 +:10D79000158E0008000000000000000085820008CF +:10D7A000DD89000825880008D982000803020A13D1 +:10D7B00015141A0809271607060519182A2C5B0DD7 +:10D7C0000E0F172D2E2F505122232B3229282625BC +:10D7D0000C0B1C1B12111F1E1D2400010410212004 +:10D7E00007062223251B12240400000000740040B9 +:10D7F00003800000050000000074004003000100E9 +:10D80000FF00000000000000000000000100000018 +:10D810000040014012891000040000000004004094 +:10D8200012020100060000000044014012810000C5 +:10D8300007000000004801401281000008000000BD +:10D84000002C01401286000009000000002C01405D +:10D85000120601000A000000002C0140128601009F +:10D860000B000000002C0140120B02000C00000015 +:10D8700000440140128100000D00000000440140FE +:10D88000128110000E00000000340140120501005A +:10D890000F00000000340140128200001000000060 +:10D8A000002C01401206110013000000003401405A +:10D8B0001284100014000000004401401281000096 +:10D8C0001500000000480140128A00001600000008 +:10D8D0000044014012811000170000000048014080 +:10D8E0001281100018000000004401401281000065 +:10D8F0001900000000480140128100001A000000D9 +:10D9000000000040128101001D000000002C0140B9 +:10D91000128610001E00000000400140128100002D +:10D920001F00000000400140120101002000000023 +:10D93000002C01401282000021000000002C014058 +:10D940001202010022000000002C0140128201009E +:10D9500023000000002C01401202020026000000FB +:10D960000004004012820000270000000004004074 +:10D970001202010028000000000400401282010091 +:10D980002900000000040040120202002A000000EA +:10D9900000340140128410002B00000000340140CC +:10D9A000120411002C000000003401401284110008 +:10D9B0002D000000002C01401284100050000000D7 +:10D9C000002C014012861100FF0000000000000042 +:10D9D00000000000D40600200200000000440040C7 +:10D9E0001207000009000000003801401207000083 +:10D9F0000E00000000440040120700001300000069 +:10DA000000440040120700001600000000380140EA +:10DA10001207000019000000004800401207000033 +:10DA20001A00000000480040120700002400000017 +:10DA300000380140120700002A000000004C00409E +:10DA4000120500002C0000000050004012050000EC +:10DA5000FF000000000000000000000003000000C4 +:10DA600000440040120700000A0000000038014096 +:10DA7000120700000F0000000044004012070000E1 +:10DA800014000000004400401207000017000000CE +:10DA90000038014012070000180000000048004054 +:10DAA000120700001B0000000048004012070000A1 +:10DAB0002500000000380140120700002B00000084 +:10DAC000004C0040120500003200000000500040F1 +:10DAD00012050000FF000000000000000000000030 +:10DAE0000000000001020304010203040506070808 +:10DAF000090A0B0C0D0E0F10020304050607080996 +:10DB00000A0B0C0D0E0F101054494D3A20556E6B38 +:10DB10006E6F776E2074696D657220696E737461C3 +:10DB20006E6365005374617274000D0A0054656C75 +:10DB3000656772616D3A200054494D3A20556E6B0D +:10DB40006E6F776E20636C6F636B20736F7572639B +:10DB5000650054494D3A20556E6B6E6F776E207498 +:10DB6000696D6572204952516E004552524F523ACA +:10DB7000206174206C65617374206F6E652055415F +:10DB800052542070696E20686173206E6F2070653A +:10DB9000726970686572616C0A004552524F523A60 +:10DBA00020552853294152542070696E73206D69A5 +:10DBB000736D617463680A00AC000020232D302B64 +:10DBC0002000686C4C00656667454647003031327E +:10DBD00033343536373839414243444546003031D5 +:10DBE0003233343536373839616263646566000034 +:10DBF0000000000000000000000000000000000025 +:10DC00000000000000000000000000000000000014 +:10DC10000000000000000000000000000000000004 +:10DC200000000000000000000000000000000000F4 +:10DC300000000000000000000000000000000000E4 +:10DC400000000000000000000000000000000000D4 +:10DC5000B5C10008B981000841C40008A9D4000872 +:0CDC60006DD5000831D60008B5D60008CC +:04DC6C009581000896 +:10DC700000000000FF0000000000000000000000A5 +:10DC80000000000000000000000000000000000094 +:10DC90000000000000000000000000000000000084 +:10DCA0000000000000000000000000000000000074 +:10DCB0000000000000000000000000000000000064 +:10DCC0000000000000000000000000000000000054 +:10DCD0000000000000000000000000000000000044 +:10DCE000000000000000000000000000050000002F +:10DCF0000000000000000000000000000000000024 +:10DD00000000000000000000000000000000000013 +:10DD10000000000000127A00AC00002000000000AB +:10DD200010DC000830DC0008F0DB00080000000018 +:10DD300000000000000000000000000000000000E3 +:10DD400000000000000000000000000000000000D3 +:10DD500000000000000000000000000000000000C3 +:10DD600000000000000000000000000000000000B3 +:0CDD7000000000000000000000000000A7 +:040000050800C5B179 +:00000001FF From 410b0a8225dc8e0f973153c759b6a881995e21fb Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 19:18:21 +0200 Subject: [PATCH 121/359] [Updater] Add version, git-sha and build time to MANIFEST.MF --- firmware_updater/updater/source/build.gradle | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 7e16af9a..33296f09 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -19,9 +19,18 @@ application { mainClass = 'org.selfbus.updater.Updater' } +def gitHash = "git rev-parse --verify --short HEAD".execute().text.trim() +def buildDate = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z") + .withZone(ZoneId.of("UTC")) + .format(Instant.now()) jar { manifest { - attributes 'Main-Class': 'org.selfbus.updater.Updater' + attributes( + 'Main-Class': application.mainClass, + 'Implementation-Version': project.version, + 'Revision': "${gitHash}", + 'Build-Date': "${buildDate}" + ) } } From 44d8d431b7fe12eabef0e10123730d610ba0e51b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 19:37:57 +0200 Subject: [PATCH 122/359] [Updater] Fix missing `java` in sourceSets path --- firmware_updater/updater/source/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index 33296f09..a313fa56 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -36,11 +36,11 @@ jar { sourceSets { main { - java.srcDirs = ['src/main'] + java.srcDirs = ['src/main/java'] resources.srcDirs = ['src/main/resources'] } test { - java.srcDirs = ['src/test'] + java.srcDirs = ['src/test/java'] //java.exclude 'dir1/', 'dir2' resources.srcDirs = ['src/test/resources'] } From f719f7e92cf2f085232974f95f2e4ba2431e9828 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 9 Aug 2024 20:16:39 +0200 Subject: [PATCH 123/359] [Updater] Use plugin `com.gradleup.shadow` for `shadowJar` Instead of creating/configuring/tinkering by our self a fatJar, we use now `com.gradleup.shadow` to create a `shadowJar`. The old `fatJar` gradle task will be now redirected to the new `shadowJar` task for some time. --- firmware_updater/updater/source/build.gradle | 45 +++++++++----------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/source/build.gradle index a313fa56..40cde3a6 100644 --- a/firmware_updater/updater/source/build.gradle +++ b/firmware_updater/updater/source/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java' id 'application' + id 'com.gradleup.shadow' version '8.3.0' id 'jvm-test-suite' } @@ -46,30 +47,8 @@ sourceSets { } } -task fatJar(type: Jar) { - manifest.from jar.manifest - archiveClassifier.set('all') - duplicatesStrategy = DuplicatesStrategy.INCLUDE - from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } - } { - /* - exclude "META-INF/*.SF" - exclude "META-INF/*.DSA" - exclude "META-INF/*.RSA" - exclude "META-INF/LICENSE" - exclude "META-INF/LICENSE.txt" - exclude "META-INF/NOTICE.txt" - exclude "META-INF/LGPL2.1" - exclude "META-INF/AL2.0" - exclude "META-INF/versions/9/module-info.class" - */ - } - - with jar -} - -artifacts { - archives fatJar +tasks.named('compileJava') { + options.javaModuleVersion = provider { project.version } } // ensure UTF-8 encoding @@ -86,6 +65,10 @@ compileTestJava.options.compilerArgs = [ '-Xlint:all', // enable all warnings ] +artifacts { + archives shadowJar +} + testing { // // comment avoids warning "suites' cannot be applied to '(groovy.lang.closure)'" @@ -102,7 +85,7 @@ testing { } targets { - all { + configureEach { testTask.configure { shouldRunAfter(test) } @@ -153,4 +136,16 @@ dependencies { // and c&p the code of // ../cz/jaybee/intelhex to // ../software-arm-lib/firmware_updater/updater/source/src/main/java/cz/jaybee/intelhex +} + +// Just to maintain compatibility with the old fatJar task for a while. +tasks.register('fatJar') { + dependsOn shadowJar + group = 'build' + description = 'An alias for the shadowJar task that creates a fat JAR' + doFirst { + def blue = '\u001B[34m' + def reset = '\u001B[0m' + logger.warn("${blue}The fatJar task is deprecated. Please use the \"shadowJar\" task directly.${reset}") + } } \ No newline at end of file From 1b386dd9dcddc7a374e6b816452577d8cf05fd30 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 04:24:25 +0200 Subject: [PATCH 124/359] [Updater] Update .gitignore(s) with settings recommend by JetBrains (IDEA) downloaded on 10.08.2024 from [Github gitignore - JetBrains.gitignore](https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore) based on [JetBrains IDEs Support IntelliJ Platform - How to manage projects under Version Control Systems](https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) --- .gitignore | 3 - firmware_updater/updater/.idea/.gitignore | 3 - firmware_updater/updater/source/.gitignore | 86 +++++++++++++++++++++- 3 files changed, 84 insertions(+), 8 deletions(-) delete mode 100644 firmware_updater/updater/.idea/.gitignore diff --git a/.gitignore b/.gitignore index 7fcd7a6f..29a6c7c2 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,3 @@ Debug* Release* Flashstart* .settings - -# IDE files -.idea diff --git a/firmware_updater/updater/.idea/.gitignore b/firmware_updater/updater/.idea/.gitignore deleted file mode 100644 index 26d33521..00000000 --- a/firmware_updater/updater/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/firmware_updater/updater/source/.gitignore b/firmware_updater/updater/source/.gitignore index f5c3e9cd..3396ecc0 100644 --- a/firmware_updater/updater/source/.gitignore +++ b/firmware_updater/updater/source/.gitignore @@ -1,7 +1,89 @@ -.idea +# Selfbus Updater specific .gradle build log out bin -settings.xml \ No newline at end of file +settings.xml + +# all below is dated 10.08.2024 source: +# https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser From b999e46c3386f0ffe38d598415777827d9b40a80 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 04:42:55 +0200 Subject: [PATCH 125/359] [Updater][Breaks build] Reorganize code base folder hierarchy This commit breaks intentionally the build to fix the code base folder hierarchy and not to loose git history. Next commits will (hopefully) fix it. Reasons for code base reorganization: - The Updater project itself was in subfolder `source`, which is redundant since we already use `src` for source files - `main` code was in `source/src/main/java/org/selfbus/*`. - `test` code was in `source/src/test/java/org/selfbus/*`. --- .../updater/{source => }/.classpath | 0 .../updater/{source => }/.gitignore | 0 .../updater/.idea/Selfbus_Updater.iml | 9 ---- firmware_updater/updater/.idea/misc.xml | 4 -- firmware_updater/updater/.idea/modules.xml | 11 ----- firmware_updater/updater/.idea/vcs.xml | 6 --- .../updater/{source => }/.project | 0 .../updater/{source => }/build.gradle | 0 .../updater/{source => }/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 firmware_updater/updater/{source => }/gradlew | 0 .../updater/{source => }/gradlew.bat | 0 .../updater/{source => }/libs/forms_rt.jar | Bin .../updater/{source => }/settings.gradle | 0 .../.idea/artifacts/SB_updater_main_jar.xml | 41 ------------------ firmware_updater/updater/source/README.md | 1 - .../org/selfbus/updater/AnsiCursor.java | 0 .../org/selfbus/updater/BinImage.java | 0 .../selfbus/updater/CliInvalidException.java | 0 .../org/selfbus/updater/CliOptions.java | 0 .../org/selfbus/updater/DeviceManagement.java | 0 .../updater/DiscoverKnxInterfaces.java | 0 .../org/selfbus/updater/FlashDiffMode.java | 0 .../org/selfbus/updater/FlashFullMode.java | 0 .../selfbus/updater/ListTextAppenders.java | 0 .../java => src}/org/selfbus/updater/Mcu.java | 0 .../org/selfbus/updater/ProgressInfo.java | 0 .../org/selfbus/updater/ResponseResult.java | 0 .../org/selfbus/updater/SBKNXLink.java | 0 .../updater/SBManagementClientImpl.java | 0 .../org/selfbus/updater/SpinningCursor.java | 0 .../org/selfbus/updater/ToolInfo.java | 0 .../org/selfbus/updater/Updater.java | 0 .../org/selfbus/updater/UpdaterException.java | 0 .../org/selfbus/updater/Utils.java | 0 .../updater/bootloader/BootDescriptor.java | 0 .../bootloader/BootloaderIdentity.java | 0 .../bootloader/BootloaderStatistic.java | 0 .../updater/bootloader/BootloaderUpdater.java | 0 .../org/selfbus/updater/gui/CliConverter.java | 0 .../updater/gui/ConColorsToStyledDoc.java | 0 .../org/selfbus/updater/gui/GuiMain.form | 0 .../org/selfbus/updater/gui/GuiMain.java | 0 .../org/selfbus/updater/gui/TextAppender.java | 0 .../mode/differential/Decompressor.java | 0 .../differential/DecompressorListener.java | 0 .../updater/mode/differential/FlashDiff.java | 0 .../updater/mode/differential/FlashPage.java | 0 .../mode/differential/FlashProgrammer.java | 0 .../updater/mode/differential/OldWindow.java | 0 .../updater/upd/UDPProtocolVersion.java | 0 .../org/selfbus/updater/upd/UDPResult.java | 0 .../org/selfbus/updater/upd/UPDCommand.java | 0 .../org/selfbus/updater/upd/UPDProtocol.java | 0 .../resources/GuiTranslation.properties | 0 .../resources/GuiTranslation_de.properties | 0 .../resources/GuiTranslation_en.properties | 0 .../frame_images/selfbus_logo_16x16.png | Bin .../resources/javax.usb.properties | 0 .../src/main => src}/resources/logback.xml | 0 .../updater/BootloaderIdentityTest.java | 0 .../updater/BootloaderStatisticTest.java | 0 .../selfbus/updater/SpinningCursorTest.java | 0 .../org/selfbus/updater/UtilsTest.java | 0 .../gui/ConColorsToStyledDocTests.java | 0 .../mode/differential/FlashDiffTests.java | 0 .../mode/differential/FlashDiffUtils.java | 0 .../src => }/test/resources/logback-test.xml | 0 .../test/resources/updater.ino.slto.v1.hex | 0 .../test/resources/updater.ino.slto.v2.hex | 0 71 files changed, 72 deletions(-) rename firmware_updater/updater/{source => }/.classpath (100%) rename firmware_updater/updater/{source => }/.gitignore (100%) delete mode 100644 firmware_updater/updater/.idea/Selfbus_Updater.iml delete mode 100644 firmware_updater/updater/.idea/misc.xml delete mode 100644 firmware_updater/updater/.idea/modules.xml delete mode 100644 firmware_updater/updater/.idea/vcs.xml rename firmware_updater/updater/{source => }/.project (100%) rename firmware_updater/updater/{source => }/build.gradle (100%) rename firmware_updater/updater/{source => }/gradle.properties (100%) rename firmware_updater/updater/{source => }/gradle/wrapper/gradle-wrapper.jar (100%) rename firmware_updater/updater/{source => }/gradle/wrapper/gradle-wrapper.properties (100%) rename firmware_updater/updater/{source => }/gradlew (100%) rename firmware_updater/updater/{source => }/gradlew.bat (100%) rename firmware_updater/updater/{source => }/libs/forms_rt.jar (100%) rename firmware_updater/updater/{source => }/settings.gradle (100%) delete mode 100644 firmware_updater/updater/source/.idea/artifacts/SB_updater_main_jar.xml delete mode 100644 firmware_updater/updater/source/README.md rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/AnsiCursor.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/BinImage.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/CliInvalidException.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/CliOptions.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/DeviceManagement.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/DiscoverKnxInterfaces.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/FlashDiffMode.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/FlashFullMode.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/ListTextAppenders.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/Mcu.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/ProgressInfo.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/ResponseResult.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/SBKNXLink.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/SBManagementClientImpl.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/SpinningCursor.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/ToolInfo.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/Updater.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/UpdaterException.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/Utils.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/bootloader/BootDescriptor.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/bootloader/BootloaderIdentity.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/bootloader/BootloaderStatistic.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/bootloader/BootloaderUpdater.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/gui/CliConverter.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/gui/ConColorsToStyledDoc.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/gui/GuiMain.form (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/gui/GuiMain.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/gui/TextAppender.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/mode/differential/Decompressor.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/mode/differential/DecompressorListener.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/mode/differential/FlashDiff.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/mode/differential/FlashPage.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/mode/differential/FlashProgrammer.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/mode/differential/OldWindow.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/upd/UDPProtocolVersion.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/upd/UDPResult.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/upd/UPDCommand.java (100%) rename firmware_updater/updater/{source/src/main/java => src}/org/selfbus/updater/upd/UPDProtocol.java (100%) rename firmware_updater/updater/{source/src/main => src}/resources/GuiTranslation.properties (100%) rename firmware_updater/updater/{source/src/main => src}/resources/GuiTranslation_de.properties (100%) rename firmware_updater/updater/{source/src/main => src}/resources/GuiTranslation_en.properties (100%) rename firmware_updater/updater/{source/src/main => src}/resources/frame_images/selfbus_logo_16x16.png (100%) rename firmware_updater/updater/{source/src/main => src}/resources/javax.usb.properties (100%) rename firmware_updater/updater/{source/src/main => src}/resources/logback.xml (100%) rename firmware_updater/updater/{source/src/test/java => test}/org/selfbus/updater/BootloaderIdentityTest.java (100%) rename firmware_updater/updater/{source/src/test/java => test}/org/selfbus/updater/BootloaderStatisticTest.java (100%) rename firmware_updater/updater/{source/src/test/java => test}/org/selfbus/updater/SpinningCursorTest.java (100%) rename firmware_updater/updater/{source/src/test/java => test}/org/selfbus/updater/UtilsTest.java (100%) rename firmware_updater/updater/{source/src/test/java => test}/org/selfbus/updater/gui/ConColorsToStyledDocTests.java (100%) rename firmware_updater/updater/{source/src/test/java => test}/org/selfbus/updater/mode/differential/FlashDiffTests.java (100%) rename firmware_updater/updater/{source/src/test/java => test}/org/selfbus/updater/mode/differential/FlashDiffUtils.java (100%) rename firmware_updater/updater/{source/src => }/test/resources/logback-test.xml (100%) rename firmware_updater/updater/{source/src => }/test/resources/updater.ino.slto.v1.hex (100%) rename firmware_updater/updater/{source/src => }/test/resources/updater.ino.slto.v2.hex (100%) diff --git a/firmware_updater/updater/source/.classpath b/firmware_updater/updater/.classpath similarity index 100% rename from firmware_updater/updater/source/.classpath rename to firmware_updater/updater/.classpath diff --git a/firmware_updater/updater/source/.gitignore b/firmware_updater/updater/.gitignore similarity index 100% rename from firmware_updater/updater/source/.gitignore rename to firmware_updater/updater/.gitignore diff --git a/firmware_updater/updater/.idea/Selfbus_Updater.iml b/firmware_updater/updater/.idea/Selfbus_Updater.iml deleted file mode 100644 index d6ebd480..00000000 --- a/firmware_updater/updater/.idea/Selfbus_Updater.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/firmware_updater/updater/.idea/misc.xml b/firmware_updater/updater/.idea/misc.xml deleted file mode 100644 index 9e54aca8..00000000 --- a/firmware_updater/updater/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/firmware_updater/updater/.idea/modules.xml b/firmware_updater/updater/.idea/modules.xml deleted file mode 100644 index 1ea5edac..00000000 --- a/firmware_updater/updater/.idea/modules.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/firmware_updater/updater/.idea/vcs.xml b/firmware_updater/updater/.idea/vcs.xml deleted file mode 100644 index b2bdec2d..00000000 --- a/firmware_updater/updater/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/firmware_updater/updater/source/.project b/firmware_updater/updater/.project similarity index 100% rename from firmware_updater/updater/source/.project rename to firmware_updater/updater/.project diff --git a/firmware_updater/updater/source/build.gradle b/firmware_updater/updater/build.gradle similarity index 100% rename from firmware_updater/updater/source/build.gradle rename to firmware_updater/updater/build.gradle diff --git a/firmware_updater/updater/source/gradle.properties b/firmware_updater/updater/gradle.properties similarity index 100% rename from firmware_updater/updater/source/gradle.properties rename to firmware_updater/updater/gradle.properties diff --git a/firmware_updater/updater/source/gradle/wrapper/gradle-wrapper.jar b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from firmware_updater/updater/source/gradle/wrapper/gradle-wrapper.jar rename to firmware_updater/updater/gradle/wrapper/gradle-wrapper.jar diff --git a/firmware_updater/updater/source/gradle/wrapper/gradle-wrapper.properties b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from firmware_updater/updater/source/gradle/wrapper/gradle-wrapper.properties rename to firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties diff --git a/firmware_updater/updater/source/gradlew b/firmware_updater/updater/gradlew similarity index 100% rename from firmware_updater/updater/source/gradlew rename to firmware_updater/updater/gradlew diff --git a/firmware_updater/updater/source/gradlew.bat b/firmware_updater/updater/gradlew.bat similarity index 100% rename from firmware_updater/updater/source/gradlew.bat rename to firmware_updater/updater/gradlew.bat diff --git a/firmware_updater/updater/source/libs/forms_rt.jar b/firmware_updater/updater/libs/forms_rt.jar similarity index 100% rename from firmware_updater/updater/source/libs/forms_rt.jar rename to firmware_updater/updater/libs/forms_rt.jar diff --git a/firmware_updater/updater/source/settings.gradle b/firmware_updater/updater/settings.gradle similarity index 100% rename from firmware_updater/updater/source/settings.gradle rename to firmware_updater/updater/settings.gradle diff --git a/firmware_updater/updater/source/.idea/artifacts/SB_updater_main_jar.xml b/firmware_updater/updater/source/.idea/artifacts/SB_updater_main_jar.xml deleted file mode 100644 index 371f0a8b..00000000 --- a/firmware_updater/updater/source/.idea/artifacts/SB_updater_main_jar.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - $PROJECT_DIR$/out/artifacts/SB_updater_main_jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/firmware_updater/updater/source/README.md b/firmware_updater/updater/source/README.md deleted file mode 100644 index e5d06855..00000000 --- a/firmware_updater/updater/source/README.md +++ /dev/null @@ -1 +0,0 @@ -[see ../README.md](../README.md) \ No newline at end of file diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/AnsiCursor.java b/firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/AnsiCursor.java rename to firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/BinImage.java rename to firmware_updater/updater/src/org/selfbus/updater/BinImage.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliInvalidException.java b/firmware_updater/updater/src/org/selfbus/updater/CliInvalidException.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliInvalidException.java rename to firmware_updater/updater/src/org/selfbus/updater/CliInvalidException.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/CliOptions.java rename to firmware_updater/updater/src/org/selfbus/updater/CliOptions.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/DeviceManagement.java rename to firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/DiscoverKnxInterfaces.java rename to firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashDiffMode.java rename to firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/FlashFullMode.java rename to firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ListTextAppenders.java b/firmware_updater/updater/src/org/selfbus/updater/ListTextAppenders.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/ListTextAppenders.java rename to firmware_updater/updater/src/org/selfbus/updater/ListTextAppenders.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Mcu.java b/firmware_updater/updater/src/org/selfbus/updater/Mcu.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/Mcu.java rename to firmware_updater/updater/src/org/selfbus/updater/Mcu.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/ProgressInfo.java rename to firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ResponseResult.java b/firmware_updater/updater/src/org/selfbus/updater/ResponseResult.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/ResponseResult.java rename to firmware_updater/updater/src/org/selfbus/updater/ResponseResult.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBKNXLink.java rename to firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/SBManagementClientImpl.java rename to firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/SpinningCursor.java b/firmware_updater/updater/src/org/selfbus/updater/SpinningCursor.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/SpinningCursor.java rename to firmware_updater/updater/src/org/selfbus/updater/SpinningCursor.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/ToolInfo.java rename to firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/Updater.java rename to firmware_updater/updater/src/org/selfbus/updater/Updater.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/UpdaterException.java b/firmware_updater/updater/src/org/selfbus/updater/UpdaterException.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/UpdaterException.java rename to firmware_updater/updater/src/org/selfbus/updater/UpdaterException.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java b/firmware_updater/updater/src/org/selfbus/updater/Utils.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/Utils.java rename to firmware_updater/updater/src/org/selfbus/updater/Utils.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootDescriptor.java rename to firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderIdentity.java rename to firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderStatistic.java rename to firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderUpdater.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderUpdater.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/bootloader/BootloaderUpdater.java rename to firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderUpdater.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java b/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/CliConverter.java rename to firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/ConColorsToStyledDoc.java rename to firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.form b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.form rename to firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/GuiMain.java rename to firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/TextAppender.java b/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/gui/TextAppender.java rename to firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/Decompressor.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/Decompressor.java rename to firmware_updater/updater/src/org/selfbus/updater/mode/differential/Decompressor.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/DecompressorListener.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/DecompressorListener.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/DecompressorListener.java rename to firmware_updater/updater/src/org/selfbus/updater/mode/differential/DecompressorListener.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashDiff.java rename to firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashPage.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashPage.java rename to firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashPage.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashProgrammer.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/FlashProgrammer.java rename to firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashProgrammer.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/OldWindow.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/mode/differential/OldWindow.java rename to firmware_updater/updater/src/org/selfbus/updater/mode/differential/OldWindow.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPProtocolVersion.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPProtocolVersion.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPProtocolVersion.java rename to firmware_updater/updater/src/org/selfbus/updater/upd/UDPProtocolVersion.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UDPResult.java rename to firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDCommand.java rename to firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java diff --git a/firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java similarity index 100% rename from firmware_updater/updater/source/src/main/java/org/selfbus/updater/upd/UPDProtocol.java rename to firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation.properties b/firmware_updater/updater/src/resources/GuiTranslation.properties similarity index 100% rename from firmware_updater/updater/source/src/main/resources/GuiTranslation.properties rename to firmware_updater/updater/src/resources/GuiTranslation.properties diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties b/firmware_updater/updater/src/resources/GuiTranslation_de.properties similarity index 100% rename from firmware_updater/updater/source/src/main/resources/GuiTranslation_de.properties rename to firmware_updater/updater/src/resources/GuiTranslation_de.properties diff --git a/firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties b/firmware_updater/updater/src/resources/GuiTranslation_en.properties similarity index 100% rename from firmware_updater/updater/source/src/main/resources/GuiTranslation_en.properties rename to firmware_updater/updater/src/resources/GuiTranslation_en.properties diff --git a/firmware_updater/updater/source/src/main/resources/frame_images/selfbus_logo_16x16.png b/firmware_updater/updater/src/resources/frame_images/selfbus_logo_16x16.png similarity index 100% rename from firmware_updater/updater/source/src/main/resources/frame_images/selfbus_logo_16x16.png rename to firmware_updater/updater/src/resources/frame_images/selfbus_logo_16x16.png diff --git a/firmware_updater/updater/source/src/main/resources/javax.usb.properties b/firmware_updater/updater/src/resources/javax.usb.properties similarity index 100% rename from firmware_updater/updater/source/src/main/resources/javax.usb.properties rename to firmware_updater/updater/src/resources/javax.usb.properties diff --git a/firmware_updater/updater/source/src/main/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml similarity index 100% rename from firmware_updater/updater/source/src/main/resources/logback.xml rename to firmware_updater/updater/src/resources/logback.xml diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java b/firmware_updater/updater/test/org/selfbus/updater/BootloaderIdentityTest.java similarity index 100% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderIdentityTest.java rename to firmware_updater/updater/test/org/selfbus/updater/BootloaderIdentityTest.java diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java b/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java similarity index 100% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/BootloaderStatisticTest.java rename to firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/SpinningCursorTest.java b/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java similarity index 100% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/SpinningCursorTest.java rename to firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java b/firmware_updater/updater/test/org/selfbus/updater/UtilsTest.java similarity index 100% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/UtilsTest.java rename to firmware_updater/updater/test/org/selfbus/updater/UtilsTest.java diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java b/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java similarity index 100% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/gui/ConColorsToStyledDocTests.java rename to firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java b/firmware_updater/updater/test/org/selfbus/updater/mode/differential/FlashDiffTests.java similarity index 100% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffTests.java rename to firmware_updater/updater/test/org/selfbus/updater/mode/differential/FlashDiffTests.java diff --git a/firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java b/firmware_updater/updater/test/org/selfbus/updater/mode/differential/FlashDiffUtils.java similarity index 100% rename from firmware_updater/updater/source/src/test/java/org/selfbus/updater/mode/differential/FlashDiffUtils.java rename to firmware_updater/updater/test/org/selfbus/updater/mode/differential/FlashDiffUtils.java diff --git a/firmware_updater/updater/source/src/test/resources/logback-test.xml b/firmware_updater/updater/test/resources/logback-test.xml similarity index 100% rename from firmware_updater/updater/source/src/test/resources/logback-test.xml rename to firmware_updater/updater/test/resources/logback-test.xml diff --git a/firmware_updater/updater/source/src/test/resources/updater.ino.slto.v1.hex b/firmware_updater/updater/test/resources/updater.ino.slto.v1.hex similarity index 100% rename from firmware_updater/updater/source/src/test/resources/updater.ino.slto.v1.hex rename to firmware_updater/updater/test/resources/updater.ino.slto.v1.hex diff --git a/firmware_updater/updater/source/src/test/resources/updater.ino.slto.v2.hex b/firmware_updater/updater/test/resources/updater.ino.slto.v2.hex similarity index 100% rename from firmware_updater/updater/source/src/test/resources/updater.ino.slto.v2.hex rename to firmware_updater/updater/test/resources/updater.ino.slto.v2.hex From 1af55631a3d6c8bbfd294b1becde24bd6420b213 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 05:25:48 +0200 Subject: [PATCH 126/359] [Updater] Fix gradle `sourceSets`, `MANIFEST.MF` & some links --- firmware_updater/updater/README.md | 5 ++--- firmware_updater/updater/build.gradle | 8 ++++---- .../updater/src/resources/META-INF/MANIFEST.MF | 3 +++ 3 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 firmware_updater/updater/src/resources/META-INF/MANIFEST.MF diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index a037bb7a..e1f4e936 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -15,7 +15,7 @@ gradle fatJar linux: gradlew fatJar windows: gradlew.bat fatJar ``` -*SB_updater-x.xx-all.jar* file is created in [build/libs](source/build/libs) directory. +*SB_updater-x.xx-all.jar* file is created in [build/libs](build/libs) directory. ## Usage ``` @@ -108,9 +108,8 @@ Change these in [**Settings Dialog**](https://www.jetbrains.com/help/idea/settin - Build, Execution, Deployment->[Build Tools](https://www.jetbrains.com/help/idea/settings-build-tools.html)->Gradle->Build and run using: Intellij IDEA - Build, Execution, Deployment->[Build Tools](https://www.jetbrains.com/help/idea/settings-build-tools.html)->Gradle->Run tests using: Intellij IDEA - ### gradle: -update [gradle wrapper](source/gradle/wrapper) to the newest version: +update [gradle wrapper](gradle/wrapper) to the newest version: ``` gradlew wrapper ``` diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 40cde3a6..538d9d72 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -37,13 +37,13 @@ jar { sourceSets { main { - java.srcDirs = ['src/main/java'] - resources.srcDirs = ['src/main/resources'] + java.srcDirs = ['src'] + resources.srcDirs = ['src/resources'] } test { - java.srcDirs = ['src/test/java'] + java.srcDirs = ['test'] //java.exclude 'dir1/', 'dir2' - resources.srcDirs = ['src/test/resources'] + resources.srcDirs = ['test/resources'] } } diff --git a/firmware_updater/updater/src/resources/META-INF/MANIFEST.MF b/firmware_updater/updater/src/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000..ba051365 --- /dev/null +++ b/firmware_updater/updater/src/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: org.selfbus.updater.Updater + From 59dbf3d27ebbd5f3811eedf8946b2c42aa7ebd82 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 05:30:52 +0200 Subject: [PATCH 127/359] [Updater] Recreate deleted `.idea` files New files in `.idea` are from a fresh gradle import in IntelliJ IDEA Community Edition 2024.2 --- firmware_updater/updater/.idea/.gitignore | 3 + firmware_updater/updater/.idea/.name | 1 + .../.idea/artifacts/SB_updater_main_jar.xml | 28 ++++ firmware_updater/updater/.idea/compiler.xml | 6 + firmware_updater/updater/.idea/misc.xml | 130 ++++++++++++++++++ firmware_updater/updater/.idea/vcs.xml | 6 + 6 files changed, 174 insertions(+) create mode 100644 firmware_updater/updater/.idea/.gitignore create mode 100644 firmware_updater/updater/.idea/.name create mode 100644 firmware_updater/updater/.idea/artifacts/SB_updater_main_jar.xml create mode 100644 firmware_updater/updater/.idea/compiler.xml create mode 100644 firmware_updater/updater/.idea/misc.xml create mode 100644 firmware_updater/updater/.idea/vcs.xml diff --git a/firmware_updater/updater/.idea/.gitignore b/firmware_updater/updater/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/firmware_updater/updater/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/firmware_updater/updater/.idea/.name b/firmware_updater/updater/.idea/.name new file mode 100644 index 00000000..42278725 --- /dev/null +++ b/firmware_updater/updater/.idea/.name @@ -0,0 +1 @@ +SB_updater \ No newline at end of file diff --git a/firmware_updater/updater/.idea/artifacts/SB_updater_main_jar.xml b/firmware_updater/updater/.idea/artifacts/SB_updater_main_jar.xml new file mode 100644 index 00000000..dd763497 --- /dev/null +++ b/firmware_updater/updater/.idea/artifacts/SB_updater_main_jar.xml @@ -0,0 +1,28 @@ + + + $PROJECT_DIR$/out/artifacts/SB_updater_main_jar + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/firmware_updater/updater/.idea/compiler.xml b/firmware_updater/updater/.idea/compiler.xml new file mode 100644 index 00000000..b589d56e --- /dev/null +++ b/firmware_updater/updater/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/firmware_updater/updater/.idea/misc.xml b/firmware_updater/updater/.idea/misc.xml new file mode 100644 index 00000000..f683393d --- /dev/null +++ b/firmware_updater/updater/.idea/misc.xml @@ -0,0 +1,130 @@ + + + + + + + + + + Abstraction issuesJava + + + Android + + + Assignment issuesJava + + + Class metricsJava + + + Code maturityJava + + + Code style issuesJava + + + ComplianceLintAndroid + + + Control flow issuesJava + + + CorrectnessLintAndroid + + + Declaration redundancyJava + + + Error handlingJava + + + GradleMigrationKotlin + + + Groovy + + + Inheritance issuesJava + + + InteroperabilityLintAndroid + + + JUnitJava + + + JVM languages + + + Java + + + Java 5Java language level migration aidsJava + + + Java language level migration aidsJava + + + Kotlin + + + LintAndroid + + + MavenMigrationKotlin + + + Method metricsGroovy + + + Method metricsJava + + + MethodNaming conventionsJava + + + MigrationKotlin + + + Naming conventionsGroovy + + + Naming conventionsJava + + + PerformanceLintAndroid + + + Probable bugsJava + + + Probable bugsKotlin + + + ProductivityLintAndroid + + + Redundant constructsKotlin + + + SecurityJava + + + SecurityLintAndroid + + + Style issuesKotlin + + + + + Android + + + + + + + \ No newline at end of file diff --git a/firmware_updater/updater/.idea/vcs.xml b/firmware_updater/updater/.idea/vcs.xml new file mode 100644 index 00000000..b2bdec2d --- /dev/null +++ b/firmware_updater/updater/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From c37ba364d887e61c0d500ddac7d2c72b695638f2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 06:06:56 +0200 Subject: [PATCH 128/359] [Update] Add application template --- .../runConfigurations/_template__of_Application.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 firmware_updater/updater/.idea/runConfigurations/_template__of_Application.xml diff --git a/firmware_updater/updater/.idea/runConfigurations/_template__of_Application.xml b/firmware_updater/updater/.idea/runConfigurations/_template__of_Application.xml new file mode 100644 index 00000000..7d508e44 --- /dev/null +++ b/firmware_updater/updater/.idea/runConfigurations/_template__of_Application.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file From a13d56b013d2f5be130211d08b3257fedf0bd834 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 13:08:40 +0200 Subject: [PATCH 129/359] [Updater] Fix typos and docu --- .../updater/gui/ConColorsToStyledDoc.java | 17 ++++++++++++----- .../src/resources/GuiTranslation_de.properties | 12 ++++++------ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index b9fef3fc..defa7026 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -56,10 +56,17 @@ private static void insertStyledString(String toInsert, String colorCode, JTextP textPane.setCaretPosition(document.getLength()); } - /* - * hier werden die Steuerzeichen für die Farben der Windows Konsole in Java Parameter gewandelt - * z.B. wird ein String "\033[0;31m dieser Text wird rot sein,\033[44m jetzt auf blauem Hintergrund" übergeben - * Dieser String wird entsprechend der Steuerbefehle in ein StyledDocument gefüllt + /** + * Converts ANSI escape sequences in the input string to styled text in a {@link JTextPane}. + *

+ * The method processes ANSI color codes and applies the corresponding text styles to the content of the JTextPane. + * For example, a string like "\033[0;31m this text will be red, \033[44m now with a blue background" + * will be parsed and displayed with the appropriate styles in the JTextPane. + *

+ * + * @param input The string containing ANSI escape sequences to be converted. + * @param textPane The {@link JTextPane} where the styled text will be inserted. + * @throws BadLocationException if an error occurs while inserting the styled text. */ public static void Convert(String input, JTextPane textPane) throws BadLocationException { Pattern pattern = Pattern.compile(RegExAnsi); @@ -104,7 +111,7 @@ public static void Convert(String input, JTextPane textPane) throws BadLocationE * @param textPane the {@link JTextPane} whose cursor position will be updated * @return {@code true} if the ANSI code was successfully processed; {@code false} otherwise * @throws BadLocationException if an error occurs when updating the cursor position - * @throws IllegalStateException if an not implemented or unexpected ANSI code is encountered + * @throws IllegalStateException if a not implemented or unexpected ANSI code is encountered */ private static boolean processAnsiCursor(String ansiCode, JTextPane textPane) throws BadLocationException { if (ansiCode.equals(AnsiCursor.off()) || ansiCode.equals(AnsiCursor.on())) { diff --git a/firmware_updater/updater/src/resources/GuiTranslation_de.properties b/firmware_updater/updater/src/resources/GuiTranslation_de.properties index 5c2a951a..efcf85fc 100644 --- a/firmware_updater/updater/src/resources/GuiTranslation_de.properties +++ b/firmware_updater/updater/src/resources/GuiTranslation_de.properties @@ -1,10 +1,10 @@ -loadFile=lade Flashdatei +loadFile=lade Firmware-Datei fileName=Dateiname selectKnxIpGateway=KNX IP Schnittstelle ipAddress=IP-Adresse scenario=Szenario uid=UID -startFlash=Starte Flashvorgang +startFlash=Starte Flash-Vorgang newDevice=neues Gerät appDevice=Gerät mit Applikation newDeviceHint=Es wurde auf ein Selfbus Gerät der Bootloader geflasht\ @@ -20,8 +20,8 @@ uidHint=die UID des Selfbus Ger fileNameHint=Der Dateipfad kann manuell bearbeitet werden
\ (Enter zum bestätigen) ipAddrHint=IP-Adresse der KNX Schnittstelle -portHint=TCP/UDP Portnummer der KNX Schnittstelle (default 3671) -stopFlash=Stoppe Flashvorgang +portHint=TCP/UDP Port der KNX Schnittstelle (default 3671) +stopFlash=Stoppe Flash Vorgang medium=Medium serial=ft12 tpuart=TPUART @@ -58,7 +58,7 @@ KNX IP Secure Ger reloadKnxIpGateways=Gateways erneut laden selectInterface=wähle Schnittstelle -diffFlash=differenzieller Flashvorgang +diffFlash=differenzieller Flash Vorgang diffFlashHint=Es werden nur veränderte Teile der Software
\ übertragen [experimentell] -logMessageCanceledFlashing=Flashvorgang wurde abgebrochen. \ No newline at end of file +logMessageCanceledFlashing=Flash Vorgang wurde abgebrochen. \ No newline at end of file From 03fea64adb3cdcf8cf45f3875271f81def01d669 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 13:25:47 +0200 Subject: [PATCH 130/359] [Updater] English is default language, no need translate --- .../src/org/selfbus/updater/gui/GuiMain.java | 4 ++ .../resources/GuiTranslation_en.properties | 63 ------------------- 2 files changed, 4 insertions(+), 63 deletions(-) delete mode 100644 firmware_updater/updater/src/resources/GuiTranslation_en.properties diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index e7945297..3c58d593 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -99,6 +99,10 @@ public class GuiMain extends JFrame { private static final String FILENAME_SETTINGS = "settings.xml"; public GuiMain() { + //todo load language from FILENAME_SETTINGS and set it here. + // Has to be done before calling $$$setupUI$$$(); + //Locale.setDefault(Locale.ENGLISH); // for tests + //Locale.setDefault(Locale.ROOT); // default language for tests $$$setupUI$$$(); guiTranslation = ResourceBundle.getBundle("GuiTranslation"); buttonLoadFile.addActionListener(new ActionListener() { diff --git a/firmware_updater/updater/src/resources/GuiTranslation_en.properties b/firmware_updater/updater/src/resources/GuiTranslation_en.properties deleted file mode 100644 index 497df8d5..00000000 --- a/firmware_updater/updater/src/resources/GuiTranslation_en.properties +++ /dev/null @@ -1,63 +0,0 @@ -loadFile=load file -fileName=file name -selectKnxIpGateway=select KNX IP gateway -ipAddress=IP address -scenario=scenario -uid=UID -startFlash=start flash -newDevice=new device -appDevice=device with application -newDeviceHint=There is a new device with flashed bootloader\ - -appDeviceHint=A device is flashed with an application over
\ -bootloader and now it should be updated -eraseCompleteFlashHint=USE WITH CAUTION! Erases the complete flash memory
\ -including the physical KNX address and all settings of
\ -the device. Only the bootloader is not deleted. -noFlashHint=disable flashing firmware -uidHint=the UID of the selfbus device -fileNameHint=you can modify the file path manually -ipAddrHint=IP address of the KNX interface -portHint=UDP port on (default 3671) -stopFlash=stop flashing -medium=medium -serial=ft12 -tpuart=TPUART -knxDeviceAddr=device address -knxProgDeviceAddr=bootloader device address -knxOwnAddress=own KNX address -port=port -useNat=NAT -knxMessageDelay=delay [ms] -eraseFlash=erase complete flash -noFlash=no flash -requestUid=request UID from device -requestUidHint=The UID can be requested from a Selfbus device.\ - -UpdaterSettings=Updater settings -KnxBusSettings=KNX bus settings -KnxGatewayConnectionSettings=KNX gateway connection settings -advancedSettings=Advanced settings -knxSecureUserPwd=User password -messagePriority=KNX telegram priority -knxSecureUser=KNX IP-Secure User -knxSecureUserHint=KNX IP Secure tunneling user identifier
\ -(1..127) (default 1) -knxSecureUserPwdHint=\ -KNX IP Secure tunneling user password
\ -(Commissioning password/Inbetriebnahmepasswort),
\ -quotation marks (") in password may not work\ - -knxSecureDevicePwd=Device password -knxSecureDevicePwdHint=\ -KNX IP Secure device authentication code
\ -(Authentication Code/Authentifizierungscode)
\ -quotation marks(") in password may not work\ - -reloadKnxIpGateways=reload gateways -selectInterface=select Interface -diffFlash=differential Flash -diffFlashHint=flash only changed parts of software
\ -[experimental] -logMessageCanceledFlashing=Flash process canceled. \ No newline at end of file From ef834ac9542c5f23bf543a6c9545aa2ce59dbc5c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 15:41:31 +0200 Subject: [PATCH 131/359] [GUI] Fix camelCase --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 3c58d593..4798f315 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -189,7 +189,7 @@ public void actionPerformed(ActionEvent e) { reloadGatewaysButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - new Thread(() -> LoadKnxIpInterfacesAndFillComboBox()).start(); + new Thread(() -> loadKnxIpInterfacesAndFillComboBox()).start(); } }); @@ -341,7 +341,7 @@ public void startUpdaterGui() { this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - new Thread(this::LoadKnxIpInterfacesAndFillComboBox).start(); + new Thread(this::loadKnxIpInterfacesAndFillComboBox).start(); TextAppender textAppender = ListTextAppenders.searchAppender(); if (textAppender != null) { @@ -349,7 +349,7 @@ public void startUpdaterGui() { } - InitGuiElementsVisibility(); + initGuiElementsVisibility(); fillScenarios(); fillMediumComboBox(); fillTelegramPriorityComboBox(); @@ -366,7 +366,7 @@ public void windowClosing(WindowEvent e) { }); } - private void LoadKnxIpInterfacesAndFillComboBox() { + private void loadKnxIpInterfacesAndFillComboBox() { reloadGatewaysButton.setEnabled(false); // ActionListener löschen und nach dem Füllen der ComboBox wieder hinzufügen // damit das Füllen kein Event auslöst @@ -508,7 +508,7 @@ public String getVisOption() { private static final Map> GuiObjectsMap = new HashMap<>(); - private void InitGuiElementsVisibility() { + private void initGuiElementsVisibility() { // hier wird definiert, in welchem Zustand der GUI welche Elemente sichtbar sein sollen GuiObjectsMap.put(buttonLoadFile, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV)); From 5275353b9c4c1c27e54b0aaf7ccd3afc224530af Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 15:47:22 +0200 Subject: [PATCH 132/359] [GUI] Rename language resources and in subfolder `language` --- .../src/org/selfbus/updater/gui/GuiMain.form | 78 +++++++++--------- .../src/org/selfbus/updater/gui/GuiMain.java | 81 ++++++++++--------- .../GuiMain.properties} | 0 .../GuiMain_de.properties} | 0 4 files changed, 80 insertions(+), 79 deletions(-) rename firmware_updater/updater/src/resources/{GuiTranslation.properties => language/GuiMain.properties} (100%) rename firmware_updater/updater/src/resources/{GuiTranslation_de.properties => language/GuiMain_de.properties} (100%) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form index 40885a28..8f4414a0 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form @@ -16,7 +16,7 @@ - + @@ -52,7 +52,7 @@ - + @@ -60,7 +60,7 @@ - + @@ -74,7 +74,7 @@ - + @@ -90,7 +90,7 @@ - + @@ -106,7 +106,7 @@ - + @@ -114,7 +114,7 @@ - + @@ -122,7 +122,7 @@ - + @@ -138,7 +138,7 @@ - + @@ -156,7 +156,7 @@ - + @@ -164,7 +164,7 @@ - + @@ -173,7 +173,7 @@ - + @@ -189,7 +189,7 @@ - + @@ -205,7 +205,7 @@ - + @@ -221,7 +221,7 @@ - + @@ -229,7 +229,7 @@ - + @@ -237,7 +237,7 @@ - + @@ -253,7 +253,7 @@ - + @@ -261,7 +261,7 @@ - + @@ -269,7 +269,7 @@ - + @@ -277,7 +277,7 @@ - + @@ -285,7 +285,7 @@ - + @@ -293,7 +293,7 @@ - + @@ -301,7 +301,7 @@ - + @@ -309,7 +309,7 @@ - + @@ -318,7 +318,7 @@ - + @@ -326,7 +326,7 @@ - + @@ -334,7 +334,7 @@ - + @@ -342,7 +342,7 @@ - + @@ -366,7 +366,7 @@ - + @@ -380,7 +380,7 @@ - + @@ -388,7 +388,7 @@ - + @@ -404,7 +404,7 @@ - + @@ -412,7 +412,7 @@ - + @@ -420,7 +420,7 @@ - + @@ -436,7 +436,7 @@ - + @@ -452,7 +452,7 @@ - + @@ -460,7 +460,7 @@ - + @@ -522,7 +522,7 @@ - + diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 4798f315..6e26afa6 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -97,6 +97,7 @@ public class GuiMain extends JFrame { private final static Logger logger = LoggerFactory.getLogger(GuiMain.class); private static final Properties userProperties = new Properties(); private static final String FILENAME_SETTINGS = "settings.xml"; + private static final String LANGUAGE_RESOURCE_BUNDLE = "language/GuiMain"; public GuiMain() { //todo load language from FILENAME_SETTINGS and set it here. @@ -104,7 +105,7 @@ public GuiMain() { //Locale.setDefault(Locale.ENGLISH); // for tests //Locale.setDefault(Locale.ROOT); // default language for tests $$$setupUI$$$(); - guiTranslation = ResourceBundle.getBundle("GuiTranslation"); + guiTranslation = ResourceBundle.getBundle(LANGUAGE_RESOURCE_BUNDLE, Locale.getDefault()); buttonLoadFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -620,7 +621,7 @@ private void setGuiElementsVisibility() { panelMain.setAutoscrolls(true); advancedSettingsCheckBox = new JCheckBox(); advancedSettingsCheckBox.setSelected(false); - this.$$$loadButtonText$$$(advancedSettingsCheckBox, this.$$$getMessageFromBundle$$$("GuiTranslation", "advancedSettings")); + this.$$$loadButtonText$$$(advancedSettingsCheckBox, this.$$$getMessageFromBundle$$$("language/GuiMain", "advancedSettings")); panelMain.add(advancedSettingsCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); mainScrollPane = new JScrollPane(); panelMain.add(mainScrollPane, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); @@ -634,149 +635,149 @@ private void setGuiElementsVisibility() { final JLabel label1 = new JLabel(); Font label1Font = this.$$$getFont$$$(null, Font.BOLD, 14, label1.getFont()); if (label1Font != null) label1.setFont(label1Font); - this.$$$loadLabelText$$$(label1, this.$$$getMessageFromBundle$$$("GuiTranslation", "KnxGatewayConnectionSettings")); + this.$$$loadLabelText$$$(label1, this.$$$getMessageFromBundle$$$("language/GuiMain", "KnxGatewayConnectionSettings")); panel1.add(label1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelIpGateway = new JLabel(); - this.$$$loadLabelText$$$(labelIpGateway, this.$$$getMessageFromBundle$$$("GuiTranslation", "selectKnxIpGateway")); + this.$$$loadLabelText$$$(labelIpGateway, this.$$$getMessageFromBundle$$$("language/GuiMain", "selectKnxIpGateway")); panel1.add(labelIpGateway, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); panel1.add(comboBoxIpGateways, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label2 = new JLabel(); - this.$$$loadLabelText$$$(label2, this.$$$getMessageFromBundle$$$("GuiTranslation", "ipAddress")); + this.$$$loadLabelText$$$(label2, this.$$$getMessageFromBundle$$$("language/GuiMain", "ipAddress")); panel1.add(label2, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textBoxKnxGatewayIpAddr = new JTextField(); panel1.add(textBoxKnxGatewayIpAddr, new GridConstraints(2, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labeIIpHint = new JLabel(); - this.$$$loadLabelText$$$(labeIIpHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "ipAddrHint")); + this.$$$loadLabelText$$$(labeIIpHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "ipAddrHint")); panel1.add(labeIIpHint, new GridConstraints(2, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldPort = new JTextField(); panel1.add(textFieldPort, new GridConstraints(3, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); final JLabel label3 = new JLabel(); - this.$$$loadLabelText$$$(label3, this.$$$getMessageFromBundle$$$("GuiTranslation", "port")); + this.$$$loadLabelText$$$(label3, this.$$$getMessageFromBundle$$$("language/GuiMain", "port")); panel1.add(label3, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelPortHint = new JLabel(); - this.$$$loadLabelText$$$(labelPortHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "portHint")); + this.$$$loadLabelText$$$(labelPortHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "portHint")); panel1.add(labelPortHint, new GridConstraints(3, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); natCheckBox = new JCheckBox(); - this.$$$loadButtonText$$$(natCheckBox, this.$$$getMessageFromBundle$$$("GuiTranslation", "useNat")); + this.$$$loadButtonText$$$(natCheckBox, this.$$$getMessageFromBundle$$$("language/GuiMain", "useNat")); panel1.add(natCheckBox, new GridConstraints(4, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldTpuart = new JTextField(); panel1.add(textFieldTpuart, new GridConstraints(5, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labelTpuart = new JLabel(); - this.$$$loadLabelText$$$(labelTpuart, this.$$$getMessageFromBundle$$$("GuiTranslation", "tpuart")); + this.$$$loadLabelText$$$(labelTpuart, this.$$$getMessageFromBundle$$$("language/GuiMain", "tpuart")); panel1.add(labelTpuart, new GridConstraints(5, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldSerial = new JTextField(); textFieldSerial.setText(""); panel1.add(textFieldSerial, new GridConstraints(6, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labelSerial = new JLabel(); - this.$$$loadLabelText$$$(labelSerial, this.$$$getMessageFromBundle$$$("GuiTranslation", "serial")); + this.$$$loadLabelText$$$(labelSerial, this.$$$getMessageFromBundle$$$("language/GuiMain", "serial")); panel1.add(labelSerial, new GridConstraints(6, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelMedium = new JLabel(); - this.$$$loadLabelText$$$(labelMedium, this.$$$getMessageFromBundle$$$("GuiTranslation", "medium")); + this.$$$loadLabelText$$$(labelMedium, this.$$$getMessageFromBundle$$$("language/GuiMain", "medium")); panel1.add(labelMedium, new GridConstraints(7, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label4 = new JLabel(); Font label4Font = this.$$$getFont$$$(null, Font.BOLD, 14, label4.getFont()); if (label4Font != null) label4.setFont(label4Font); - this.$$$loadLabelText$$$(label4, this.$$$getMessageFromBundle$$$("GuiTranslation", "UpdaterSettings")); + this.$$$loadLabelText$$$(label4, this.$$$getMessageFromBundle$$$("language/GuiMain", "UpdaterSettings")); panel1.add(label4, new GridConstraints(9, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JSeparator separator1 = new JSeparator(); panel1.add(separator1, new GridConstraints(8, 1, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, new Dimension(-1, 1), 0, false)); final JLabel label5 = new JLabel(); - this.$$$loadLabelText$$$(label5, this.$$$getMessageFromBundle$$$("GuiTranslation", "scenario")); + this.$$$loadLabelText$$$(label5, this.$$$getMessageFromBundle$$$("language/GuiMain", "scenario")); panel1.add(label5, new GridConstraints(10, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelScenarioHint = new JLabel(); labelScenarioHint.setText("Label"); panel1.add(labelScenarioHint, new GridConstraints(10, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); buttonLoadFile = new JButton(); - this.$$$loadButtonText$$$(buttonLoadFile, this.$$$getMessageFromBundle$$$("GuiTranslation", "loadFile")); + this.$$$loadButtonText$$$(buttonLoadFile, this.$$$getMessageFromBundle$$$("language/GuiMain", "loadFile")); panel1.add(buttonLoadFile, new GridConstraints(11, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldFileName = new JTextField(); panel1.add(textFieldFileName, new GridConstraints(12, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labelFileName = new JLabel(); - this.$$$loadLabelText$$$(labelFileName, this.$$$getMessageFromBundle$$$("GuiTranslation", "fileName")); + this.$$$loadLabelText$$$(labelFileName, this.$$$getMessageFromBundle$$$("language/GuiMain", "fileName")); panel1.add(labelFileName, new GridConstraints(12, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelFileNameHint = new JLabel(); - this.$$$loadLabelText$$$(labelFileNameHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "fileNameHint")); + this.$$$loadLabelText$$$(labelFileNameHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "fileNameHint")); panel1.add(labelFileNameHint, new GridConstraints(12, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); buttonRequestUid = new JButton(); - this.$$$loadButtonText$$$(buttonRequestUid, this.$$$getMessageFromBundle$$$("GuiTranslation", "requestUid")); + this.$$$loadButtonText$$$(buttonRequestUid, this.$$$getMessageFromBundle$$$("language/GuiMain", "requestUid")); panel1.add(buttonRequestUid, new GridConstraints(13, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldUid = new JTextField(); panel1.add(textFieldUid, new GridConstraints(14, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labelUid = new JLabel(); - this.$$$loadLabelText$$$(labelUid, this.$$$getMessageFromBundle$$$("GuiTranslation", "uid")); + this.$$$loadLabelText$$$(labelUid, this.$$$getMessageFromBundle$$$("language/GuiMain", "uid")); panel1.add(labelUid, new GridConstraints(14, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelUidHint = new JLabel(); - this.$$$loadLabelText$$$(labelUidHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "uidHint")); + this.$$$loadLabelText$$$(labelUidHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "uidHint")); panel1.add(labelUidHint, new GridConstraints(14, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); CheckBoxDiffFlash = new JCheckBox(); - this.$$$loadButtonText$$$(CheckBoxDiffFlash, this.$$$getMessageFromBundle$$$("GuiTranslation", "diffFlash")); + this.$$$loadButtonText$$$(CheckBoxDiffFlash, this.$$$getMessageFromBundle$$$("language/GuiMain", "diffFlash")); panel1.add(CheckBoxDiffFlash, new GridConstraints(15, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelFullFlashHint = new JLabel(); - this.$$$loadLabelText$$$(labelFullFlashHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "diffFlashHint")); + this.$$$loadLabelText$$$(labelFullFlashHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "diffFlashHint")); panel1.add(labelFullFlashHint, new GridConstraints(15, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); eraseCompleteFlashCheckBox = new JCheckBox(); - this.$$$loadButtonText$$$(eraseCompleteFlashCheckBox, this.$$$getMessageFromBundle$$$("GuiTranslation", "eraseFlash")); + this.$$$loadButtonText$$$(eraseCompleteFlashCheckBox, this.$$$getMessageFromBundle$$$("language/GuiMain", "eraseFlash")); panel1.add(eraseCompleteFlashCheckBox, new GridConstraints(16, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelEraseCompleteFlash = new JLabel(); - this.$$$loadLabelText$$$(labelEraseCompleteFlash, this.$$$getMessageFromBundle$$$("GuiTranslation", "eraseCompleteFlashHint")); + this.$$$loadLabelText$$$(labelEraseCompleteFlash, this.$$$getMessageFromBundle$$$("language/GuiMain", "eraseCompleteFlashHint")); panel1.add(labelEraseCompleteFlash, new GridConstraints(16, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); noFlashCheckBox = new JCheckBox(); - this.$$$loadButtonText$$$(noFlashCheckBox, this.$$$getMessageFromBundle$$$("GuiTranslation", "noFlash")); + this.$$$loadButtonText$$$(noFlashCheckBox, this.$$$getMessageFromBundle$$$("language/GuiMain", "noFlash")); panel1.add(noFlashCheckBox, new GridConstraints(17, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelNoFlashHint = new JLabel(); - this.$$$loadLabelText$$$(labelNoFlashHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "noFlashHint")); + this.$$$loadLabelText$$$(labelNoFlashHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "noFlashHint")); panel1.add(labelNoFlashHint, new GridConstraints(17, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label6 = new JLabel(); Font label6Font = this.$$$getFont$$$(null, Font.BOLD, 14, label6.getFont()); if (label6Font != null) label6.setFont(label6Font); - this.$$$loadLabelText$$$(label6, this.$$$getMessageFromBundle$$$("GuiTranslation", "KnxBusSettings")); + this.$$$loadLabelText$$$(label6, this.$$$getMessageFromBundle$$$("language/GuiMain", "KnxBusSettings")); panel1.add(label6, new GridConstraints(19, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelBootloaderDeviceAddr = new JLabel(); - this.$$$loadLabelText$$$(labelBootloaderDeviceAddr, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxProgDeviceAddr")); + this.$$$loadLabelText$$$(labelBootloaderDeviceAddr, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxProgDeviceAddr")); panel1.add(labelBootloaderDeviceAddr, new GridConstraints(20, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelDeviceAddress = new JLabel(); - this.$$$loadLabelText$$$(labelDeviceAddress, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxDeviceAddr")); + this.$$$loadLabelText$$$(labelDeviceAddress, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxDeviceAddr")); panel1.add(labelDeviceAddress, new GridConstraints(21, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelOwnAddress = new JLabel(); - this.$$$loadLabelText$$$(labelOwnAddress, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxOwnAddress")); + this.$$$loadLabelText$$$(labelOwnAddress, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxOwnAddress")); panel1.add(labelOwnAddress, new GridConstraints(22, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldOwnAddress = new JTextField(); panel1.add(textFieldOwnAddress, new GridConstraints(22, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); textFieldDelay = new JTextField(); panel1.add(textFieldDelay, new GridConstraints(23, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labelDelay = new JLabel(); - this.$$$loadLabelText$$$(labelDelay, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxMessageDelay")); + this.$$$loadLabelText$$$(labelDelay, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxMessageDelay")); panel1.add(labelDelay, new GridConstraints(23, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); panel1.add(comboBoxKnxTelegramPriority, new GridConstraints(25, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelTelegramPriority = new JLabel(); - this.$$$loadLabelText$$$(labelTelegramPriority, this.$$$getMessageFromBundle$$$("GuiTranslation", "messagePriority")); + this.$$$loadLabelText$$$(labelTelegramPriority, this.$$$getMessageFromBundle$$$("language/GuiMain", "messagePriority")); panel1.add(labelTelegramPriority, new GridConstraints(25, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelKnxSecureUser = new JLabel(); - this.$$$loadLabelText$$$(labelKnxSecureUser, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxSecureUser")); + this.$$$loadLabelText$$$(labelKnxSecureUser, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxSecureUser")); panel1.add(labelKnxSecureUser, new GridConstraints(26, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldKnxSecureUser = new JTextField(); panel1.add(textFieldKnxSecureUser, new GridConstraints(26, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labelKnxSecureUserHint = new JLabel(); - this.$$$loadLabelText$$$(labelKnxSecureUserHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxSecureUserHint")); + this.$$$loadLabelText$$$(labelKnxSecureUserHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxSecureUserHint")); panel1.add(labelKnxSecureUserHint, new GridConstraints(26, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelKnxSecureUserPwd = new JLabel(); - this.$$$loadLabelText$$$(labelKnxSecureUserPwd, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxSecureUserPwd")); + this.$$$loadLabelText$$$(labelKnxSecureUserPwd, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxSecureUserPwd")); panel1.add(labelKnxSecureUserPwd, new GridConstraints(27, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelKnxSecureUserPwdHint = new JLabel(); - this.$$$loadLabelText$$$(labelKnxSecureUserPwdHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxSecureUserPwdHint")); + this.$$$loadLabelText$$$(labelKnxSecureUserPwdHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxSecureUserPwdHint")); panel1.add(labelKnxSecureUserPwdHint, new GridConstraints(27, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldKnxSecureUserPwd = new JTextField(); panel1.add(textFieldKnxSecureUserPwd, new GridConstraints(27, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labelKnxSecureDevicePwd = new JLabel(); - this.$$$loadLabelText$$$(labelKnxSecureDevicePwd, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxSecureDevicePwd")); + this.$$$loadLabelText$$$(labelKnxSecureDevicePwd, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxSecureDevicePwd")); panel1.add(labelKnxSecureDevicePwd, new GridConstraints(28, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); textFieldKnxSecureDevicePwd = new JTextField(); panel1.add(textFieldKnxSecureDevicePwd, new GridConstraints(28, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); labelKnxSecureDevicePwdHint = new JLabel(); - this.$$$loadLabelText$$$(labelKnxSecureDevicePwdHint, this.$$$getMessageFromBundle$$$("GuiTranslation", "knxSecureDevicePwdHint")); + this.$$$loadLabelText$$$(labelKnxSecureDevicePwdHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "knxSecureDevicePwdHint")); panel1.add(labelKnxSecureDevicePwdHint, new GridConstraints(28, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); buttonStartStopFlash = new JButton(); - this.$$$loadButtonText$$$(buttonStartStopFlash, this.$$$getMessageFromBundle$$$("GuiTranslation", "startFlash")); + this.$$$loadButtonText$$$(buttonStartStopFlash, this.$$$getMessageFromBundle$$$("language/GuiMain", "startFlash")); panel1.add(buttonStartStopFlash, new GridConstraints(29, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JScrollPane scrollPane1 = new JScrollPane(); panel1.add(scrollPane1, new GridConstraints(30, 0, 1, 5, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, new Dimension(-1, 200), new Dimension(-1, 200), null, 0, false)); @@ -792,7 +793,7 @@ private void setGuiElementsVisibility() { panel1.add(comboBoxMedium, new GridConstraints(7, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); panel1.add(comboBoxScenario, new GridConstraints(10, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); reloadGatewaysButton = new JButton(); - this.$$$loadButtonText$$$(reloadGatewaysButton, this.$$$getMessageFromBundle$$$("GuiTranslation", "reloadKnxIpGateways")); + this.$$$loadButtonText$$$(reloadGatewaysButton, this.$$$getMessageFromBundle$$$("language/GuiMain", "reloadKnxIpGateways")); panel1.add(reloadGatewaysButton, new GridConstraints(1, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); } diff --git a/firmware_updater/updater/src/resources/GuiTranslation.properties b/firmware_updater/updater/src/resources/language/GuiMain.properties similarity index 100% rename from firmware_updater/updater/src/resources/GuiTranslation.properties rename to firmware_updater/updater/src/resources/language/GuiMain.properties diff --git a/firmware_updater/updater/src/resources/GuiTranslation_de.properties b/firmware_updater/updater/src/resources/language/GuiMain_de.properties similarity index 100% rename from firmware_updater/updater/src/resources/GuiTranslation_de.properties rename to firmware_updater/updater/src/resources/language/GuiMain_de.properties From 042ee6262cd6cf6f8d7e4938f3f834509dce3fd3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 15:53:05 +0200 Subject: [PATCH 133/359] [GUI] Fix warning hardcoded value --- .../updater/src/org/selfbus/updater/gui/GuiMain.form | 2 +- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 5 ++--- .../updater/src/resources/language/GuiMain.properties | 3 ++- .../updater/src/resources/language/GuiMain_de.properties | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form index 8f4414a0..f63a2c74 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form @@ -197,7 +197,7 @@ - + diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 6e26afa6..99221e83 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -423,8 +423,7 @@ private void setFrameImages() { Image image = ImageIO.read(imageStream); frameImageList.add(image); logger.debug("Added {} to frameImageList", resourceName); - } - catch (IOException e) { + } catch (IOException e) { logger.error("Could not add {} to frameImageList {}", resourceName, Arrays.toString(e.getStackTrace())); } } @@ -685,7 +684,7 @@ private void setGuiElementsVisibility() { this.$$$loadLabelText$$$(label5, this.$$$getMessageFromBundle$$$("language/GuiMain", "scenario")); panel1.add(label5, new GridConstraints(10, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); labelScenarioHint = new JLabel(); - labelScenarioHint.setText("Label"); + this.$$$loadLabelText$$$(labelScenarioHint, this.$$$getMessageFromBundle$$$("language/GuiMain", "labelScenarioHint")); panel1.add(labelScenarioHint, new GridConstraints(10, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); buttonLoadFile = new JButton(); this.$$$loadButtonText$$$(buttonLoadFile, this.$$$getMessageFromBundle$$$("language/GuiMain", "loadFile")); diff --git a/firmware_updater/updater/src/resources/language/GuiMain.properties b/firmware_updater/updater/src/resources/language/GuiMain.properties index 9d2138b4..91dc9475 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain.properties @@ -59,4 +59,5 @@ reloadKnxIpGateways=reload gateways selectInterface=select Interface diffFlash=differential Flash diffFlashHint=flash only changed parts of software
\\ [experimental] -logMessageCanceledFlashing=Flash process canceled. \ No newline at end of file +logMessageCanceledFlashing=Flash process canceled. +labelScenarioHint=Label \ No newline at end of file diff --git a/firmware_updater/updater/src/resources/language/GuiMain_de.properties b/firmware_updater/updater/src/resources/language/GuiMain_de.properties index efcf85fc..3398d6d2 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain_de.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain_de.properties @@ -61,4 +61,5 @@ selectInterface=w diffFlash=differenzieller Flash Vorgang diffFlashHint=Es werden nur veränderte Teile der Software
\ übertragen [experimentell] -logMessageCanceledFlashing=Flash Vorgang wurde abgebrochen. \ No newline at end of file +logMessageCanceledFlashing=Flash Vorgang wurde abgebrochen. +labelScenarioHint=Label \ No newline at end of file From 4671ef35e54c604dcd9f45c3a4879bf031078c0c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 18:03:57 +0200 Subject: [PATCH 134/359] [GUI] Only one element. No need for list. --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 99221e83..6909c98c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -516,7 +516,7 @@ private void initGuiElementsVisibility() { GuiObjectsMap.put(labelFileName, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV)); GuiObjectsMap.put(labelFileNameHint, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV)); - GuiObjectsMap.put(buttonRequestUid, Arrays.asList(GuiObjsVisOpts.REQUID)); + GuiObjectsMap.put(buttonRequestUid, List.of(GuiObjsVisOpts.REQUID)); GuiObjectsMap.put(labelUid, Arrays.asList(GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID)); GuiObjectsMap.put(textFieldUid, Arrays.asList(GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID)); @@ -531,8 +531,8 @@ private void initGuiElementsVisibility() { GuiObjectsMap.put(labelTpuart, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID, GuiObjsVisOpts.ADVSET)); GuiObjectsMap.put(textFieldTpuart, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID, GuiObjsVisOpts.ADVSET)); - GuiObjectsMap.put(labelDeviceAddress, Arrays.asList(GuiObjsVisOpts.APPDEV)); - GuiObjectsMap.put(textFieldDeviceAddress, Arrays.asList(GuiObjsVisOpts.APPDEV)); + GuiObjectsMap.put(labelDeviceAddress, List.of(GuiObjsVisOpts.APPDEV)); + GuiObjectsMap.put(textFieldDeviceAddress, List.of(GuiObjsVisOpts.APPDEV)); GuiObjectsMap.put(labelBootloaderDeviceAddr, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID, GuiObjsVisOpts.ADVSET)); GuiObjectsMap.put(textFieldBootloaderDeviceAddress, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID, GuiObjsVisOpts.ADVSET)); From 6af9a5b74fe6b2e5d4af05183e9017c90a60beb7 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 18:04:31 +0200 Subject: [PATCH 135/359] =?UTF-8?q?[Updater]=20Don=C2=B4t=20use=20custom?= =?UTF-8?q?=20tag=20`@warning`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../selfbus/updater/gui/ConColorsToStyledDoc.java | 5 +++-- .../src/org/selfbus/updater/upd/UPDCommand.java | 12 +++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index defa7026..b217d343 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -455,7 +455,8 @@ public static void setCursorPosition(JTextPane textPane, Point newPosition) thro * It invokes the {@link #colorCodeToStyle} method with the provided color codes. * * @param colorCodes an array of color codes to be converted to styles. - * @warning This method is intended for unit tests only. + * + *

Warning: This method is intended for unit tests only.

*/ public static void testColorCodeToStyle(String [] colorCodes) { colorCodeToStyle(colorCodes); @@ -466,7 +467,7 @@ public static void testColorCodeToStyle(String [] colorCodes) { * It returns the current {@link #stringStyle}. * * @return the current string style. - * @warning This method is intended for unit tests only. + *

Warning: This method is intended for unit tests only.

*/ public static Style testStringStyle() { return stringStyle; diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java index 609b61a0..8a927e42 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java @@ -18,8 +18,11 @@ public enum UPDCommand { PROGRAM_DECOMPRESSED_DATA((byte)0xeb, "PROGRAM_DECOMPRESSED_DATA"), //!< Flash bytes from ramBuffer to flash with differential method @note device must be unlocked ERASE_COMPLETE_FLASH((byte)0xea, "ERASE_COMPLETE_FLASH"), //!< Erase the entire flash area excluding the bootloader itself @note device must be unlocked ERASE_ADDRESS_RANGE((byte)0xe9, "ERASE_ADDRESS_RANGE"), //!< Erase flash from given start address to end address (start: data[3-6] end: data[7-10]) @note device must be unlocked - REQ_DATA((byte)0xe8, "REQ_DATA"), //!< Return bytes from flash at given address? @note device must be unlocked - //!<@warning Not implemented + /** + * Return bytes from flash at given address? @note device must be unlocked + *

Warning:Not implemented.

+ */ + REQ_DATA((byte)0xe8, "REQ_DATA"), DUMP_FLASH((byte)0xe7, "DUMP_FLASH"), //!< DUMP the flash of a given address range (data[0-3] - data[4-7]) to serial port of the mcu, works only with DEBUG version of bootloader REQUEST_STATISTIC((byte)0xdf, "STATISTIC_REQUEST"), //!< Return some statistic data for the active connection RESPONSE_STATISTIC((byte)0xde, "STATISTIC_RESPONSE"), //!< Response for @ref UPD_STATISTIC_RESPONSE containing the statistic data @@ -35,7 +38,10 @@ public enum UPDCommand { REQUEST_BL_IDENTITY((byte)0xb8, "REQUEST_BL_IDENTITY"), //!< Return the bootloader's identity @note device must be unlocked RESPONSE_BL_IDENTITY((byte)0xb7, "RESPONSE_BL_IDENTITY"), //!< Response for @ref UPD_REQUEST_BL_IDENTITY containing the identity RESPONSE_BL_VERSION_MISMATCH((byte)0xb6, "RESPONSE_BL_VERSION_MISMATCH"), //!< Response for @ref UPD_REQUEST_BL_IDENTITY containing the minimum required major and minor version of Selfbus Updater - SET_EMULATION((byte)0x01, "SET_EMULATION"); //!<@warning Not implemented + /** + *

Warning:Not implemented.

+ */ + SET_EMULATION((byte)0x01, "SET_EMULATION"); private static final Map BY_INDEX = new HashMap<>(); static { From 8a0fa9ecb28e48eb94e8f06e09effe4b3ed2b159 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 18:52:22 +0200 Subject: [PATCH 136/359] [Updater] Use Javadoc not DoxyGen --- .../org/selfbus/updater/DeviceManagement.java | 20 ++- .../src/org/selfbus/updater/Updater.java | 25 ++- .../updater/gui/ConColorsToStyledDoc.java | 9 +- .../org/selfbus/updater/upd/UPDCommand.java | 144 ++++++++++++++---- .../org/selfbus/updater/upd/UPDProtocol.java | 10 +- 5 files changed, 161 insertions(+), 47 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 9817bff0..dfb26cd7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -31,10 +31,19 @@ * Provides methods to send firmware update telegrams to the bootloader (MCU) */ public final class DeviceManagement { - private static final int RESTART_ERASE_CODE = 7; //!< EraseCode for the APCI_MASTER_RESET_PDU (valid from 1..7) - private static final int RESTART_CHANNEL = 255; //!< Channelnumber for the APCI_MASTER_RESET_PDU + /** + * EraseCode for the APCI_MASTER_RESET_PDU (valid from 1..7) + */ + private static final int RESTART_ERASE_CODE = 7; + /** + * Channelnumber for the APCI_MASTER_RESET_PDU + */ + private static final int RESTART_CHANNEL = 255; - private static final int MAX_UPD_COMMAND_RETRY = 3; //!< default maximum retries a UPD command is sent to the client + /** + * Default maximum retries a UPD command is sent to the client + */ + private static final int MAX_UPD_COMMAND_RETRY = 3; private UDPProtocolVersion protocolVersion; @@ -47,7 +56,10 @@ private DeviceManagement () { } private final static Logger logger = LoggerFactory.getLogger(DeviceManagement.class); - private SBManagementClientImpl mc; //!< calimero device management client + /** + * Calimero device management client + */ + private SBManagementClientImpl mc; private Destination progDestination; private KNXNetworkLink link; diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 0587c4ed..827e9034 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -147,11 +147,26 @@ public void run() { } } - public static final int DELAY_MIN = 0; //!< minimum delay between two UPDCommand.SEND_DATA telegrams in milliseconds - public static final int DELAY_MAX = 500; //!< maximum delay between two UPDCommand.SEND_DATA telegrams in milliseconds - public static final int DELAY_DEFAULT = 100; //!< default delay between two UPDCommand.SEND_DATA telegrams in milliseconds - public static final IndividualAddress PHYS_ADDRESS_BOOTLOADER = new IndividualAddress(15, 15,192); //!< physical address the bootloader is using - public static final IndividualAddress PHYS_ADDRESS_OWN = new IndividualAddress(0, 0,0); //!< physical address the Selfbus Updater is using + /** + * Minimum delay between two UPDCommand.SEND_DATA telegrams in milliseconds + */ + public static final int DELAY_MIN = 0; + /** + * Maximum delay between two UPDCommand.SEND_DATA telegrams in milliseconds + */ + public static final int DELAY_MAX = 500; + /** + * Default delay between two UPDCommand.SEND_DATA telegrams in milliseconds + */ + public static final int DELAY_DEFAULT = 100; + /** + * Physical address the bootloader is using + */ + public static final IndividualAddress PHYS_ADDRESS_BOOTLOADER = new IndividualAddress(15, 15,192); + /** + * Physical address the Selfbus Updater is using + */ + public static final IndividualAddress PHYS_ADDRESS_OWN = new IndividualAddress(0, 0,0); /* * (non-Javadoc) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index b217d343..5eb35996 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -104,7 +104,7 @@ public static void Convert(String input, JTextPane textPane) throws BadLocationE /** * Processes ANSI cursor control codes and updates the cursor position in the given {@link JTextPane}. * - *

Warning: This method is partially implemented. Some cursor movement codes are not yet handled and will throw an exception if encountered.

* + *

Warning: This method is partially implemented. Some cursor movement codes are not yet handled and will throw an exception if encountered.

*

When encountering an unsupported ANSI cursor movement code, an {@link IllegalStateException} is thrown.

* * @param ansiCode the ANSI code to process @@ -451,23 +451,20 @@ public static void setCursorPosition(JTextPane textPane, Point newPosition) thro } /** - * This method is intended for unit tests only. + *

Warning: This method is intended for unit tests only.

* It invokes the {@link #colorCodeToStyle} method with the provided color codes. * * @param colorCodes an array of color codes to be converted to styles. - * - *

Warning: This method is intended for unit tests only.

*/ public static void testColorCodeToStyle(String [] colorCodes) { colorCodeToStyle(colorCodes); } /** - * This method is intended for unit tests only. + *

Warning: This method is intended for unit tests only.

* It returns the current {@link #stringStyle}. * * @return the current string style. - *

Warning: This method is intended for unit tests only.

*/ public static Style testStringStyle() { return stringStyle; diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java index 8a927e42..4ac6503e 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java @@ -6,40 +6,124 @@ /** * Implementation of the UPD/UDP protocol control commands *

- * see /firmware_updater/bootloader/inc/upd_protocol.h for details + * See {@code /firmware_updater/bootloader/inc/upd_protocol.h} for details + *

*/ public enum UPDCommand { - // ERASE_SECTOR(0, "ERASE_SECTOR"), //!< Erase flash sector number (data[3]) @note device must be unlocked - //!< @note deprecated, use ERASE_ADDRESSRANGE instead - SEND_DATA((byte)0xef, "SEND_DATA"), //!< Copy ((data[0] & 0x0f)-1) bytes to ramBuffer starting from address data[3] @note device must be unlocked - PROGRAM((byte)0xee, "PROGRAM"), //!< Copy count (data[3-6]) bytes from ramBuffer to address (data[7-10]) in flash buffer, crc in data[11-14] @note device must be unlocked - UPDATE_BOOT_DESC((byte)0xed, "UPDATE_BOOT_DESC"), //!< Flash an application boot descriptor block @note device must be unlocked - SEND_DATA_TO_DECOMPRESS((byte)0xec, "SEND_DATA_TO_DECOMPRESS"), //!< Copy bytes from telegram (data) to ramBuffer with differential method @note device must be unlocked - PROGRAM_DECOMPRESSED_DATA((byte)0xeb, "PROGRAM_DECOMPRESSED_DATA"), //!< Flash bytes from ramBuffer to flash with differential method @note device must be unlocked - ERASE_COMPLETE_FLASH((byte)0xea, "ERASE_COMPLETE_FLASH"), //!< Erase the entire flash area excluding the bootloader itself @note device must be unlocked - ERASE_ADDRESS_RANGE((byte)0xe9, "ERASE_ADDRESS_RANGE"), //!< Erase flash from given start address to end address (start: data[3-6] end: data[7-10]) @note device must be unlocked - /** - * Return bytes from flash at given address? @note device must be unlocked - *

Warning:Not implemented.

+ /* + * Erase flash sector number (data[3]). + *

Device must be unlocked.

+ * @deprecated Use ERASE_ADDRESSRANGE instead. + */ + //ERASE_SECTOR((byte) 0, "ERASE_SECTOR"), + /** + * Copy ((data[0] & 0x0f)-1) bytes to ramBuffer starting from address data[3]. + *

Device must be unlocked.

+ */ + SEND_DATA((byte)0xef, "SEND_DATA"), + /** + * Copy count (data[3-6]) bytes from ramBuffer to address (data[7-10]) in flash buffer, crc in data[11-14]. + *

Device must be unlocked.

+ */ + PROGRAM((byte)0xee, "PROGRAM"), + /** + * Flash an application boot descriptor block. + *

Device must be unlocked.

+ */ + UPDATE_BOOT_DESC((byte)0xed, "UPDATE_BOOT_DESC"), + /** + * Copy bytes from telegram (data) to ramBuffer with differential method. + *

Device must be unlocked.

+ */ + SEND_DATA_TO_DECOMPRESS((byte)0xec, "SEND_DATA_TO_DECOMPRESS"), + /** + * Flash bytes from ramBuffer to flash with differential method. + *

Device must be unlocked.

+ */ + PROGRAM_DECOMPRESSED_DATA((byte)0xeb, "PROGRAM_DECOMPRESSED_DATA"), + /** + * Erase the entire flash area excluding the bootloader itself. + *

Device must be unlocked.

+ */ + ERASE_COMPLETE_FLASH((byte)0xea, "ERASE_COMPLETE_FLASH"), + /** + * Erase flash from given start address to end address (start: data[3-6] end: data[7-10]). + *

Device must be unlocked.

+ */ + ERASE_ADDRESS_RANGE((byte)0xe9, "ERASE_ADDRESS_RANGE"), + /** + *

Warning: Not implemented.

+ * Return bytes from flash at given address. */ REQ_DATA((byte)0xe8, "REQ_DATA"), - DUMP_FLASH((byte)0xe7, "DUMP_FLASH"), //!< DUMP the flash of a given address range (data[0-3] - data[4-7]) to serial port of the mcu, works only with DEBUG version of bootloader - REQUEST_STATISTIC((byte)0xdf, "STATISTIC_REQUEST"), //!< Return some statistic data for the active connection - RESPONSE_STATISTIC((byte)0xde, "STATISTIC_RESPONSE"), //!< Response for @ref UPD_STATISTIC_RESPONSE containing the statistic data - SEND_LAST_ERROR((byte)0xdc, "SEND_LAST_ERROR"), //!< Response containing the last error - UNLOCK_DEVICE((byte)0xbf, "UNLOCK_DEVICE"), //!< Unlock the device for operations, which are only allowed on an unlocked device - REQUEST_UID((byte)0xbe, "REQUEST_UID"), //!< Return the 12 byte shorten UID (GUID) of the mcu @note device must be unlocked - RESPONSE_UID((byte)0xbd, "RESPONSE_UID"), //!< Response for @ref REQUEST_UID containing the first 12 bytes of the UID - APP_VERSION_REQUEST((byte)0xbc, "APP_VERSION_REQUEST"), //!< Return address of AppVersion string - APP_VERSION_RESPONSE((byte)0xbb, "APP_VERSION_RESPONSE"), //!< Response for @ref APP_VERSION_REQUEST containing the application version string - // RESET(35, "RESET"), //!< Reset the device @note device must be unlocked - REQUEST_BOOT_DESC((byte)0xba, "REQUEST_BOOT_DESC"), //!< Return the application boot descriptor block @note device must be unlocked - RESPONSE_BOOT_DESC((byte)0xb9, "RESPONSE_BOOT_DESC"), //!< Response for @ref REQUEST_BOOT_DESC containing the application boot descriptor block - REQUEST_BL_IDENTITY((byte)0xb8, "REQUEST_BL_IDENTITY"), //!< Return the bootloader's identity @note device must be unlocked - RESPONSE_BL_IDENTITY((byte)0xb7, "RESPONSE_BL_IDENTITY"), //!< Response for @ref UPD_REQUEST_BL_IDENTITY containing the identity - RESPONSE_BL_VERSION_MISMATCH((byte)0xb6, "RESPONSE_BL_VERSION_MISMATCH"), //!< Response for @ref UPD_REQUEST_BL_IDENTITY containing the minimum required major and minor version of Selfbus Updater - /** - *

Warning:Not implemented.

+ /** + * DUMP the flash of a given address range (data[0-3] - data[4-7]) to the serial port of the mcu. + *

Note: Works only with DEBUG version of the bootloader.

+ */ + DUMP_FLASH((byte)0xe7, "DUMP_FLASH"), + /** + * Request some statistic data for the active connection. + */ + REQUEST_STATISTIC((byte)0xdf, "STATISTIC_REQUEST"), + /** + * Response for {@link #REQUEST_STATISTIC} containing the statistic data. + */ + RESPONSE_STATISTIC((byte)0xde, "STATISTIC_RESPONSE"), + /** + * Response containing the last error. + */ + SEND_LAST_ERROR((byte)0xdc, "SEND_LAST_ERROR"), + /** + * Unlock the device for operations, which are only allowed on an unlocked device. + */ + UNLOCK_DEVICE((byte)0xbf, "UNLOCK_DEVICE"), + /** + * Request the 12 byte shorten UID (GUID) of the mcu. + *

Device must be unlocked.

+ */ + REQUEST_UID((byte)0xbe, "REQUEST_UID"), + /** + * Response for {@link #REQUEST_UID} containing the first 12 bytes of the UID. + */ + RESPONSE_UID((byte)0xbd, "RESPONSE_UID"), + /** + * Request address of AppVersion string. + */ + APP_VERSION_REQUEST((byte)0xbc, "APP_VERSION_REQUEST"), + /** + * Response for {@link #APP_VERSION_REQUEST} containing the application version string. + */ + APP_VERSION_RESPONSE((byte)0xbb, "APP_VERSION_RESPONSE"), + /* + * Reset the device. + *

Device must be unlocked.

+ */ + //RESET((byte) 35, "RESET"), + /** + * Request the application boot descriptor block. + *

Device must be unlocked.

+ */ + REQUEST_BOOT_DESC((byte)0xba, "REQUEST_BOOT_DESC"), + /** + * Response for {@link #REQUEST_BOOT_DESC} containing the application boot descriptor block. + */ + RESPONSE_BOOT_DESC((byte)0xb9, "RESPONSE_BOOT_DESC"), + /** + * Request the bootloader's identity. + *

Device must be unlocked.

+ */ + REQUEST_BL_IDENTITY((byte)0xb8, "REQUEST_BL_IDENTITY"), + /** + * Response for {@link #REQUEST_BL_IDENTITY} containing the identity. + */ + RESPONSE_BL_IDENTITY((byte)0xb7, "RESPONSE_BL_IDENTITY"), + /** + * Negative response for {@link #REQUEST_BL_IDENTITY} + * containing the minimum required major and minor version of the Selfbus Updater. + */ + RESPONSE_BL_VERSION_MISMATCH((byte)0xb6, "RESPONSE_BL_VERSION_MISMATCH"), + /** + *

Warning: Not implemented.

*/ SET_EMULATION((byte)0x01, "SET_EMULATION"); diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java index 3ebe8350..fc7da52e 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java @@ -14,8 +14,14 @@ public final class UPDProtocol { public static final int COMMAND_POSITION = 2; public static final int DATA_POSITION = 3; - public static final int UID_LENGTH_USED = 12; //!< uid/guid length of the mcu used for unlocking/flashing - public static final int UID_LENGTH_MAX = 16; //!< uid/guid length of the mcu + /** + * uid/guid length of the mcu used for unlocking/flashing. + */ + public static final int UID_LENGTH_USED = 12; + /** + * uid/guid length of the mcu. + */ + public static final int UID_LENGTH_MAX = 16; private UPDProtocol() {} From 39dc7c30d9a15552a5ccea6eed47558da4234256 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 21:41:19 +0200 Subject: [PATCH 137/359] [Updater] Use `System.lineSeparator()` instead of `\n` --- .../updater/src/org/selfbus/updater/ToolInfo.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 403eb788..5f3f409c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -29,8 +29,6 @@ private ToolInfo() {} /** * Returns the version as string representation. - *

- * The returned version * * @return version as string */ @@ -57,7 +55,8 @@ public static String getTool() { } public static String getFullInfo() { - return getToolAndVersion() + String.format("\n%s\n%s", desc, author); + return getToolAndVersion() + String.format("%s%s%s%s", System.lineSeparator(), desc, + System.lineSeparator(),author); } public static String getToolAndVersion() { From 844b4661901037c412316687caf13130c9f82e6c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 10 Aug 2024 22:01:26 +0200 Subject: [PATCH 138/359] [Updater] Reorder startup from GUI and console - GUI and console mode now use the same `Updater` constructor - Most startup "logic" is now done in `main(..)` and notr in several constructors/methods - Credits and ASCII logo now only display when using `--version` --- .../src/org/selfbus/updater/Credits.java | 34 +++++ .../src/org/selfbus/updater/Updater.java | 137 ++++++++---------- 2 files changed, 91 insertions(+), 80 deletions(-) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/Credits.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/Credits.java b/firmware_updater/updater/src/org/selfbus/updater/Credits.java new file mode 100644 index 00000000..4dd9bfdd --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/Credits.java @@ -0,0 +1,34 @@ +package org.selfbus.updater; + +public final class Credits { + @SuppressWarnings("unused") + private Credits() {} + + private static final String[] authors = { + "Deti Fliegl", + "Pavel Kriz", + "Dr. Stefan Haller", + "Oliver Stefan", + "et al.", + }; + + public static String getAsciiLogo() { + return """ + _____ ________ __________ __ _______ __ ______ ____ ___ ________________ + / ___// ____/ / / ____/ __ )/ / / / ___/ / / / / __ \\/ __ \\/ |/_ __/ ____/ __ \\ + \\__ \\/ __/ / / / /_ / __ / / / /\\__ \\ / / / / /_/ / / / / /| | / / / __/ / /_/ / + ___/ / /___/ /___/ __/ / /_/ / /_/ /___/ / / /_/ / ____/ /_/ / ___ |/ / / /___/ _, _/ + /____/_____/_____/_/ /_____/\\____//____/ \\____/_/ /_____/_/ |_/_/ /_____/_/ |_|"""; + } + + public static String getAuthors() { + StringBuilder result = new StringBuilder("by "); + for (int i = 0; i < authors.length; i++) { + result.append(authors[i]); + if (i < authors.length - 1) { + result.append(", "); + } + } + return result.toString(); + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 827e9034..1dc70ca5 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -20,97 +20,83 @@ import org.selfbus.updater.gui.GuiMain; /** - * A Tool for updating firmware of a Selfbus device in a KNX network. + * A Tool for updating the firmware of a Selfbus device in a KNX network. *

- * Updater is a {@link Runnable} tool implementation allowing a user to update - * KNXduino devices.
+ * {@link Updater} is a {@link Runnable} tool implementation allowing + * a user to update Selfbus KNX devices.
*
- * This tool supports KNX network access using a KNXnet/IP connection FT1.2 or TPUART - * connection. It uses the {@link SBManagementClientImpl} functionality of the library - * to read KNX device description, properties, and memory locations. It collects - * and shows device information similar to the ETS. + * This tool supports KNX network access using a KNXnet/IP, USB, FT1.2 or TPUART + * connection. It uses the {@link DeviceManagement} to access the Selfbus KNX device. *

- * When running this tool from the console, the main- method of - * this class is invoked, otherwise use this class in the context appropriate to - * a {@link Runnable}.
- * In console mode, the KNX device information, as well as errors and problems - * during its execution are written to logback LOGGER. - *

- * CAUTION: - * {@link SBManagementClientImpl} uses java reflections to get access to private field's/method's - * of calimero-core's ManagementClientImpl - * - * @author Deti Fliegl - * @author Pavel Kriz - * @author Stefan Haller - * @author Oliver Stefan + * When running this tool from the console, the {@code main} method of this class is invoked, + * otherwise use this class in the context appropriate to a {@link Runnable}.
*/ public class Updater implements Runnable { + @SuppressWarnings("unused") + private Updater() {} // disable default constructor + private final static Logger logger = LoggerFactory.getLogger(Updater.class); - private final CliOptions cliOptions; - private final SBKNXLink sbKNXLink; + private CliOptions cliOptions = null; + private SBKNXLink sbKNXLink = null; /** - * Creates a new Updater instance using the supplied options. - *

- * Mandatory arguments are an IP host/address or a FT1.2 port identifier, - * depending on the type of connection to the KNX network, and the KNX - * device individual address ("area.line.device"). See - * {@link #main(String[])} for the list of options. + * Constructs an instance of the {@link #Updater} class. * - * @param args - * list with options - * @throws KNXIllegalArgumentException - * on unknown/invalid options + * @param cliOptions the command-line options to be used */ - public Updater(final String[] args) { + public Updater(CliOptions cliOptions) { logger.debug(ToolInfo.getFullInfo()); logger.debug(Settings.getLibraryHeader(false)); - logger.info(ansi().fgBright(GREEN).bold().a( - " _____ ________ __________ __ _______ __ ______ ____ ___ ________________ \n" + - " / ___// ____/ / / ____/ __ )/ / / / ___/ / / / / __ \\/ __ \\/ |/_ __/ ____/ __ \\\n" + - " \\__ \\/ __/ / / / /_ / __ / / / /\\__ \\ / / / / /_/ / / / / /| | / / / __/ / /_/ /\n" + - " ___/ / /___/ /___/ __/ / /_/ / /_/ /___/ / / /_/ / ____/ /_/ / ___ |/ / / /___/ _, _/ \n" + - "/____/_____/_____/_/ /_____/\\____//____/ \\____/_/ /_____/_/ |_/_/ /_____/_/ |_| \n" + - "by Dr. Stefan Haller, Oliver Stefan et al. {}").reset().toString(), - ToolInfo.getToolAndVersion()); - try { - // read in user-supplied command line options - this.cliOptions = new CliOptions(args, String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()) , - "Selfbus KNX-Firmware update tool options", "", PHYS_ADDRESS_BOOTLOADER, PHYS_ADDRESS_OWN); - this.sbKNXLink = new SBKNXLink(); - this.sbKNXLink.setCliOptions(cliOptions); - } catch (final KNXIllegalArgumentException e) { - throw e; - } catch (final RuntimeException e) { - throw new KNXIllegalArgumentException(e.getMessage(), e); - } - } - - public Updater(CliOptions cliOptions){ this.cliOptions = cliOptions; this.sbKNXLink = new SBKNXLink(); this.sbKNXLink.setCliOptions(cliOptions); } - public static void main(final String[] args) { + public static void initJansi() { AnsiConsole.systemInstall(); - if(args.length == 0) { + } + + public static void finalizeJansi() { + System.out.print(AnsiCursor.on()); // make sure we enable the cursor + AnsiConsole.systemUninstall(); + } + + public static void main(final String[] args) { + initJansi(); + + if (args.length == 0) { GuiMain.startSwingGui(); - }else { - try { - final Updater d = new Updater(args); - final ShutdownHandler sh = new ShutdownHandler().register(); - d.run(); - sh.unregister(); - } catch (final Throwable t) { - logger.error("parsing options ", t); - } finally { - System.out.print(AnsiCursor.on()); // make sure we enable the cursor - logger.debug("main exit"); - } + finalizeJansi(); + return; + } + + // read in user-supplied command line options + CliOptions options = new CliOptions(args, String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()) , + "Selfbus KNX-Firmware update tool options", "", PHYS_ADDRESS_BOOTLOADER, PHYS_ADDRESS_OWN); + if (options.version()) { + logger.info(ansi().fgBright(GREEN).bold().a(Credits.getAsciiLogo()).reset().toString()); + logger.info(ansi().fgBright(GREEN).bold().a(Credits.getAuthors()).reset().toString()); + ToolInfo.showVersion(); + finalizeJansi(); + return; + } + + logger.info(ansi().fgBright(GREEN).bold().a(ToolInfo.getToolAndVersion()).reset().toString()); + if (options.help()) { + logger.info(options.helpToString()); + finalizeJansi(); + return; + } + + try { + final Updater updater = new Updater(options); + final ShutdownHandler shutDownHandler = new ShutdownHandler().register(); + updater.run(); + shutDownHandler.unregister(); + } finally { + finalizeJansi(); + logger.debug("main exit"); } - AnsiConsole.systemUninstall(); } /** @@ -178,15 +164,6 @@ public void run() { boolean canceled = false; KNXNetworkLink link = null; try { - if (cliOptions.help()) { - logger.info(cliOptions.helpToString()); - return; - } - if (cliOptions.version()) { - ToolInfo.showVersion(); - return; - } - byte[] uid = cliOptions.uid(); final String hexFileName = cliOptions.fileName(); BinImage newFirmware = null; From 6f230f136d8dbe7a91e00e9cecb5b6d0031195da Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 13:55:55 +0200 Subject: [PATCH 139/359] [Updater] Move and exclude `.hex` files from IDEA inspections E.g. inspection proofreading makes no sense on `.hex` files. --- firmware_updater/updater/.idea/modules.xml | 8 ++++++++ .../updater/.idea/modules/SB_updater.test.iml | 8 ++++++++ .../selfbus/updater/mode/differential/FlashDiffTests.java | 6 +++--- .../test/resources/{ => hex}/updater.ino.slto.v1.hex | 0 .../test/resources/{ => hex}/updater.ino.slto.v2.hex | 0 5 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 firmware_updater/updater/.idea/modules.xml create mode 100644 firmware_updater/updater/.idea/modules/SB_updater.test.iml rename firmware_updater/updater/test/resources/{ => hex}/updater.ino.slto.v1.hex (100%) rename firmware_updater/updater/test/resources/{ => hex}/updater.ino.slto.v2.hex (100%) diff --git a/firmware_updater/updater/.idea/modules.xml b/firmware_updater/updater/.idea/modules.xml new file mode 100644 index 00000000..b6e5eb49 --- /dev/null +++ b/firmware_updater/updater/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/firmware_updater/updater/.idea/modules/SB_updater.test.iml b/firmware_updater/updater/.idea/modules/SB_updater.test.iml new file mode 100644 index 00000000..132577dd --- /dev/null +++ b/firmware_updater/updater/.idea/modules/SB_updater.test.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/firmware_updater/updater/test/org/selfbus/updater/mode/differential/FlashDiffTests.java b/firmware_updater/updater/test/org/selfbus/updater/mode/differential/FlashDiffTests.java index b31662e3..a3c99b0b 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/mode/differential/FlashDiffTests.java +++ b/firmware_updater/updater/test/org/selfbus/updater/mode/differential/FlashDiffTests.java @@ -46,8 +46,8 @@ private void performTest(BinImage img1, BinImage img2) { public void testDiff() throws URISyntaxException { // test of upgrade from old version to newer (longer) ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - URI uri1 = Objects.requireNonNull(contextClassLoader.getResource("updater.ino.slto.v1.hex")).toURI(); - URI uri2 = Objects.requireNonNull(contextClassLoader.getResource("updater.ino.slto.v2.hex")).toURI(); + URI uri1 = Objects.requireNonNull(contextClassLoader.getResource("hex/updater.ino.slto.v1.hex")).toURI(); + URI uri2 = Objects.requireNonNull(contextClassLoader.getResource("hex/updater.ino.slto.v2.hex")).toURI(); BinImage img1 = BinImage.readFromHex(uri1.getPath()); BinImage img2 = BinImage.readFromHex(uri2.getPath()); performTest(img1, img2); @@ -57,7 +57,7 @@ public void testDiff() throws URISyntaxException { @Test public void testDiff2() throws URISyntaxException, UpdaterException { // test of new firmware into empty MCU - URI uri2 = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("updater.ino.slto.v2.hex")).toURI(); + URI uri2 = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("hex/updater.ino.slto.v2.hex")).toURI(); BinImage img2 = BinImage.readFromHex(uri2.getPath()); BinImage img1 = BinImage.dummyFilled(img2.getBinData().length, 0xff); performTest(img1, img2); diff --git a/firmware_updater/updater/test/resources/updater.ino.slto.v1.hex b/firmware_updater/updater/test/resources/hex/updater.ino.slto.v1.hex similarity index 100% rename from firmware_updater/updater/test/resources/updater.ino.slto.v1.hex rename to firmware_updater/updater/test/resources/hex/updater.ino.slto.v1.hex diff --git a/firmware_updater/updater/test/resources/updater.ino.slto.v2.hex b/firmware_updater/updater/test/resources/hex/updater.ino.slto.v2.hex similarity index 100% rename from firmware_updater/updater/test/resources/updater.ino.slto.v2.hex rename to firmware_updater/updater/test/resources/hex/updater.ino.slto.v2.hex From cf78f28538450b670f3d8c9ae7734fecaabc067b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 14:05:17 +0200 Subject: [PATCH 140/359] [Updater] Use expression lambdas --- .../src/org/selfbus/updater/gui/GuiMain.java | 8 ++------ .../updater/gui/ConColorsToStyledDocTests.java | 16 ++++------------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 6909c98c..03bd9fe1 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -148,9 +148,7 @@ public void run() { final Updater d = new Updater(cliOptions); d.run(); - SwingUtilities.invokeLater(() -> { - guiMainInstance.buttonStartStopFlash.setText(startFlash); - }); + SwingUtilities.invokeLater(() -> guiMainInstance.buttonStartStopFlash.setText(startFlash)); } }; updaterThread.start(); @@ -166,9 +164,7 @@ public void actionPerformed(ActionEvent e) { setCliOptions(); final Updater upd = new Updater(cliOptions); String uid = upd.requestUid(); - SwingUtilities.invokeLater(() -> { - guiMainInstance.textFieldUid.setText(uid); - }); + SwingUtilities.invokeLater(() -> guiMainInstance.textFieldUid.setText(uid)); }); updaterThread.start(); } diff --git a/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java b/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java index 09810a5c..66636f25 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java +++ b/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java @@ -169,19 +169,13 @@ public void testStrikeOff() { @Test public void testExceptionOnInvalidCode() { - assertThrows(IllegalStateException.class, () -> { - testColorCodeToStyle(new String[]{"999"}); - }); + assertThrows(IllegalStateException.class, () -> testColorCodeToStyle(new String[]{"999"})); } @Test public void testExceptionOnExtendedColors() { - assertThrows(IllegalStateException.class, () -> { - testColorCodeToStyle(new String[]{"0","38"}); - }); - assertThrows(IllegalStateException.class, () -> { - testColorCodeToStyle(new String[]{"1", "48"}); - }); + assertThrows(IllegalStateException.class, () -> testColorCodeToStyle(new String[]{"0","38"})); + assertThrows(IllegalStateException.class, () -> testColorCodeToStyle(new String[]{"1", "48"})); } @Test @@ -247,9 +241,7 @@ public void testCursorSaveAndRestore() throws BadLocationException { }; for (String underTest : testCases) { document.remove(0, document.getLength()); - assertThrows(IllegalStateException.class, () -> { - ConColorsToStyledDoc.Convert(underTest, textPane); - }); + assertThrows(IllegalStateException.class, () -> ConColorsToStyledDoc.Convert(underTest, textPane)); } } From b5146bc8932e27e8ba147b6cac03156b87c82810 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 14:12:32 +0200 Subject: [PATCH 141/359] [Updater] Simplify `ActionListener`s with lambdas --- .../src/org/selfbus/updater/gui/GuiMain.java | 112 +++++++----------- 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 03bd9fe1..48dfd8d9 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -106,89 +106,65 @@ public GuiMain() { //Locale.setDefault(Locale.ROOT); // default language for tests $$$setupUI$$$(); guiTranslation = ResourceBundle.getBundle(LANGUAGE_RESOURCE_BUNDLE, Locale.getDefault()); - buttonLoadFile.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JFileChooser fc = new JFileChooser(); - String filePath = textFieldFileName.getText(); + buttonLoadFile.addActionListener(actionListener -> { + JFileChooser fc = new JFileChooser(); + String filePath = textFieldFileName.getText(); - if (filePath != null && !filePath.isEmpty()) { - fc.setCurrentDirectory(new File(filePath).getParentFile()); - } - fc.setFileFilter(new FileNameExtensionFilter("HEX", "hex")); - int result = fc.showOpenDialog(panelMain); + if (filePath != null && !filePath.isEmpty()) { + fc.setCurrentDirectory(new File(filePath).getParentFile()); + } + fc.setFileFilter(new FileNameExtensionFilter("HEX", "hex")); + int result = fc.showOpenDialog(panelMain); - if (result == JFileChooser.APPROVE_OPTION) { - if (fc.getSelectedFile().exists()) { - textFieldFileName.setText(fc.getSelectedFile().toString()); - } + if (result == JFileChooser.APPROVE_OPTION) { + if (fc.getSelectedFile().exists()) { + textFieldFileName.setText(fc.getSelectedFile().toString()); } } }); - buttonStartStopFlash.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - String stopFlash = guiTranslation.getString("stopFlash"); - String startFlash = guiTranslation.getString("startFlash"); - - if (Objects.equals(buttonStartStopFlash.getText(), stopFlash)) { - if (updaterThread != null) { - updaterThread.interrupt(); - buttonStartStopFlash.setText(startFlash); - logger.info(guiTranslation.getString("logMessageCanceledFlashing")); - return; - } + buttonStartStopFlash.addActionListener(actionListener -> { + String stopFlash = guiTranslation.getString("stopFlash"); + String startFlash = guiTranslation.getString("startFlash"); + + if (Objects.equals(buttonStartStopFlash.getText(), stopFlash)) { + if (updaterThread != null) { + updaterThread.interrupt(); + buttonStartStopFlash.setText(startFlash); + logger.info(guiTranslation.getString("logMessageCanceledFlashing")); + return; } + } - jLoggingPane.setText(""); - updaterThread = new Thread() { - public void run() { - setCliOptions(); + jLoggingPane.setText(""); + updaterThread = new Thread() { + public void run() { + setCliOptions(); - final Updater d = new Updater(cliOptions); - d.run(); - SwingUtilities.invokeLater(() -> guiMainInstance.buttonStartStopFlash.setText(startFlash)); - } - }; - updaterThread.start(); - buttonStartStopFlash.setText(stopFlash); - } + final Updater d = new Updater(cliOptions); + d.run(); + SwingUtilities.invokeLater(() -> guiMainInstance.buttonStartStopFlash.setText(startFlash)); + } + }; + updaterThread.start(); + buttonStartStopFlash.setText(stopFlash); }); - buttonRequestUid.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { + buttonRequestUid.addActionListener(actionListener -> { - updaterThread = new Thread(() -> { - setCliOptions(); - final Updater upd = new Updater(cliOptions); - String uid = upd.requestUid(); - SwingUtilities.invokeLater(() -> guiMainInstance.textFieldUid.setText(uid)); - }); - updaterThread.start(); - } + updaterThread = new Thread(() -> { + setCliOptions(); + final Updater upd = new Updater(cliOptions); + String uid = upd.requestUid(); + SwingUtilities.invokeLater(() -> guiMainInstance.textFieldUid.setText(uid)); + }); + updaterThread.start(); }); - comboBoxScenario.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - setGuiElementsVisibility(); - } - }); + comboBoxScenario.addActionListener(actionListener -> setGuiElementsVisibility()); - advancedSettingsCheckBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - setGuiElementsVisibility(); - } - }); - reloadGatewaysButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - new Thread(() -> loadKnxIpInterfacesAndFillComboBox()).start(); - } - }); + advancedSettingsCheckBox.addActionListener(actionListener -> setGuiElementsVisibility()); + reloadGatewaysButton.addActionListener(actionListener -> new Thread(this::loadKnxIpInterfacesAndFillComboBox).start()); comboBoxIpGateways.addActionListener(comboBoxIpGatewaysActionListener); } From 5fea7192da090c5edc9709f8332e9b1a7a5e19c3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 14:14:12 +0200 Subject: [PATCH 142/359] [Updater] Add `private CliInvalidException()` to entry points --- firmware_updater/updater/.idea/misc.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firmware_updater/updater/.idea/misc.xml b/firmware_updater/updater/.idea/misc.xml index f683393d..df27bd37 100644 --- a/firmware_updater/updater/.idea/misc.xml +++ b/firmware_updater/updater/.idea/misc.xml @@ -1,5 +1,8 @@ + + + From 3c15edc397eabe9d5c0a63b9edd8bca18f0fb4b0 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 14:31:26 +0200 Subject: [PATCH 143/359] [Updater] Simplify `updaterThread` and `JCombobox`s --- .../src/org/selfbus/updater/gui/GuiMain.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 48dfd8d9..8252df5f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -137,15 +137,13 @@ public GuiMain() { } jLoggingPane.setText(""); - updaterThread = new Thread() { - public void run() { - setCliOptions(); + updaterThread = new Thread(() -> { + setCliOptions(); - final Updater d = new Updater(cliOptions); - d.run(); - SwingUtilities.invokeLater(() -> guiMainInstance.buttonStartStopFlash.setText(startFlash)); - } - }; + final Updater updater = new Updater(cliOptions); + updater.run(); + SwingUtilities.invokeLater(() -> guiMainInstance.buttonStartStopFlash.setText(startFlash)); + }); updaterThread.start(); buttonStartStopFlash.setText(stopFlash); }); @@ -407,10 +405,10 @@ private void setFrameImages() { private void createUIComponents() { // TODO: place custom component creation code here - comboBoxIpGateways = new JComboBox(); - comboBoxMedium = new JComboBox(); - comboBoxScenario = new JComboBox(); - comboBoxKnxTelegramPriority = new JComboBox(); + comboBoxIpGateways = new JComboBox<>(); + comboBoxMedium = new JComboBox<>(); + comboBoxScenario = new JComboBox<>(); + comboBoxKnxTelegramPriority = new JComboBox<>(); this.setFrameImages(); } From 524f9db6cc1dca23c6e1bd21ace46897f0eff2a6 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 14:48:38 +0200 Subject: [PATCH 144/359] [Updater] Use actual root logger level, not our "cached" `logLevel` --- .../src/org/selfbus/updater/CliOptions.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 4aa2b266..035a3fb6 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -153,7 +153,6 @@ public class CliOptions { private int delay = 0; private boolean NO_FLASH = false; - private Level logLevel = Level.DEBUG; private boolean eraseFullFlash = false; private long dumpFlashStartAddress = -1; private long dumpFlashEndAddress = -1; @@ -272,7 +271,7 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe .numberOfArgs(1) .required(false) .type(String.class) - .desc(String.format("Logfile logging level [TRACE|DEBUG|INFO] (default %s)", this.logLevel.toString())).build(); + .desc(String.format("Logfile logging level [TRACE|DEBUG|INFO] (default %s)", getLogLevel().toString())).build(); Option userId = Option.builder(null).longOpt(OPT_LONG_USER_ID) .argName("id") @@ -368,19 +367,16 @@ private void parse(final String[] args) { CommandLineParser parser = new DefaultParser(); try { cmdLine = parser.parse(cliOptions, args); - // get the log level for log file output - ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); if (cmdLine.hasOption(OPT_SHORT_LOGLEVEL)) { String cliLogLevel = cmdLine.getOptionValue(OPT_SHORT_LOGLEVEL).toUpperCase(); if (VALID_LOG_LEVELS.contains(cliLogLevel)) { - logLevel = Level.toLevel(cliLogLevel); - root.setLevel(logLevel); + setLogLevel(Level.toLevel(cliLogLevel)); } else { - logger.warn(ansi().fg(RED).a("invalid {} {}, using {}").reset().toString(), OPT_LONG_LOGLEVEL, cliLogLevel, root.getLevel().toString()); + logger.warn(ansi().fg(RED).a("invalid {} {}, using {}").reset().toString(), OPT_LONG_LOGLEVEL, cliLogLevel, getLogLevel().toString()); } } - logger.debug("logLevel={}", root.getLevel().toString()); + logger.debug("logLevel={}", getLogLevel()); if (cmdLine.hasOption(OPT_LONG_PRIORITY)) { try { @@ -777,8 +773,14 @@ public boolean version() { return version; } - public Level logLevel() { - return logLevel; + public Level getLogLevel() { + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + return root.getLevel(); + } + + public void setLogLevel(Level newLevel) { + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + root.setLevel(newLevel); } public int userId() { From f187222f6377e841806a42cbaeab5f81ab804451 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 18:07:31 +0200 Subject: [PATCH 145/359] =?UTF-8?q?[Updater]=20Suppress=20warning=20`SameR?= =?UTF-8?q?eturnValue`,=20it=C2=B4s=20intentional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../updater/src/org/selfbus/updater/AnsiCursor.java | 3 +++ firmware_updater/updater/src/org/selfbus/updater/Credits.java | 1 + 2 files changed, 4 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java b/firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java index 38264a25..505d917d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java +++ b/firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java @@ -4,10 +4,13 @@ public final class AnsiCursor { @SuppressWarnings("unused") private AnsiCursor() {} // avoids instance creation + @SuppressWarnings("SameReturnValue") public static String on() { // cursor on return "\033[?25h"; } + + @SuppressWarnings("SameReturnValue") public static String off() { // cursor off return "\033[?25l"; diff --git a/firmware_updater/updater/src/org/selfbus/updater/Credits.java b/firmware_updater/updater/src/org/selfbus/updater/Credits.java index 4dd9bfdd..35dd6a80 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Credits.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Credits.java @@ -12,6 +12,7 @@ private Credits() {} "et al.", }; + @SuppressWarnings("SameReturnValue") public static String getAsciiLogo() { return """ _____ ________ __________ __ _______ __ ______ ____ ___ ________________ From 9202b35233b348f9574b910331e4d3324aeb7b71 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 18:09:27 +0200 Subject: [PATCH 146/359] [Updater] Suppress warnings `unused`, `SameParameterValue` This is intentional behavior. --- firmware_updater/updater/src/org/selfbus/updater/Utils.java | 1 + .../updater/src/org/selfbus/updater/gui/GuiMain.java | 2 ++ .../updater/src/org/selfbus/updater/upd/UPDProtocol.java | 5 +++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Utils.java b/firmware_updater/updater/src/org/selfbus/updater/Utils.java index eae579d5..2b580831 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Utils.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Utils.java @@ -17,6 +17,7 @@ * Basic utilities usable for the application */ public class Utils { + @SuppressWarnings("unused") private static final Logger logger = LoggerFactory.getLogger(Utils.class); public static long streamToLong(byte[] stream, int offset) { diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 8252df5f..792ed30c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -180,6 +180,7 @@ public void actionPerformed(ActionEvent e) { } }; + @SuppressWarnings("SameParameterValue") private void saveAllParameters(String fileName) { userProperties.setProperty("AdvancedSettings", advancedSettingsCheckBox.isSelected() ? "true" : "false"); userProperties.setProperty("GatewayIpAddr", textBoxKnxGatewayIpAddr.getText()); @@ -212,6 +213,7 @@ private void saveAllParameters(String fileName) { } } + @SuppressWarnings("SameParameterValue") private void loadAllParameters(String fileName) { if (new File(fileName).exists()) { diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java index fc7da52e..1bc84b0c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java @@ -10,6 +10,9 @@ * Implementation of the UPD/UDP protocol handling */ public final class UPDProtocol { + @SuppressWarnings("unused") + private UPDProtocol() {} + private static final Logger logger = LoggerFactory.getLogger(UPDProtocol.class); public static final int COMMAND_POSITION = 2; @@ -23,8 +26,6 @@ public final class UPDProtocol { */ public static final int UID_LENGTH_MAX = 16; - private UPDProtocol() {} - public static long checkResult(byte[] result) { return checkResult(result, true); } From 3066504abfdee76b29b7c3e96ecff05a2df58b39 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 19:13:15 +0200 Subject: [PATCH 147/359] [Updater] Refactor `CliOptions`s getter/setter - Move uid `ByteArray` handling to `UPDProtocol.java` - Rename and Move `parseHost` to `SBKNXLink` --- .../src/org/selfbus/updater/CliOptions.java | 499 ++++++++++-------- .../org/selfbus/updater/DeviceManagement.java | 14 +- .../src/org/selfbus/updater/SBKNXLink.java | 63 ++- .../src/org/selfbus/updater/Updater.java | 66 +-- .../src/org/selfbus/updater/Utils.java | 29 - .../org/selfbus/updater/upd/UPDProtocol.java | 28 + 6 files changed, 395 insertions(+), 304 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 035a3fb6..daf785ad 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -23,7 +23,6 @@ import java.io.PrintWriter; import java.io.StringWriter; -import java.net.InetAddress; import java.util.Arrays; import java.util.List; @@ -126,53 +125,49 @@ public class CliOptions { private final String helpFooter; private final String helpApplicationName; - private InetAddress knxInterface; + private String knxInterface =""; private String fileName = ""; - private InetAddress localhost; - private int localport = 0; + private String localhost = ""; + private int localPort = 0; private int port = KNXnetIPConnection.DEFAULT_PORT; - private boolean nat = false; - private String ft12 = ""; - private String tpuart = ""; - private String usbInterface = ""; - private boolean tunnelingV2 = false; - private boolean tunnelingV1 = false; - private boolean routing = false; + private boolean natIsSet = false; + private String ft12SerialPort = ""; + private String tpuartSerialPort = ""; + private String usbVendorIdAndProductId = ""; + private boolean tunnelingV2isSet = false; + private boolean tunnelingV1isSet = false; + private boolean routingIsSet = false; private String medium = "tp1"; - private int userId = 1; - private String userPassword = ""; - private String devicePassword = ""; + private int knxSecureUserId = 1; + private String knxSecureUserPassword = ""; + private String knxSecureDevicePassword = ""; - private IndividualAddress progDevice; - private IndividualAddress ownAddress; - private IndividualAddress device = null; - private byte[] uid; - private boolean full = false; - private int delay = 0; + private IndividualAddress progDevicePhysicalAddress; + private IndividualAddress ownPhysicalAddress; + private IndividualAddress devicePhysicalAddress = null; + private String uid; + private boolean flashingFullModeIsSet = false; + private int delayMs = 0; - private boolean NO_FLASH = false; - private boolean eraseFullFlash = false; + private boolean noFlashIsSet = false; + private boolean eraseFullFlashIsSet = false; private long dumpFlashStartAddress = -1; private long dumpFlashEndAddress = -1; private Priority priority = Priority.LOW; - private boolean logStatistics = false; + private boolean logStatisticsIsSet = false; private int blockSize = Mcu.UPD_PROGRAM_SIZE; - private boolean help = false; - private boolean version = false; - - public CliOptions(final String[] args, String helpApplicationName, String helpHeader, String helpFooter, - IndividualAddress progDevice, IndividualAddress ownAddress) { + IndividualAddress progDevicePhysicalAddress, IndividualAddress ownAddress) { this.helpApplicationName = helpApplicationName; this.helpHeader = helpHeader; this.helpFooter = helpFooter; - this.progDevice = progDevice; - this.ownAddress = ownAddress; + setProgDevicePhysicalAddress(progDevicePhysicalAddress); //todo check why we have this as a method parameter and why is it set here? + setOwnPhysicalAddress(ownAddress); //todo check why we have this as a method parameter and why is it set here? Option tunnelingV2 = new Option(OPT_SHORT_TUNNEL_V2, OPT_LONG_TUNNEL_V2, false, "use KNXnet/IP tunneling v2 (TCP) (experimental)"); Option tunnelingV1 = new Option(OPT_SHORT_TUNNEL_V1, OPT_LONG_TUNNEL_V1, false, "use KNXnet/IP tunneling v1 (UDP)"); @@ -236,13 +231,13 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe .numberOfArgs(1) .required(false) .type(TPSettings.class) - .desc(String.format("KNX medium [tp1|rf] (default %s)", this.medium)).build(); ///\todo not all implemented missing [tp0|p110|p132] + .desc(String.format("KNX medium [tp1|rf] (default %s)", getMedium())).build(); ///\todo not all implemented missing [tp0|p110|p132] Option optProgDevice = Option.builder(OPT_SHORT_PROG_DEVICE).longOpt(OPT_LONG_PROG_DEVICE) .argName("x.x.x") .numberOfArgs(1) .required(false) .type(IndividualAddress.class) - .desc(String.format("KNX device address in bootloader mode (default %s)", this.progDevice.toString())).build(); + .desc(String.format("KNX device address in bootloader mode (default %s)", getProgDevicePhysicalAddress().toString())).build(); Option device = Option.builder(OPT_SHORT_DEVICE).longOpt(OPT_LONG_DEVICE) .argName("x.x.x") .numberOfArgs(1) @@ -254,7 +249,7 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe .numberOfArgs(1) .required(false) .type(IndividualAddress.class) - .desc(String.format("own physical KNX address (default %s)", this.ownAddress.toString())).build(); + .desc(String.format("own physical KNX address (default %s)", getOwnPhysicalAddress().toString())).build(); Option uid = Option.builder(OPT_SHORT_UID).longOpt(OPT_LONG_UID) .argName("uid") .numberOfArgs(1) @@ -278,7 +273,7 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe .numberOfArgs(1) .required(false) .type(Number.class) - .desc(String.format("KNX IP Secure tunneling user identifier (1..127) (default %d)", this.userId)).build(); + .desc(String.format("KNX IP Secure tunneling user identifier (1..127) (default %d)", getKnxSecureUserId())).build(); Option userPasswd = Option.builder(null).longOpt(OPT_LONG_USER_PASSWORD) .argName("password") .numberOfArgs(1) @@ -297,15 +292,15 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe .numberOfArgs(1) .required(false) .type(String.class) - .desc(String.format("KNX telegram priority (default %s)", this.priority.toString().toUpperCase())).build(); + .desc(String.format("KNX telegram priority (default %s)", getPriority().toString().toUpperCase())).build(); - Option blockSize = Option.builder(OPT_SHORT_BLOCKSIZE).longOpt(OPT_LONG_BLOCKSIZE) + Option blockSizeOption = Option.builder(OPT_SHORT_BLOCKSIZE).longOpt(OPT_LONG_BLOCKSIZE) .argName("256|512|1024") .valueSeparator(' ') .numberOfArgs(1) .required(false) .type(Number.class) - .desc(String.format("Block size to program (default %d bytes)", this.blockSize)).build(); + .desc(String.format("Block size to program (default %d bytes)", getBlockSize())).build(); Option logStatistic = new Option(null, OPT_LONG_LOGSTATISTIC, false, "show more statistic data"); @@ -325,7 +320,7 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe cliOptions.addOption(optProgDevice); cliOptions.addOption(ownPhysicalAddress); cliOptions.addOption(knxPriority); - cliOptions.addOption(blockSize); + cliOptions.addOption(blockSizeOption); cliOptions.addOption(userId); cliOptions.addOption(userPasswd); @@ -380,239 +375,215 @@ private void parse(final String[] args) { if (cmdLine.hasOption(OPT_LONG_PRIORITY)) { try { - priority = Priority.get(cmdLine.getOptionValue(OPT_LONG_PRIORITY)); + setPriority(Priority.get(cmdLine.getOptionValue(OPT_LONG_PRIORITY))); //todo simplify } catch (KNXIllegalArgumentException e) { logger.warn(ansi().fg(RED).a("invalid --{} {}, using {}").reset().toString(), - OPT_LONG_PRIORITY, cmdLine.getOptionValue(OPT_LONG_PRIORITY), priority); + OPT_LONG_PRIORITY, cmdLine.getOptionValue(OPT_LONG_PRIORITY), getPriority()); } } - logger.debug("priority={}", priority.toString()); + logger.debug("priority={}", getPriority().toString()); - if (cmdLine.hasOption(OPT_SHORT_HELP)) { - help = true; - logger.debug("help={}", true); + if (getHelpIsSet()) { + logger.debug("help={}", getHelpIsSet()); return; } - if (cmdLine.hasOption(OPT_SHORT_VERSION)) { - version = true; - logger.debug("version={}", true); + if (getVersionIsSet()) { + logger.debug("version={}", getVersionIsSet()); return; } - if (cmdLine.hasOption(OPT_SHORT_NO_FLASH)) { - NO_FLASH = true; - } - logger.debug("NO_FLASH={}", NO_FLASH); + setNoFlashIsSet(cmdLine.hasOption(OPT_SHORT_NO_FLASH)); + logger.debug("NO_FLASH={}", getNoFlashIsSet()); - if (cmdLine.hasOption(OPT_LONG_ERASEFLASH)) { - eraseFullFlash = true; - } - logger.debug("eraseFlash={}", eraseFullFlash); + setEraseFullFlashIsSet(cmdLine.hasOption(OPT_LONG_ERASEFLASH)); + logger.debug("eraseFlash={}", getEraseFullFlashIsSet()); if (cmdLine.hasOption(OPT_LONG_DUMPFLASH)) { String[] optArgs = cmdLine.getOptionValues(OPT_LONG_DUMPFLASH); - dumpFlashStartAddress = Long.decode(optArgs[0]); - dumpFlashEndAddress = Long.decode(optArgs[1]); + setDumpFlashStartAddress(Long.decode(optArgs[0])); + setDumpFlashEndAddress(Long.decode(optArgs[1])); } - logger.debug("dumpFlashStartAddress={}", dumpFlashStartAddress); - logger.debug("dumpFlashEndAddress={}", dumpFlashEndAddress); + logger.debug("dumpFlashStartAddress={}", getDumpFlashStartAddress()); + logger.debug("dumpFlashEndAddress={}", getDumpFlashEndAddress()); - if (cmdLine.hasOption(OPT_SHORT_FULL)) { - full = true; - } - logger.debug("full={}", full); + setFlashingFullModeIsSet(cmdLine.hasOption(OPT_SHORT_FULL)); + logger.debug("full={}", getFlashingFullModeIsSet()); - if (cmdLine.hasOption(OPT_SHORT_TUNNEL_V2)) { - tunnelingV2 = true; - } - logger.debug("tunnelingV2={}", tunnelingV2); + setTunnelingV2isSet(cmdLine.hasOption(OPT_SHORT_TUNNEL_V2)); + logger.debug("tunnelingV2={}", getTunnelingV2isSet()); - if (cmdLine.hasOption(OPT_SHORT_TUNNEL_V1)) { - tunnelingV1 = true; - } - logger.debug("tunneling={}", tunnelingV1); + setTunnelingV1isSet(cmdLine.hasOption(OPT_SHORT_TUNNEL_V1)); + logger.debug("tunneling={}", getTunnelingV2isSet()); - if (cmdLine.hasOption(OPT_SHORT_ROUTING)) { - routing = true; - } - logger.debug("routing={}", routing); + setRoutingIsSet(cmdLine.hasOption(OPT_SHORT_ROUTING)); + logger.debug("routing={}", getRoutingIsSet()); - if (cmdLine.hasOption(OPT_SHORT_NAT)) { - nat = true; - } - logger.debug("nat={}", nat); + setNatIsSet(cmdLine.hasOption(OPT_SHORT_NAT)); + logger.debug("nat={}", getNatIsSet()); if (cmdLine.hasOption(OPT_SHORT_FILENAME)) { - fileName = cmdLine.getOptionValue(OPT_SHORT_FILENAME); + setFileName(cmdLine.getOptionValue(OPT_SHORT_FILENAME)); } - logger.debug("fileName={}", fileName); + logger.debug("fileName={}", getFileName()); if (cmdLine.hasOption(OPT_SHORT_LOCALHOST)) { - localhost = Utils.parseHost(cmdLine.getOptionValue(OPT_SHORT_LOCALHOST)); + setLocalhost(cmdLine.getOptionValue(OPT_SHORT_LOCALHOST)); } - logger.debug("localhost={}", localhost); + logger.debug("localhost={}", getLocalhost()); if (cmdLine.hasOption(OPT_SHORT_LOCALPORT)) { - localport = ((Number)cmdLine.getParsedOptionValue(OPT_SHORT_LOCALPORT)).intValue(); + setLocalPort(((Number)cmdLine.getParsedOptionValue(OPT_SHORT_LOCALPORT)).intValue()); } - logger.debug("localport={}", localport); + logger.debug("localport={}", getLocalPort()); if (cmdLine.hasOption(OPT_SHORT_PORT)) { - port = ((Number)cmdLine.getParsedOptionValue(OPT_SHORT_PORT)).intValue(); + setPort(((Number)cmdLine.getParsedOptionValue(OPT_SHORT_PORT)).intValue()); } - logger.debug("port={}", port); + logger.debug("port={}", getPort()); if (cmdLine.hasOption(OPT_SHORT_MEDIUM)) { - medium = cmdLine.getOptionValue(OPT_SHORT_MEDIUM); + setMedium(cmdLine.getOptionValue(OPT_SHORT_MEDIUM)); } - logger.debug("medium={}", medium); + logger.debug("medium={}", getMedium()); if (cmdLine.hasOption(OPT_LONG_DELAY)) { - delay = ((Number)cmdLine.getParsedOptionValue(OPT_LONG_DELAY)).intValue(); - if ((delay < Updater.DELAY_MIN) || (delay > Updater.DELAY_MAX)) { - logger.warn(ansi().fg(RED).a( - String.format("option --%s {} is invalid (min:{}, max:{}) => set to {}", OPT_LONG_DELAY)).reset().toString(), - delay, Updater.DELAY_MIN, Updater.DELAY_MAX, Updater.DELAY_DEFAULT); - delay = Updater.DELAY_DEFAULT; // set to DELAY_DEFAULT in case of invalid waiting time - } + setDelayMs(((Number)cmdLine.getParsedOptionValue(OPT_LONG_DELAY)).intValue()); } - logger.debug("delay={}", delay); + logger.debug("delay={}", getDelayMs()); if (cmdLine.hasOption(OPT_SHORT_BLOCKSIZE)) { int newBlockSize = ((Number)cmdLine.getParsedOptionValue(OPT_LONG_BLOCKSIZE)).intValue(); if (VALID_BLOCKSIZES.contains(newBlockSize)) { - blockSize = newBlockSize; + setBlockSize(newBlockSize); } else { - logger.info(ansi().fg(YELLOW).a("--{} {} is not supported => Set --{} to default {} bytes").reset().toString(), OPT_LONG_BLOCKSIZE, newBlockSize, OPT_LONG_BLOCKSIZE, blockSize); + logger.info(ansi().fg(YELLOW).a("--{} {} is not supported => Set --{} to default {} bytes").reset().toString(), + OPT_LONG_BLOCKSIZE, newBlockSize, OPT_LONG_BLOCKSIZE, getBlockSize()); } } - logger.debug("{}={}", OPT_LONG_BLOCKSIZE, delay); + logger.debug("{}={}", OPT_LONG_BLOCKSIZE, getBlockSize()); if (cmdLine.hasOption(OPT_SHORT_UID)) { - uid = uidToByteArray(cmdLine.getOptionValue(OPT_SHORT_UID)); + setUid(cmdLine.getOptionValue(OPT_SHORT_UID)); } - logger.debug("uid={}", Utils.byteArrayToHex(uid)); + logger.debug("uid={}", getUid()); if (cmdLine.hasOption(OPT_SHORT_DEVICE)) { - device = new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_DEVICE)); + setDevicePhysicalAddress(new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_DEVICE))); } else { - device = null; + setDevicePhysicalAddress(null); } - logger.debug("device={}", device); + logger.debug("device={}", getDevicePhysicalAddress()); if (cmdLine.hasOption(OPT_SHORT_PROG_DEVICE)) { - progDevice = new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_PROG_DEVICE)); + setProgDevicePhysicalAddress(new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_PROG_DEVICE))); } - - logger.debug("progDevice={}", progDevice); + logger.debug("progDevice={}", getProgDevicePhysicalAddress()); if (cmdLine.hasOption(OPT_SHORT_OWN_ADDRESS)) { - ownAddress = new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_OWN_ADDRESS)); + setOwnPhysicalAddress(new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_OWN_ADDRESS))); } - logger.debug("ownAddress={}", ownAddress); + logger.debug("ownAddress={}", getOwnPhysicalAddress()); if (cmdLine.hasOption(OPT_SHORT_FT12)) { - ft12 = cmdLine.getOptionValue(OPT_SHORT_FT12); + setFt12SerialPort(cmdLine.getOptionValue(OPT_SHORT_FT12)); } - logger.debug("ft12={}", ft12); + logger.debug("ft12={}", getFt12SerialPort()); if (cmdLine.hasOption(OPT_SHORT_TPUART)) { - tpuart = cmdLine.getOptionValue(OPT_SHORT_TPUART); + setTpuartSerialPort(cmdLine.getOptionValue(OPT_SHORT_TPUART)); } - logger.debug("tpuart={}", tpuart); + logger.debug("tpuart={}", getTpuartSerialPort()); if (cmdLine.hasOption(OPT_LONG_USB)) { - usbInterface = cmdLine.getOptionValue(OPT_LONG_USB); + setUsbVendorIdAndProductId(cmdLine.getOptionValue(OPT_LONG_USB)); } - logger.debug("usb={}", usbInterface); + logger.debug("usb={}", getUsbVendorIdAndProductId()); if (cmdLine.getArgs().length > 0) { - knxInterface = Utils.parseHost(cmdLine.getArgs()[0]); + setKnxInterface(cmdLine.getArgs()[0]); } else { - knxInterface = null; + setKnxInterface(null); } - logger.debug("knxInterface={}", knxInterface); + logger.debug("knxInterface={}", getKnxInterface()); if (cmdLine.hasOption(OPT_LONG_USER_ID)) { logger.debug("KNX IP Secure --{} is set", OPT_LONG_USER_ID); // log only that it´s, but not the actual value - userId = ((Number)cmdLine.getParsedOptionValue(OPT_LONG_USER_ID)).intValue(); + setKnxSecureUserId(((Number)cmdLine.getParsedOptionValue(OPT_LONG_USER_ID)).intValue()); } if (cmdLine.hasOption(OPT_LONG_USER_PASSWORD)) { logger.debug("KNX IP Secure --{} is set", OPT_LONG_USER_PASSWORD); // log only that it´s, but not the actual value - userPassword = cmdLine.getOptionValue(OPT_LONG_USER_PASSWORD); + setKnxSecureUserPassword(cmdLine.getOptionValue(OPT_LONG_USER_PASSWORD)); } if (cmdLine.hasOption(OPT_LONG_DEVICE_PASSWORD)) { logger.debug("KNX IP Secure --{} is set", OPT_LONG_DEVICE_PASSWORD); // log only that it´s, but not the actual value - devicePassword = cmdLine.getOptionValue(OPT_LONG_DEVICE_PASSWORD); + setKnxSecureDevicePassword(cmdLine.getOptionValue(OPT_LONG_DEVICE_PASSWORD)); } - if (cmdLine.hasOption(OPT_LONG_LOGSTATISTIC)) { - logStatistics = true; - } - logger.debug("logStatistics={}", logStatistics); + setLogStatisticsIsSet(cmdLine.hasOption(OPT_LONG_LOGSTATISTIC)); + logger.debug("logStatistics={}", getLogStatisticsIsSet()); // some logical checks for options which exclude each other // differential mode and eraseflash makes no sense - if (eraseFullFlash() && (!full())) { - full = true; + if (getEraseFullFlashIsSet() && (!getFlashingFullModeIsSet())) { + setFlashingFullModeIsSet(true); logger.info(ansi().fg(RED).a("--{} is set. --> switching to full flash mode").reset().toString(), OPT_LONG_ERASEFLASH); } // nat only possible with tunneling v1 - if (nat() && (!tunnelingV1())) { + if (getNatIsSet() && (!getTunnelingV1isSet())) { throw new CliInvalidException(String.format(ansi().fg(RED).a("Option --%s can only be used together with --%s").reset().toString(), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1)); } // nat not allowed with tunneling v2 - if (nat() && (tunnelingV2())) { + if (getNatIsSet() && (getTunnelingV2isSet())) { throw new CliInvalidException(String.format(ansi().fg(RED).a("Option --%s can not be used together with --%s").reset().toString(), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2)); } // check IP-secure configuration - if (!(userPassword().isEmpty()) || !(devicePassword().isEmpty())) { - if (knxInterface() == null) { + if (!(getKnxSecureUserPassword().isEmpty()) || !(getKnxSecureDevicePassword().isEmpty())) { + if (getKnxInterface().isEmpty()) { throw new CliInvalidException(ansi().fg(RED).a("No IP-Interface specified for IP-secure").reset().toString()); } - else if (!getUsbInterface().isEmpty()) { + else if (!getUsbVendorIdAndProductId().isEmpty()) { throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_USB)).reset().toString()); } - else if (!ft12().isEmpty()) { + else if (!getFt12SerialPort().isEmpty()) { throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_FT12)).reset().toString()); } - else if (!tpuart().isEmpty()) { + else if (!getTpuartSerialPort().isEmpty()) { throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TPUART)).reset().toString()); } - else if (nat()) { + else if (getNatIsSet()) { throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_NAT)).reset().toString()); } - else if (tunnelingV1()) { + else if (getTunnelingV1isSet()) { throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TUNNEL_V1)).reset().toString()); } - else if (tunnelingV2()) { + else if (getTunnelingV2isSet()) { throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TUNNEL_V2)).reset().toString()); } // ensure that all three IP-Secure arguments are set - if ((userPassword().isEmpty()) || (devicePassword().isEmpty())) { + if ((getKnxSecureUserPassword().isEmpty()) || (getKnxSecureDevicePassword().isEmpty())) { throw new CliInvalidException(ansi().fg(RED).a(String.format("For IP-secure --%s, --%s and --%s must be set", OPT_LONG_USER_ID, OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD)).reset().toString()); } } int interfacesSet = 0; - if (knxInterface() != null) interfacesSet++; - if (routing()) interfacesSet++; - if (!ft12().isEmpty()) interfacesSet++; - if (!tpuart().isEmpty()) interfacesSet++; - if (!getUsbInterface().isEmpty()) interfacesSet++; + if (!getKnxInterface().isEmpty()) interfacesSet++; + if (getRoutingIsSet()) interfacesSet++; + if (!getFt12SerialPort().isEmpty()) interfacesSet++; + if (!getTpuartSerialPort().isEmpty()) interfacesSet++; + if (!getUsbVendorIdAndProductId().isEmpty()) interfacesSet++; if (interfacesSet > 1) { throw new CliInvalidException(ansi().fg(RED).a("Only one bus interface can be used.").reset().toString()); @@ -663,114 +634,194 @@ public String helpToString() { return sw.toString(); } - ///\todo move to Utils.java - private byte[] uidToByteArray(String str) { - String[] tokens = str.split(":"); - if (tokens.length != UPDProtocol.UID_LENGTH_USED) { - logger.warn("ignoring --uid {}, wrong size {}, expected {}", str, tokens.length, UPDProtocol.UID_LENGTH_USED); - return null; - } - byte[] uid = new byte[tokens.length]; - for (int n = 0; n < tokens.length; n++) { - uid[n] = (byte) Integer.parseUnsignedInt(tokens[n], 16); - } - return uid; - } - - public InetAddress knxInterface() { + public String getKnxInterface() { return knxInterface; } - public InetAddress host() { - return knxInterface; + private void setKnxInterface(String knxInterface) { + this.knxInterface = knxInterface; } - public String fileName() { + public String getFileName() { return fileName; } - public InetAddress localhost() { + private void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getLocalhost() { return localhost; } - public int localport() { - return localport; + private void setLocalhost(String localhost) { + this.localhost = localhost; } - public int port() { + public int getLocalPort() { + return localPort; + } + + private void setLocalPort(int localPort) { + this.localPort = localPort; + } + + public int getPort() { return port; } - public boolean nat() { - return nat; + private void setPort(int port) { + this.port = port; + } + + public boolean getNatIsSet() { + return natIsSet; + } + + private void setNatIsSet(boolean natIsSet) { + this.natIsSet = natIsSet; + } + + public String getFt12SerialPort() { + return ft12SerialPort; } - public String ft12() { - return ft12; + private void setFt12SerialPort(String ft12SerialPort) { + this.ft12SerialPort = ft12SerialPort; } - public String tpuart() { - return tpuart; + public String getTpuartSerialPort() { + return tpuartSerialPort; } - public boolean tunnelingV2() { return tunnelingV2; } + private void setTpuartSerialPort(String tpuartSerialPort) { + this.tpuartSerialPort = tpuartSerialPort; + } + + public boolean getTunnelingV2isSet() { + return tunnelingV2isSet; + } + + private void setTunnelingV2isSet(boolean tunnelingV2isSet) { + this.tunnelingV2isSet = tunnelingV2isSet; + } + + public boolean getTunnelingV1isSet() { + return tunnelingV1isSet; + } + + private void setTunnelingV1isSet(boolean tunnelingV1isSet) { + this.tunnelingV1isSet = tunnelingV1isSet; + } - public boolean tunnelingV1() { return tunnelingV1; } + public boolean getRoutingIsSet() { + return routingIsSet; + } - public boolean routing() { - return routing; + private void setRoutingIsSet(boolean routingIsSet) { + this.routingIsSet = routingIsSet; } - public String medium() { + public String getMedium() { return medium; } - public IndividualAddress progDevice() { - return progDevice; + private void setMedium(String medium) { + this.medium = medium; + } + + public IndividualAddress getProgDevicePhysicalAddress() { + return progDevicePhysicalAddress; + } + + private void setProgDevicePhysicalAddress(IndividualAddress progDevicePhysicalAddress){ + this.progDevicePhysicalAddress = progDevicePhysicalAddress; } - public IndividualAddress device() { - return device; + public IndividualAddress getDevicePhysicalAddress() { + return devicePhysicalAddress; } - public IndividualAddress ownAddress() { - return ownAddress; + private void setDevicePhysicalAddress(IndividualAddress devicePhysicalAddress) { + this.devicePhysicalAddress = devicePhysicalAddress; } - public byte[] uid() { + public IndividualAddress getOwnPhysicalAddress() { + return ownPhysicalAddress; + } + + private void setOwnPhysicalAddress(IndividualAddress ownPhysicalAddress) { + this.ownPhysicalAddress = ownPhysicalAddress; + } + + public String getUid() { return uid; } - public boolean full() { - return full; + private void setUid(String uidString) { + this.uid = uidString; + } + + public boolean getFlashingFullModeIsSet() { + return flashingFullModeIsSet; + } + + private void setFlashingFullModeIsSet(boolean flashingFullModeIsSet) { + this.flashingFullModeIsSet = flashingFullModeIsSet; + } + + public int getDelayMs() { + return delayMs; } - public int delay() { - return delay; + private void setDelayMs(int delayMs) { + if ((delayMs < Updater.DELAY_MIN) || (delayMs > Updater.DELAY_MAX)) { + logger.warn(ansi().fg(RED).a( + String.format("option --%s {} is invalid (min:{}, max:{}) => set to {}", OPT_LONG_DELAY)).reset().toString(), + delayMs, Updater.DELAY_MIN, Updater.DELAY_MAX, Updater.DELAY_DEFAULT); + delayMs = Updater.DELAY_DEFAULT; // set to DELAY_DEFAULT in case of invalid waiting time + } + this.delayMs = delayMs; } - public boolean NO_FLASH() { - return NO_FLASH; + public boolean getNoFlashIsSet() { + return noFlashIsSet; } - public boolean eraseFullFlash() { - return eraseFullFlash; + private void setNoFlashIsSet(boolean noFlashIsSet) { + this.noFlashIsSet = noFlashIsSet; } - public long dumpFlashStartAddress() { + public boolean getEraseFullFlashIsSet() { + return eraseFullFlashIsSet; + } + + private void setEraseFullFlashIsSet(boolean eraseFullFlashIsSet) { + this.eraseFullFlashIsSet = eraseFullFlashIsSet; + } + + public long getDumpFlashStartAddress() { return dumpFlashStartAddress; } - public long dumpFlashEndAddress() { + private void setDumpFlashStartAddress(long dumpFlashStartAddress) { + this.dumpFlashStartAddress = dumpFlashStartAddress; + } + + public long getDumpFlashEndAddress() { return dumpFlashEndAddress; } - public boolean help() { - return help; + private void setDumpFlashEndAddress(long dumpFlashEndAddress) { + this.dumpFlashEndAddress = dumpFlashEndAddress; + } + + public boolean getHelpIsSet() { + return cmdLine.hasOption(OPT_SHORT_HELP); } - public boolean version() { - return version; + public boolean getVersionIsSet() { + return cmdLine.hasOption(OPT_SHORT_VERSION); } public Level getLogLevel() { @@ -778,36 +829,64 @@ public Level getLogLevel() { return root.getLevel(); } - public void setLogLevel(Level newLevel) { + private void setLogLevel(Level newLevel) { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.setLevel(newLevel); } - public int userId() { - return userId; + public int getKnxSecureUserId() { + return knxSecureUserId; } - public String userPassword() { - return userPassword; + private void setKnxSecureUserId(int knxSecureUserId) { + this.knxSecureUserId = knxSecureUserId; } - public String devicePassword() { - return devicePassword; + public String getKnxSecureUserPassword() { + return knxSecureUserPassword; } - public Priority priority() { + private void setKnxSecureUserPassword(String knxSecureUserPassword) { + this.knxSecureUserPassword = knxSecureUserPassword; + } + + public String getKnxSecureDevicePassword() { + return knxSecureDevicePassword; + } + + private void setKnxSecureDevicePassword(String knxSecureDevicePassword) { + this.knxSecureDevicePassword = knxSecureDevicePassword; + } + + public Priority getPriority() { return priority; } - public boolean logStatistics() { - return logStatistics; + private void setPriority(Priority priority) { + this.priority = priority; + } + + public boolean getLogStatisticsIsSet() { + return logStatisticsIsSet; + } + + private void setLogStatisticsIsSet(boolean logStatisticsIsSet) { + this.logStatisticsIsSet = logStatisticsIsSet; } public int getBlockSize() { return blockSize; } - public String getUsbInterface() { - return usbInterface; + private void setBlockSize(int blockSize) { + this.blockSize = blockSize; + } + + public String getUsbVendorIdAndProductId() { + return usbVendorIdAndProductId; + } + + public void setUsbVendorIdAndProductId(String usbVendorIdAndProductId) { + this.usbVendorIdAndProductId = usbVendorIdAndProductId; } } \ No newline at end of file diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index dfb26cd7..0d69cdc9 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -113,7 +113,7 @@ public void restartDeviceToBootloader(IndividualAddress device) { } } - public byte[] requestUIDFromDevice() + public String requestUIDFromDevice() throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { logger.info("Requesting UID from {}", progDestination.getAddress()); byte[] result = sendWithRetry(UPDCommand.REQUEST_UID, new byte[0], getMaxUpdCommandRetry()).data(); @@ -126,12 +126,12 @@ public byte[] requestUIDFromDevice() byte[] uid; if ((result.length >= UPDProtocol.UID_LENGTH_USED) && (result.length <= UPDProtocol.UID_LENGTH_MAX)){ uid = Arrays.copyOfRange(result, DATA_POSITION, UPDProtocol.UID_LENGTH_USED + DATA_POSITION); - logger.info(" got: {} length {}", Utils.byteArrayToHex(uid), uid.length); - return uid; + logger.info(" got: {} length {}", UPDProtocol.byteArrayToHex(uid), uid.length); + return UPDProtocol.byteArrayToHex(uid); } else { uid = Arrays.copyOfRange(result, DATA_POSITION, result.length - DATA_POSITION); logger.error("Request UID failed {} result.length={}, UID_LENGTH_USED={}, UID_LENGTH_MAX={}", - Utils.byteArrayToHex(uid),uid.length, UPDProtocol.UID_LENGTH_USED, UPDProtocol.UID_LENGTH_MAX); + UPDProtocol.byteArrayToHex(uid),uid.length, UPDProtocol.UID_LENGTH_USED, UPDProtocol.UID_LENGTH_MAX); restartProgrammingDevice(); throw new UpdaterException("Selfbus update failed."); } @@ -202,10 +202,10 @@ public String requestAppVersionString() return new String(result,DATA_POSITION,result.length - DATA_POSITION); // Convert 12 bytes to string starting from result[DATA_POSITION]; } - public void unlockDeviceWithUID(byte[] uid) + public void unlockDeviceWithUID(String uid) throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { - logger.info("Unlocking device {} with UID {}", progDestination.getAddress(), Utils.byteArrayToHex(uid)); - byte[] result = sendWithRetry(UPDCommand.UNLOCK_DEVICE, uid, getMaxUpdCommandRetry()).data(); + logger.info("Unlocking device {} with UID {}", progDestination.getAddress(), uid); + byte[] result = sendWithRetry(UPDCommand.UNLOCK_DEVICE, UPDProtocol.uidToByteArray(uid), getMaxUpdCommandRetry()).data(); if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS.id) { restartProgrammingDevice(); throw new UpdaterException("Selfbus update failed."); diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index 63777833..c2fd28d7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -18,7 +18,9 @@ import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.Collections; +import java.util.Objects; import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; @@ -35,9 +37,9 @@ private KNXNetworkLink createSecureTunnelingLink(InetSocketAddress local, InetSo KNXMediumSettings medium) throws KNXException, InterruptedException { // KNX IP Secure TCP tunneling v2 connection logger.info("Connect using KNX IP Secure tunneling"); - byte[] deviceAuthCode = SecureConnection.hashDeviceAuthenticationPassword(cliOptions.devicePassword().toCharArray()); - byte[] userKey = SecureConnection.hashUserPassword(cliOptions.userPassword().toCharArray()); - final var session = Utils.tcpConnection(local, remote).newSecureSession(cliOptions.userId(), userKey, deviceAuthCode); + byte[] deviceAuthCode = SecureConnection.hashDeviceAuthenticationPassword(cliOptions.getKnxSecureDevicePassword().toCharArray()); + byte[] userKey = SecureConnection.hashUserPassword(cliOptions.getKnxSecureUserPassword().toCharArray()); + final var session = Utils.tcpConnection(local, remote).newSecureSession(cliOptions.getKnxSecureUserId(), userKey, deviceAuthCode); return KNXNetworkLinkIP.newSecureTunnelingLink(session, medium); } @@ -60,9 +62,14 @@ private KNXNetworkLink createRoutingLink(InetSocketAddress local, KNXMediumSetti return KNXNetworkLinkIP.newRoutingLink(local.getAddress(), KNXnetIPRouting.DefaultMulticast, medium); } - private static InetSocketAddress createLocalSocket(final InetAddress host, final Integer port) { - final int p = port != null ? port : 0; - return host != null ? new InetSocketAddress(host, p) : new InetSocketAddress(p); + private static InetSocketAddress createLocalSocket(String host, Integer port) { + port = Objects.requireNonNullElse(port, 0); + if (host.isEmpty()) { + return new InetSocketAddress(port); + } + else { + return new InetSocketAddress(host, port); + } } private static KNXMediumSettings getMedium(final String id, IndividualAddress ownAddress) { @@ -85,6 +92,12 @@ public void setCliOptions(CliOptions cliOptions) { this.cliOptions = cliOptions; } + private InetAddress resolveHost(final String host) throws UnknownHostException { + InetAddress res = InetAddress.getByName(host); + logger.debug("Resolved {} with {}", host, res); + return res; + } + /** * Creates the KNX network link to access the network specified in * options. @@ -94,48 +107,48 @@ public void setCliOptions(CliOptions cliOptions) { * @throws KNXException on problems on link creation * @throws InterruptedException on interrupted thread */ - public KNXNetworkLink openLink() throws KNXException, InterruptedException { + public KNXNetworkLink openLink() throws KNXException, InterruptedException, UnknownHostException { KNXNetworkLink newLink = doOpenLink(); logger.info("KNX connection: {}", newLink); return newLink; } private KNXNetworkLink doOpenLink() throws KNXException, - InterruptedException { - final KNXMediumSettings medium = getMedium(cliOptions.medium(), cliOptions.ownAddress()); + InterruptedException, UnknownHostException { + final KNXMediumSettings medium = getMedium(cliOptions.getMedium(), cliOptions.getOwnPhysicalAddress()); logger.debug("Creating KNX network link {}", medium); - if (!cliOptions.ft12().isEmpty()) { + if (!cliOptions.getFt12SerialPort().isEmpty()) { // create FT1.2 network link - return new KNXNetworkLinkFT12(cliOptions.ft12(), medium); - } else if (!cliOptions.tpuart().isEmpty()) { + return new KNXNetworkLinkFT12(cliOptions.getFt12SerialPort(), medium); + } else if (!cliOptions.getTpuartSerialPort().isEmpty()) { // create TPUART network link - KNXNetworkLinkTpuart linkTpuart = new KNXNetworkLinkTpuart(cliOptions.tpuart(), medium, Collections.emptyList()); - linkTpuart.addAddress(cliOptions.ownAddress()); //\todo check if this is rly needed + KNXNetworkLinkTpuart linkTpuart = new KNXNetworkLinkTpuart(cliOptions.getTpuartSerialPort(), medium, Collections.emptyList()); + linkTpuart.addAddress(cliOptions.getOwnPhysicalAddress()); //\todo check if this is rly needed return linkTpuart; - } else if (!cliOptions.getUsbInterface().isEmpty()) { + } else if (!cliOptions.getUsbVendorIdAndProductId().isEmpty()) { // create USB network link - return new KNXNetworkLinkUsb(cliOptions.getUsbInterface(), medium); + return new KNXNetworkLinkUsb(cliOptions.getUsbVendorIdAndProductId(), medium); } // create local and remote socket address for network link - InetSocketAddress local = createLocalSocket(cliOptions.localhost(), cliOptions.localport()); + InetSocketAddress local = createLocalSocket(cliOptions.getLocalhost(), cliOptions.getLocalPort()); - final InetSocketAddress remote = new InetSocketAddress(cliOptions.knxInterface(), cliOptions.port()); + final InetSocketAddress remote = new InetSocketAddress(resolveHost(cliOptions.getKnxInterface()), cliOptions.getPort()); // Connect using KNX IP Secure - if ((!cliOptions.devicePassword().isEmpty()) && (!cliOptions.userPassword().isEmpty())) { + if ((!cliOptions.getKnxSecureDevicePassword().isEmpty()) && (!cliOptions.getKnxSecureUserPassword().isEmpty())) { return createSecureTunnelingLink(local, remote, medium); } - if (cliOptions.tunnelingV2()) { + if (cliOptions.getTunnelingV2isSet()) { return createTunnelingLinkV2(local, remote, medium); } - if (cliOptions.tunnelingV1()) { - return createTunnelingLinkV1(local, remote, cliOptions.nat(), medium); + if (cliOptions.getTunnelingV1isSet()) { + return createTunnelingLinkV1(local, remote, cliOptions.getNatIsSet(), medium); } - if (cliOptions.routing()) { + if (cliOptions.getRoutingIsSet()) { return createRoutingLink(local, medium); } @@ -148,12 +161,12 @@ private KNXNetworkLink doOpenLink() throws KNXException, // try unsecure UDP tunneling v1 connection with nat option set on cli try { - return createTunnelingLinkV1(local, remote, cliOptions.nat(), medium); + return createTunnelingLinkV1(local, remote, cliOptions.getNatIsSet(), medium); } catch (final KNXException | InterruptedException e) { logger.info(ansi().fg(YELLOW).a("failed with {}").reset().toString(), e.toString()); } // last chance try unsecure UDP tunneling v1 connection with INVERTED nat option set on cli - return createTunnelingLinkV1(local, remote, !cliOptions.nat(), medium); + return createTunnelingLinkV1(local, remote, !cliOptions.getNatIsSet(), medium); } } \ No newline at end of file diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 1dc70ca5..ea56ae11 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -12,6 +12,7 @@ import tuwien.auto.calimero.link.KNXNetworkLink; import java.io.IOException; +import java.net.UnknownHostException; import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; @@ -73,7 +74,7 @@ public static void main(final String[] args) { // read in user-supplied command line options CliOptions options = new CliOptions(args, String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()) , "Selfbus KNX-Firmware update tool options", "", PHYS_ADDRESS_BOOTLOADER, PHYS_ADDRESS_OWN); - if (options.version()) { + if (options.getVersionIsSet()) { logger.info(ansi().fgBright(GREEN).bold().a(Credits.getAsciiLogo()).reset().toString()); logger.info(ansi().fgBright(GREEN).bold().a(Credits.getAuthors()).reset().toString()); ToolInfo.showVersion(); @@ -82,7 +83,7 @@ public static void main(final String[] args) { } logger.info(ansi().fgBright(GREEN).bold().a(ToolInfo.getToolAndVersion()).reset().toString()); - if (options.help()) { + if (options.getHelpIsSet()) { logger.info(options.helpToString()); finalizeJansi(); return; @@ -164,14 +165,13 @@ public void run() { boolean canceled = false; KNXNetworkLink link = null; try { - byte[] uid = cliOptions.uid(); - final String hexFileName = cliOptions.fileName(); + final String hexFileName = cliOptions.getFileName(); BinImage newFirmware = null; if (!hexFileName.isEmpty()) { // check if the firmware file exists if (!Utils.fileExists(hexFileName)) { - logger.error(ansi().fg(RED).a("File '{}' does not exist!").reset().toString(), cliOptions.fileName()); + logger.error(ansi().fg(RED).a("File '{}' does not exist!").reset().toString(), cliOptions.getFileName()); throw new UpdaterException("Selfbus update failed."); } // Load Firmware hex file @@ -193,28 +193,28 @@ public void run() { link = sbKNXLink.openLink(); - DeviceManagement dm = new DeviceManagement(link, cliOptions.progDevice(), cliOptions.priority()); + DeviceManagement dm = new DeviceManagement(link, cliOptions.getProgDevicePhysicalAddress(), cliOptions.getPriority()); - logger.debug("Telegram priority: {}", cliOptions.priority()); + logger.debug("Telegram priority: {}", cliOptions.getPriority()); //for option --device restart the device in bootloader mode - if (cliOptions.device() != null) { // phys. knx address of the device in normal operation + if (cliOptions.getDevicePhysicalAddress() != null) { // phys. knx address of the device in normal operation dm.checkDeviceInProgrammingMode(null); // check that before no device is in programming mode - dm.restartDeviceToBootloader(cliOptions.device()); + dm.restartDeviceToBootloader(cliOptions.getDevicePhysicalAddress()); } - dm.checkDeviceInProgrammingMode(cliOptions.progDevice()); - - if (uid == null) { + dm.checkDeviceInProgrammingMode(cliOptions.getProgDevicePhysicalAddress()); + String uid = cliOptions.getUid(); + if (uid.isEmpty()) { uid = dm.requestUIDFromDevice(); } dm.unlockDeviceWithUID(uid); - if ((cliOptions.dumpFlashStartAddress() >= 0) && (cliOptions.dumpFlashEndAddress() >= 0)) { + if ((cliOptions.getDumpFlashStartAddress() >= 0) && (cliOptions.getDumpFlashEndAddress() >= 0)) { logger.warn(ansi().fgBright(GREEN).a("Dumping flash content range 0x{}-0x{} to bootloader's serial port.").reset().toString(), - String.format("%04X", cliOptions.dumpFlashStartAddress()), String.format("%04X", cliOptions.dumpFlashEndAddress())); - dm.dumpFlashRange(cliOptions.dumpFlashStartAddress(), cliOptions.dumpFlashEndAddress()); + String.format("%04X", cliOptions.getDumpFlashStartAddress()), String.format("%04X", cliOptions.getDumpFlashEndAddress())); + dm.dumpFlashRange(cliOptions.getDumpFlashStartAddress(), cliOptions.getDumpFlashEndAddress()); return; } @@ -237,7 +237,7 @@ public void run() { // From here on we need a valid firmware if (newFirmware == null) { - if (cliOptions.device() != null) { + if (cliOptions.getDevicePhysicalAddress() != null) { dm.restartProgrammingDevice(); } // to get here `uid == null` must be true, so it's fine to exit with no-error @@ -266,13 +266,13 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres newFirmware.startAddress() - bootLoaderIdentity.applicationFirstAddress()); } - if (cliOptions.eraseFullFlash()) { + if (cliOptions.getEraseFullFlashIsSet()) { logger.warn(ansi().fgBright(RED).a("Deleting the entire flash except from the bootloader itself!").reset().toString()); dm.eraseFlash(); } boolean diffMode = false; - if (!(cliOptions.full())) { + if (!(cliOptions.getFlashingFullModeIsSet())) { if (bootDescriptor.valid()) { diffMode = FlashDiffMode.setupDifferentialMode(bootDescriptor); } @@ -293,7 +293,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres cliOptions.getBlockSize(), dm.getBlockSize()); } - if (!cliOptions.NO_FLASH()) { // is flashing firmware disabled? for debugging use only! + if (!cliOptions.getNoFlashIsSet()) { // is flashing firmware disabled? for debugging use only! // Start to flash the new firmware ResponseResult resultTotal; logger.info(ansi().bg(GREEN).fg(BLACK).a("Starting to send new firmware now:").reset().toString()); @@ -302,7 +302,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres resultTotal = FlashDiffMode.doDifferentialFlash(dm, newFirmware.startAddress(), newFirmware.getBinData()); } else { - resultTotal = FlashFullMode.doFullFlash(dm, newFirmware, cliOptions.delay(), !cliOptions.eraseFullFlash(), cliOptions.logStatistics()); + resultTotal = FlashFullMode.doFullFlash(dm, newFirmware, cliOptions.getDelayMs(), !cliOptions.getEraseFullFlashIsSet(), cliOptions.getLogStatisticsIsSet()); } dm.requestBootLoaderStatistic(); @@ -330,13 +330,13 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres BootDescriptor newBootDescriptor = new BootDescriptor(newFirmware.startAddress(), newFirmware.endAddress(), (int) newFirmware.crc32(), newFirmware.getAppVersionAddress()); logger.info("Updating boot descriptor with {}", newBootDescriptor); - dm.programBootDescriptor(newBootDescriptor, cliOptions.delay()); - String deviceInfo = cliOptions.progDevice().toString(); - if (cliOptions.device() != null) { - deviceInfo = cliOptions.device().toString(); + dm.programBootDescriptor(newBootDescriptor, cliOptions.getDelayMs()); + String deviceInfo = cliOptions.getProgDevicePhysicalAddress().toString(); + if (cliOptions.getDevicePhysicalAddress() != null) { + deviceInfo = cliOptions.getDevicePhysicalAddress().toString(); } logger.info("Finished programming device {} with '{}'", ansi().fgBright(YELLOW).a(deviceInfo).reset().toString(), - ansi().fgBright(YELLOW).a(shortenPath(cliOptions.fileName(), 1)).reset().toString()); + ansi().fgBright(YELLOW).a(shortenPath(cliOptions.getFileName(), 1)).reset().toString()); logger.info(ansi().bg(GREEN).fg(BLACK).a("Firmware Update done, Restarting device").reset().toString()); dm.restartProgrammingDevice(); @@ -365,25 +365,25 @@ public String requestUid(){ KNXNetworkLink link; try { link = this.sbKNXLink.openLink(); - DeviceManagement dm = new DeviceManagement(link, cliOptions.progDevice(), cliOptions.priority()); + DeviceManagement dm = new DeviceManagement(link, cliOptions.getProgDevicePhysicalAddress(), cliOptions.getPriority()); //for option --device restart the device in bootloader mode - if (cliOptions.device() != null) { // phys. knx address of the device in normal operation + if (cliOptions.getDevicePhysicalAddress() != null) { // phys. knx address of the device in normal operation dm.checkDeviceInProgrammingMode(null); // check that before no device is in programming mode - dm.restartDeviceToBootloader(cliOptions.device()); + dm.restartDeviceToBootloader(cliOptions.getDevicePhysicalAddress()); } - dm.checkDeviceInProgrammingMode(cliOptions.progDevice()); + dm.checkDeviceInProgrammingMode(cliOptions.getProgDevicePhysicalAddress()); - byte[] uid = dm.requestUIDFromDevice(); + String uid = dm.requestUIDFromDevice(); - if (cliOptions.device() != null) { + if (cliOptions.getDevicePhysicalAddress() != null) { dm.restartProgrammingDevice(); } link.close(); - return Utils.byteArrayToHex(uid); + return uid; - } catch (InterruptedException | UpdaterException | KNXException e) { + } catch (InterruptedException | UpdaterException | KNXException | UnknownHostException e) { throw new RuntimeException(e); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Utils.java b/firmware_updater/updater/src/org/selfbus/updater/Utils.java index 2b580831..a2158068 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Utils.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Utils.java @@ -2,13 +2,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.KNXIllegalArgumentException; import tuwien.auto.calimero.knxnetip.TcpConnection; import java.io.File; -import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import java.util.zip.CRC32; @@ -41,32 +38,6 @@ public static void shortToStream(byte[] stream, int offset, short val) { stream[offset] = (byte) (val); } - public static String byteArrayToHex(byte[] bytes) { - if (bytes == null) { - return ""; - } - - StringBuilder txt = new StringBuilder(); - for (int i = 0; i < bytes.length; i++) { - if (i != 0) { - txt.append(":"); - } - txt.append(String.format("%02X", bytes[i])); - } - return txt.toString(); - } - - public static InetAddress parseHost(final String host) { - try { - InetAddress res = InetAddress.getByName(host); - logger.debug("Resolved {} with {}", host, res); - return res; - } catch (final UnknownHostException e) { - throw new KNXIllegalArgumentException( - "failed to read host " + host, e); - } - } - public static boolean fileExists(String fileName) { File f = new File(fileName); return (f.exists() && f.isFile()); diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java index 1bc84b0c..54331143 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java @@ -49,4 +49,32 @@ public static long checkResult(byte[] result, boolean verbose) { } return udpResult.id; } + + public static byte[] uidToByteArray(String str) { + String[] tokens = str.split(":"); + if (tokens.length != UPDProtocol.UID_LENGTH_USED) { + logger.warn("ignoring --uid {}, wrong size {}, expected {}", str, tokens.length, UPDProtocol.UID_LENGTH_USED); + return null; + } + byte[] uid = new byte[tokens.length]; + for (int n = 0; n < tokens.length; n++) { + uid[n] = (byte) Integer.parseUnsignedInt(tokens[n], 16); + } + return uid; + } + + public static String byteArrayToHex(byte[] bytes) { + if (bytes == null) { + return ""; + } + + StringBuilder txt = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + if (i != 0) { + txt.append(":"); + } + txt.append(String.format("%02X", bytes[i])); + } + return txt.toString(); + } } From 28c3d0a815cbbc41976f1f30dfedc95ef6236eb2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 19:22:10 +0200 Subject: [PATCH 148/359] [Updater] Simplify `if`s --- .../src/org/selfbus/updater/gui/GuiMain.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 792ed30c..0ad4186d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -561,18 +561,12 @@ private void setGuiElementsVisibility() { } for (var guiObjectEntry : GuiObjectsMap.entrySet()) { - if (guiObjectEntry.getValue().contains(selectedScenario) && guiObjectEntry.getValue().contains(GuiObjsVisOpts.ADVSET)) { - if (advancedSettingsCheckBox.isSelected()) { - ((JComponent) guiObjectEntry.getKey()).setVisible(true); - } else { - ((JComponent) guiObjectEntry.getKey()).setVisible(false); - } - - } else if (guiObjectEntry.getValue().contains(selectedScenario)) { - ((JComponent) guiObjectEntry.getKey()).setVisible(true); - } else { - - ((JComponent) guiObjectEntry.getKey()).setVisible(false); + boolean isInScenario = guiObjectEntry.getValue().contains(selectedScenario); + if (isInScenario && guiObjectEntry.getValue().contains(GuiObjsVisOpts.ADVSET)) { + ((JComponent) guiObjectEntry.getKey()).setVisible(advancedSettingsCheckBox.isSelected()); + } + else { + ((JComponent) guiObjectEntry.getKey()).setVisible(isInScenario); } } } From d72bdbe5eb424723d5f201ddb0bc45acf566f30f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 19:28:42 +0200 Subject: [PATCH 149/359] [Updater] Make things `final` --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 0ad4186d..120bbd1f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -169,7 +169,7 @@ public GuiMain() { // externe Definition des ActionListeners, weil dieser vor der Füllung der ComboBox gelöscht und anschließend // wieder eingefügt werden muss - public ActionListener comboBoxIpGatewaysActionListener = new ActionListener() { + public final ActionListener comboBoxIpGatewaysActionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (comboBoxIpGateways.getSelectedItem() == null) return; @@ -416,7 +416,7 @@ private void createUIComponents() { public static class ComboItem { private final String key; - Object value; + final Object value; public ComboItem(String key, Object value) { this.key = key; From 4dce25ce3424aa197950d6c78cc4680e070a2a8a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 19:52:39 +0200 Subject: [PATCH 150/359] [Updater] Use constant string for logging (performance) --- .../org/selfbus/updater/DeviceManagement.java | 6 +++--- .../updater/mode/differential/FlashDiff.java | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 0d69cdc9..cbd57833 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -218,7 +218,7 @@ public void eraseAddressRange(long startAddress, long totalLength) byte[] telegram = new byte[8]; Utils.longToStream(telegram, 0 , startAddress); Utils.longToStream(telegram, 4 , endAddress); - logger.info(String.format("Erasing firmware address range: 0x%04X-0x%04X", startAddress, endAddress)); + logger.info("Erasing firmware address range: {}", String.format("0x%04X-0x%04X", startAddress, endAddress)); Duration oldResponseTimeout = mc.responseTimeout(); Duration newResponseTimeout = MAX_FLASH_ERASE_TIMEOUT.multipliedBy(2); if (oldResponseTimeout.compareTo(newResponseTimeout) < 0) { @@ -350,8 +350,8 @@ public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) byte[] programBootDescriptor = new byte[8]; Utils.longToStream(programBootDescriptor, 0, streamBootDescriptor.length); Utils.longToStream(programBootDescriptor, 4, crc32Value); - logger.debug(String.format("Updating boot descriptor with crc32 0x%08X, length %d", - crc32Value, streamBootDescriptor.length)); + logger.debug("Updating boot descriptor with crc32 {}", + String.format("0x%08X, length %d", crc32Value, streamBootDescriptor.length)); ResponseResult programResult = sendWithRetry(UPDCommand.UPDATE_BOOT_DESC, programBootDescriptor, getMaxUpdCommandRetry()); if (UPDProtocol.checkResult(programResult.data()) != UDPResult.IAP_SUCCESS.id) { restartProgrammingDevice(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java index 3fd5fec7..860659f7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java @@ -157,11 +157,11 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash byte cmdByte = (byte)CMD_COPY; if (bestResult.length <= MAX_LENGTH_SHORT) { cmdByte = (byte)(cmdByte | FLAG_SHORT | (bestResult.length & 0b111111)); // command + 6 bits of the length - logger.trace(String.format("@ b=%02X i=%d CMD_COPY", (cmdByte & 0xff), i)); + logger.trace("@ {} CMD_COPY", String.format("b=%02X i=%d", (cmdByte & 0xff), i)); outputDiffStream.add(cmdByte); } else { cmdByte = (byte)(cmdByte | FLAG_LONG | ((bestResult.length >> 8) & 0b111111)); // command + 6 bits from high byte of the length - logger.trace(String.format("@ b=%02X i=%d CMD_COPY FLAG_LONG", (cmdByte & 0xff), i)); + logger.trace("@ {} CMD_COPY FLAG_LONG", String.format("b=%02X i=%d", (cmdByte & 0xff), i)); byte lengthLowByte = (byte)(bestResult.length & 0xff); // 8 low bits of the length outputDiffStream.add(cmdByte); size += 1; @@ -178,7 +178,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash } else { location = "ROM"; } - logger.trace(String.format("DO COPY FROM %s l=%d sa=%08X",location , bestResult.length, bestResult.offset)); + logger.trace("DO COPY FROM {} {}", location, String.format("l=%d sa=%08X", bestResult.length, bestResult.offset)); byte[] srcData = bestResult.sourceType == SourceType.BACKWARD_RAM ? w.getOldBinData() : img1.getBinData(); StringBuilder traceMessage = new StringBuilder(); for (int k = 0; k < bestResult.length; k++) { @@ -197,8 +197,8 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash outputDiffStream.add(addr1); } else { - logger.trace(String.format("%08x RAW: %02x@ b=%02X i=%d raw", i, img2.getBinData()[i], - (img2.getBinData()[i] & 0xff), i)); + logger.trace("{} raw", String.format("%08x RAW: %02x@ b=%02X i=%d", + i, img2.getBinData()[i], (img2.getBinData()[i] & 0xff), i)); rawBuffer.add(img2.getBinData()[i]); i++; } @@ -209,7 +209,8 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash crc32Block.update(img2.getBinData(), i - FlashPage.PAGE_SIZE, FlashPage.PAGE_SIZE); //p = new FlashPage(img1.getBinData(), i); - logger.trace(String.format("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE)); + logger.trace("# FLASH PAGE startAddrOfPageToBeFlashed={}", + String.format("%08X", i * FlashPage.PAGE_SIZE)); StringBuilder traceMessage = new StringBuilder(); for (int k = pages*FlashPage.PAGE_SIZE; (k < (pages+1)*FlashPage.PAGE_SIZE && k < img2.getBinData().length); k++) { if (k % 16 == 0) { @@ -233,8 +234,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash if (!outputDiffStream.isEmpty()) { crc32Block.reset(); crc32Block.update(img2.getBinData(), img2.getBinData().length - (img2.getBinData().length % FlashPage.PAGE_SIZE), img2.getBinData().length % FlashPage.PAGE_SIZE); - - logger.trace(String.format("# FLASH PAGE startAddrOfPageToBeFlashed=%08X", i*FlashPage.PAGE_SIZE)); + logger.trace("# FLASH PAGE startAddrOfPageToBeFlashed={}", String.format("%08X", i * FlashPage.PAGE_SIZE)); StringBuilder traceMessage = new StringBuilder(); for (int k = pages*FlashPage.PAGE_SIZE; (k < (pages+1)*FlashPage.PAGE_SIZE && k < img2.getBinData().length); k++) { traceMessage.append(String.format("%02X ", (img2.getBinData()[k] & 0xff))); From 5508363f191e2624327bea6d21928e7007ecee0a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 20:00:24 +0200 Subject: [PATCH 151/359] [Updater] Fix uid `NullPointerException` --- .../updater/src/org/selfbus/updater/CliOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index daf785ad..b984683c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -147,7 +147,7 @@ public class CliOptions { private IndividualAddress progDevicePhysicalAddress; private IndividualAddress ownPhysicalAddress; private IndividualAddress devicePhysicalAddress = null; - private String uid; + private String uid = ""; private boolean flashingFullModeIsSet = false; private int delayMs = 0; From 28bf89b6f03712ec8fee052ff301a830dd22a05d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 20:55:22 +0200 Subject: [PATCH 152/359] [Updater] Fix enum `UDPResult` No need to expose/use `UDPResult.id`. Instead use `UDPResult` directly and its `.valueOf`. --- .../org/selfbus/updater/DeviceManagement.java | 12 +++++----- .../org/selfbus/updater/FlashDiffMode.java | 4 ++-- .../org/selfbus/updater/FlashFullMode.java | 10 ++++---- .../org/selfbus/updater/upd/UDPResult.java | 23 ++++++++----------- .../org/selfbus/updater/upd/UPDProtocol.java | 14 +++++------ 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index cbd57833..687234bf 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -206,7 +206,7 @@ public void unlockDeviceWithUID(String uid) throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { logger.info("Unlocking device {} with UID {}", progDestination.getAddress(), uid); byte[] result = sendWithRetry(UPDCommand.UNLOCK_DEVICE, UPDProtocol.uidToByteArray(uid), getMaxUpdCommandRetry()).data(); - if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS.id) { + if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { restartProgrammingDevice(); throw new UpdaterException("Selfbus update failed."); } @@ -231,7 +231,7 @@ public void eraseAddressRange(long startAddress, long totalLength) mc.responseTimeout(oldResponseTimeout); // restore responseTimeout logger.trace("mc.ResponseTimeout restored to {}", mc.responseTimeout()); - if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS.id) { + if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { restartProgrammingDevice(); throw new UpdaterException("Erasing firmware address range failed."); } @@ -240,7 +240,7 @@ public void eraseAddressRange(long startAddress, long totalLength) public void eraseFlash() throws KNXLinkClosedException, InterruptedException, UpdaterException, KNXTimeoutException { byte[] result = sendWithRetry(UPDCommand.ERASE_COMPLETE_FLASH, new byte[0], getMaxUpdCommandRetry()).data(); - if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS.id) { + if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { restartProgrammingDevice(); throw new UpdaterException("Deleting the entire flash failed."); } @@ -253,7 +253,7 @@ public void dumpFlashRange(long startAddress, long endAddress) Utils.longToStream(telegram, 4 , endAddress); // sendWithRetry will always time out, because the mcu is busy dumping the flash byte[] result = sendWithRetry(UPDCommand.DUMP_FLASH, telegram, 0).data(); - if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS.id) { + if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { restartProgrammingDevice(); throw new UpdaterException("Flash dumping failed."); } @@ -281,7 +281,7 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo ResponseResult tmp = sendWithRetry(UPDCommand.SEND_DATA, txBuffer, maxRetry); result.addCounters(tmp); - if (UPDProtocol.checkResult(tmp.data(), false) != UDPResult.IAP_SUCCESS.id) { + if (UPDProtocol.checkResult(tmp.data(), false) != UDPResult.IAP_SUCCESS) { restartProgrammingDevice(); throw new UpdaterException("doFlash failed."); } @@ -353,7 +353,7 @@ public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) logger.debug("Updating boot descriptor with crc32 {}", String.format("0x%08X, length %d", crc32Value, streamBootDescriptor.length)); ResponseResult programResult = sendWithRetry(UPDCommand.UPDATE_BOOT_DESC, programBootDescriptor, getMaxUpdCommandRetry()); - if (UPDProtocol.checkResult(programResult.data()) != UDPResult.IAP_SUCCESS.id) { + if (UPDProtocol.checkResult(programResult.data()) != UDPResult.IAP_SUCCESS) { restartProgrammingDevice(); throw new UpdaterException("Updating boot descriptor failed."); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index 9b298721..5badd32a 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -120,7 +120,7 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start byte[] txBuf = Arrays.copyOf(buf, j); // avoid padded last telegram result.set(dm.sendWithRetry(UPDCommand.SEND_DATA_TO_DECOMPRESS, txBuf, dm.getMaxUpdCommandRetry())); //\todo switch to full flash mode on a NOT_IMPLEMENTED instead of exiting - if (UPDProtocol.checkResult(result.get().data(), false) != UDPResult.IAP_SUCCESS.id) { + if (UPDProtocol.checkResult(result.get().data(), false) != UDPResult.IAP_SUCCESS) { dm.restartProgrammingDevice(); throw new UpdaterException("Selfbus update failed."); } @@ -132,7 +132,7 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start Utils.longToStream(progPars, 0, (int) crc32); logger.debug("Program device next page diff, crc32 0x{}", String.format("%08X", crc32)); result.set(dm.sendWithRetry(UPDCommand.PROGRAM_DECOMPRESSED_DATA, progPars, dm.getMaxUpdCommandRetry())); - if (UPDProtocol.checkResult(result.get().data()) != UDPResult.IAP_SUCCESS.id) { + if (UPDProtocol.checkResult(result.get().data()) != UDPResult.IAP_SUCCESS) { throw new UpdaterException("Selfbus update failed."); } }); diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index 63916212..080ebb6d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -1,5 +1,6 @@ package org.selfbus.updater; +import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; import org.selfbus.updater.upd.UPDProtocol; import org.slf4j.Logger; @@ -11,7 +12,6 @@ import java.io.IOException; import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; -import static org.selfbus.updater.upd.UDPResult.*; /** * Provides full flash mode for the bootloader (MCU) @@ -82,17 +82,17 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa resultProgramData = dm.sendWithRetry(UPDCommand.PROGRAM, progPars, dm.getMaxUpdCommandRetry()); - long result = UPDProtocol.checkResult(resultProgramData.data()); - if ((result == BYTECOUNT_RECEIVED_TOO_LOW.id) || (result == BYTECOUNT_RECEIVED_TOO_HIGH.id)) { + UDPResult result = UPDProtocol.checkResult(resultProgramData.data()); + if ((result == UDPResult.BYTECOUNT_RECEIVED_TOO_LOW) || (result == UDPResult.BYTECOUNT_RECEIVED_TOO_HIGH)) { repeat = true; // do not count failed transfer progressInfo.update(-txBuffer.length); } - else if (result == IAP_COMPARE_ERROR.id) { + else if (result == UDPResult.IAP_COMPARE_ERROR) { throw new UpdaterException(String.format("ProgramData update failed. %s", String.format(ansi().fg(RED).a("Try again with option '--%s 256'").reset().toString(), CliOptions.OPT_LONG_BLOCKSIZE))); } - else if (result == IAP_SUCCESS.id) { + else if (result == UDPResult.IAP_SUCCESS) { progAddress += txBuffer.length; if (logStatistics) { dm.requestBootLoaderStatistic(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java index 16faa2e3..60511673 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java @@ -35,7 +35,7 @@ public enum UDPResult { IAP_BUSY((byte)0x74, "Flash (IAP) Flash programming hardware interface is busy.", true), /** Unknown IAP_Status */ - UDP_IAP_UNKNOWN((byte)0x73, "Unknown IAP_Status", true), + IAP_UNKNOWN((byte)0x73, "Unknown IAP_Status", true), /** Command is not defined */ UNKNOWN_COMMAND((byte)0x5f, "Command unknown", true), @@ -61,7 +61,7 @@ public enum UDPResult { /** Data received is invalid */ INVALID_DATA((byte)0x55, "Data received is invalid", true), /** No data received in telegram */ - UDP_NO_DATA((byte)0x54, "No data received in telegram", true), + NO_DATA((byte)0x54, "No data received in telegram", true), /** Flash page could not be programmed */ FLASH_ERROR((byte)0x53, "Flash page could not be programmed", true), @@ -69,7 +69,7 @@ public enum UDPResult { PAGE_NOT_ALLOWED_TO_ERASE((byte)0x52, "Flash page not allowed to erase", true), /** Address range not allowed to erase */ - UDP_ADDRESS_RANGE_NOT_ALLOWED_TO_ERASE((byte)0x51, "Address range not allowed to erase", true), + ADDRESS_RANGE_NOT_ALLOWED_TO_ERASE((byte)0x51, "Address range not allowed to erase", true), /** Number of bytes received with @ref UPD_SEND_DATA is lower than number of bytes to program with @ref UPD_PROGRAM */ BYTECOUNT_RECEIVED_TOO_LOW((byte)0x50, "Number of bytes received is lower than number of bytes to program", true), @@ -90,14 +90,14 @@ public enum UDPResult { } /** ID of the UDPResult */ - public final byte id; + private final byte id; /** Simple description of the UDPResult */ private final String description; /** True if the UPDResult represents an error */ private final boolean isError; /** - * Create a UDPResult instance from given id, description and error state + * Create a UDPResult from given id, description and error state * @param id ID of the UDPResult * @param description Description of the UDPResult * @param isError Set to true, if the UPDResult represents an error, otherwise set to false @@ -109,22 +109,17 @@ public enum UDPResult { } /** - * Create a UPDResult instance from a given ID + * Create a UPDResult from a given ID * @param index ID of the UPDResult * @return Instance of the UPDResult from the given ID */ - public static UDPResult valueOfIndex(byte index) { - for (UDPResult e: values()) { - if (e.id == index) { - return e; - } - } - return INVALID; + public static UDPResult valueOf(byte index) { + return BY_INDEX.getOrDefault(index, INVALID); } @Override public String toString() { - return this.description; + return String.format("0x%02X %s", this.id, this.description); } /** diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java index 54331143..32902abf 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java @@ -26,28 +26,28 @@ private UPDProtocol() {} */ public static final int UID_LENGTH_MAX = 16; - public static long checkResult(byte[] result) { + public static UDPResult checkResult(byte[] result) { return checkResult(result, true); } - public static long checkResult(byte[] result, boolean verbose) { + public static UDPResult checkResult(byte[] result, boolean verbose) { if (result[COMMAND_POSITION] != UPDCommand.SEND_LAST_ERROR.id) { logger.error("checkResult called on other than UPDCommand.SEND_LAST_ERROR.id=0x{}, result[{}]=0x{}", String.format("%02X", UPDCommand.SEND_LAST_ERROR.id), COMMAND_POSITION, String.format("%02X", result[COMMAND_POSITION])); - return 0; + return UDPResult.INVALID; } - UDPResult udpResult = UDPResult.valueOfIndex(result[DATA_POSITION]); + UDPResult udpResult = UDPResult.valueOf(result[DATA_POSITION]); if (udpResult.isError()) { - logger.error(ansi().fgBright(RED).a("{} resultCode=0x{}").reset().toString(), udpResult, String.format("%02X", udpResult.id)); + logger.error("{}UDPResult: {}{}", ansi().fgBright(RED), udpResult, ansi().reset()); } else { if (verbose) { - logger.trace("done ({})", ansi().fgBright(GREEN).a(udpResult.id).reset().toString()); + logger.trace("{}done ({}){}", ansi().fgBright(GREEN), udpResult, ansi().reset()); } } - return udpResult.id; + return udpResult; } public static byte[] uidToByteArray(String str) { From aeb946a457b75756389a2daa24e4d2b855daa308 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 23:24:52 +0200 Subject: [PATCH 153/359] [Updater] 0x00 is `UPDCommand.INVALID` `0x00` is since commit c0cfb0c reused as `UPDCommand.INVALID` --- .../updater/src/org/selfbus/updater/upd/UPDCommand.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java index 4ac6503e..60e57073 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java @@ -10,12 +10,10 @@ *

*/ public enum UPDCommand { - /* - * Erase flash sector number (data[3]). - *

Device must be unlocked.

- * @deprecated Use ERASE_ADDRESSRANGE instead. + /** + * Marks an invalid {@link UPDCommand} code */ - //ERASE_SECTOR((byte) 0, "ERASE_SECTOR"), + INVALID((byte) 0, "INVALID"), /** * Copy ((data[0] & 0x0f)-1) bytes to ramBuffer starting from address data[3]. *

Device must be unlocked.

From 50532f91b4557a372d9d3cca8d9f4aefb5aa6e0c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 11 Aug 2024 23:39:15 +0200 Subject: [PATCH 154/359] [Updater] Fix enum `UDPCommand` access No need to expose/use `UDPCommand.id`. Instead use `UDPCommand` directly and its `.valueOf`. (Nearly the same as with `UDPResult`) --- .../org/selfbus/updater/DeviceManagement.java | 55 ++++++++----------- .../src/org/selfbus/updater/Updater.java | 9 ++- .../org/selfbus/updater/upd/UDPResult.java | 2 +- .../org/selfbus/updater/upd/UPDCommand.java | 43 +++++++++++++-- .../org/selfbus/updater/upd/UPDProtocol.java | 15 +++-- .../updater/upd/UPDProtocolException.java | 12 ++++ 6 files changed, 87 insertions(+), 49 deletions(-) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocolException.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 687234bf..a29ccd48 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -24,7 +24,6 @@ import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; import static org.selfbus.updater.Mcu.MAX_FLASH_ERASE_TIMEOUT; -import static org.selfbus.updater.upd.UPDProtocol.COMMAND_POSITION; import static org.selfbus.updater.upd.UPDProtocol.DATA_POSITION; /** @@ -117,10 +116,10 @@ public String requestUIDFromDevice() throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { logger.info("Requesting UID from {}", progDestination.getAddress()); byte[] result = sendWithRetry(UPDCommand.REQUEST_UID, new byte[0], getMaxUpdCommandRetry()).data(); - if (result[COMMAND_POSITION] != UPDCommand.RESPONSE_UID.id) { - UPDProtocol.checkResult(result, true); + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.RESPONSE_UID) { restartProgrammingDevice(); - throw new UpdaterException(String.format("Requesting UID failed! result[%d]=0x%02X", COMMAND_POSITION, result[COMMAND_POSITION])); + throw new UpdaterException("Requesting UID failed!"); } byte[] uid; @@ -146,17 +145,15 @@ public BootloaderIdentity requestBootloaderIdentity() telegram[1] = (byte) ToolInfo.versionMinor(); byte[] result = sendWithRetry(UPDCommand.REQUEST_BL_IDENTITY, telegram, getMaxUpdCommandRetry()).data(); - if (result[COMMAND_POSITION] != UPDCommand.RESPONSE_BL_IDENTITY.id) + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.RESPONSE_BL_IDENTITY) { - if (result[COMMAND_POSITION] == UPDCommand.RESPONSE_BL_VERSION_MISMATCH.id) { + if (command == UPDCommand.RESPONSE_BL_VERSION_MISMATCH) { long minMajorVersion = result[DATA_POSITION] & 0xff; long minMinorVersion = result[DATA_POSITION + 1] & 0xff; logger.error(ansi().fg(RED).a("Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.").reset().toString(), ToolInfo.getVersion(), minMajorVersion, minMinorVersion); } - else { - UPDProtocol.checkResult(result); - } restartProgrammingDevice(); throw new UpdaterException("Requesting Bootloader Identity failed!"); } @@ -180,25 +177,24 @@ public BootDescriptor requestBootDescriptor() throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { logger.debug("Requesting Boot Descriptor"); byte[] result = sendWithRetry(UPDCommand.REQUEST_BOOT_DESC, new byte[0], getMaxUpdCommandRetry()).data(); - if (result[COMMAND_POSITION] != UPDCommand.RESPONSE_BOOT_DESC.id) { - UPDProtocol.checkResult(result); + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.RESPONSE_BOOT_DESC) { restartProgrammingDevice(); - throw new UpdaterException(String.format("Boot descriptor request failed! result[%d]=0x%02X, result[%d]=0x%02X", - COMMAND_POSITION, result[COMMAND_POSITION], - DATA_POSITION, result[DATA_POSITION])); + throw new UpdaterException("Boot descriptor request failed!"); } + BootDescriptor bootDescriptor = BootDescriptor.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); logger.info("Current firmware: {}", bootDescriptor); return bootDescriptor; } - public String requestAppVersionString() - throws UpdaterException { + public String requestAppVersionString() throws UpdaterException { byte[] result = sendWithRetry(UPDCommand.APP_VERSION_REQUEST, new byte[0], getMaxUpdCommandRetry()).data(); - if (result[COMMAND_POSITION] != UPDCommand.APP_VERSION_RESPONSE.id){ - UPDProtocol.checkResult(result); - return null; + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.APP_VERSION_RESPONSE) { + return ""; } + return new String(result,DATA_POSITION,result.length - DATA_POSITION); // Convert 12 bytes to string starting from result[DATA_POSITION]; } @@ -363,21 +359,16 @@ public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) } } - public void requestBootLoaderStatistic() - throws UpdaterException { + public void requestBootLoaderStatistic() throws UpdaterException { logger.debug("Requesting Bootloader statistic"); byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); - if (result[COMMAND_POSITION] == UPDCommand.RESPONSE_STATISTIC.id) - { - BootloaderStatistic blStatistic = BootloaderStatistic.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info("Bootloader: {}", blStatistic); - } - else { - logger.warn(ansi().fg(RED).a(" {}").reset().toString(), - String.format("Requesting Bootloader statistic failed! result[%d]=0x%02X, result[%d]=0x%02X", - COMMAND_POSITION, result[COMMAND_POSITION], - DATA_POSITION, result[DATA_POSITION])); + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.RESPONSE_STATISTIC) { + logger.warn("Requesting Bootloader statistic {}failed!{}", ansi().fg(RED), ansi().reset()); } + + BootloaderStatistic blStatistic = BootloaderStatistic.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); + logger.info("Bootloader: {}", blStatistic); //todo does´nt work with ProgressInfo and SpinningCursor } public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetry) @@ -385,7 +376,7 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr ResponseResult result = new ResponseResult(); while (true) { try { - byte[] data2 = mc.sendUpdateData(progDestination, command.id, data); + byte[] data2 = mc.sendUpdateData(progDestination, command.toByte(), data); result.copyFromArray(data2); return result; } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index ea56ae11..0679af68 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -228,11 +228,10 @@ public void run() { logger.debug("Requesting APP_VERSION"); String appVersion = dm.requestAppVersionString(); - if (appVersion != null) { - logger.info("Current APP_VERSION: {}", ansi().fgBright(GREEN).a(appVersion).reset().toString()); - } - else { - logger.info("Requesting APP_VERSION {}", ansi().fgBright(RED).a(" failed!").reset().toString()); + if (appVersion.isEmpty()) { + logger.info("Current APP_VERSION: {}invalid{} ", ansi().fgBright(RED), ansi().reset()); + } else { + logger.info("Current APP_VERSION: {}{}{}", ansi().fgBright(GREEN), appVersion, ansi().reset()); } // From here on we need a valid firmware diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java index 60511673..be353ac8 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java @@ -114,7 +114,7 @@ public enum UDPResult { * @return Instance of the UPDResult from the given ID */ public static UDPResult valueOf(byte index) { - return BY_INDEX.getOrDefault(index, INVALID); + return BY_INDEX.getOrDefault(index, UDPResult.INVALID); } @Override diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java index 60e57073..a4eb2d70 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java @@ -1,5 +1,8 @@ package org.selfbus.updater.upd; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.HashMap; import java.util.Map; @@ -131,19 +134,49 @@ public enum UPDCommand { BY_INDEX.put(e.id, e); } } - - public final byte id; + private static final Logger logger = LoggerFactory.getLogger(UPDCommand.class); + private final byte id; private final String description; + UPDCommand(byte id, String description) { this.id = id; this.description = description; } - public static UPDCommand valueOfIndex(Integer index) { - return BY_INDEX.get(index); + public static UPDCommand valueOf(byte index) { + return BY_INDEX.getOrDefault(index, UPDCommand.INVALID); + } + + public byte toByte() { + return id; } + + //todo get rid of this stupid "helper" method (needs a little bit more refactoring) + public static UPDCommand tryFromByteArray(byte[] bytes) { + try { + return fromByteArray(bytes); + } + catch (UPDProtocolException e) { + logger.error("Failed with {}", e.getMessage()); + logger.error("bytes: {}", bytes); + return UPDCommand.INVALID; + } + } + + public static UPDCommand fromByteArray(byte[] bytes) throws UPDProtocolException { + if (bytes == null) { + throw new UPDProtocolException("bytes==null"); + } + + if (bytes.length < UPDProtocol.getCommandPosition() - 1) { + throw new UPDProtocolException(String.format("Too few bytes. Expected #%d called with #%d", + bytes.length, UPDProtocol.getCommandPosition() - 1)); + } + return UPDCommand.valueOf(bytes[UPDProtocol.getCommandPosition()]); + } + @Override public String toString() { - return String.format("%s.%s", this.getClass().getSimpleName(), this.description); + return String.format("0x%02X %s %s", this.id, this.getClass().getSimpleName(), this.description); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java index 32902abf..05061fbf 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java @@ -15,7 +15,7 @@ private UPDProtocol() {} private static final Logger logger = LoggerFactory.getLogger(UPDProtocol.class); - public static final int COMMAND_POSITION = 2; + private static final int COMMAND_POSITION = 2; public static final int DATA_POSITION = 3; /** * uid/guid length of the mcu used for unlocking/flashing. @@ -31,11 +31,10 @@ public static UDPResult checkResult(byte[] result) { } public static UDPResult checkResult(byte[] result, boolean verbose) { - if (result[COMMAND_POSITION] != UPDCommand.SEND_LAST_ERROR.id) { - logger.error("checkResult called on other than UPDCommand.SEND_LAST_ERROR.id=0x{}, result[{}]=0x{}", - String.format("%02X", UPDCommand.SEND_LAST_ERROR.id), - COMMAND_POSITION, - String.format("%02X", result[COMMAND_POSITION])); + UPDCommand command = UPDCommand.valueOf(result[getCommandPosition()]); + if (command != UPDCommand.SEND_LAST_ERROR) { + logger.error("checkResult called on other than {}, ", UPDCommand.SEND_LAST_ERROR); + logger.error(" result[{}]=0x{}", getCommandPosition(), String.format("%02X", result[getCommandPosition()])); return UDPResult.INVALID; } @@ -77,4 +76,8 @@ public static String byteArrayToHex(byte[] bytes) { } return txt.toString(); } + + public static int getCommandPosition() { + return COMMAND_POSITION; + } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocolException.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocolException.java new file mode 100644 index 00000000..daba2ddd --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocolException.java @@ -0,0 +1,12 @@ +package org.selfbus.updater.upd; + +public class UPDProtocolException extends Exception { + @SuppressWarnings("unused") + private UPDProtocolException(Throwable e) { + super(e); + } + + public UPDProtocolException(String message) { + super(message); + } +} From 3802ef61c50164493a507713324c4ad95cbb8814 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 12 Aug 2024 00:03:01 +0200 Subject: [PATCH 155/359] [Updater] Collapse `catch` The same `UpdaterException` with same message is thrown on `KNXIllegalArgumentException` and `Throwable`. --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index a29ccd48..fc897eaf 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -388,12 +388,8 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr logger.warn(ansi().fg(RED).a("{} {} : {}").reset().toString(), command, e.getMessage(), e.getClass().getSimpleName()); result.incDropCount(); } - catch (KNXIllegalArgumentException e) { - throw new UpdaterException(String.format("%s failed.", command), e); - } catch (Throwable e) { throw new UpdaterException(String.format("%s failed.", command), e); - // logger.error("{}{} Exception {}{}", ConColors.RED, command, e, ConColors.RESET); } if (maxRetry > 0) { From ab53dfa8cc3ba9d31fae61a18f8717bba385d874 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 12 Aug 2024 02:26:57 +0200 Subject: [PATCH 156/359] [GUI] Move `actionListener` to separate methods Simplified start/stop button and translation handling. --- .../src/org/selfbus/updater/gui/GuiMain.java | 122 ++++++++++-------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 120bbd1f..71070a9f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -99,71 +99,87 @@ public class GuiMain extends JFrame { private static final String FILENAME_SETTINGS = "settings.xml"; private static final String LANGUAGE_RESOURCE_BUNDLE = "language/GuiMain"; - public GuiMain() { - //todo load language from FILENAME_SETTINGS and set it here. - // Has to be done before calling $$$setupUI$$$(); - //Locale.setDefault(Locale.ENGLISH); // for tests - //Locale.setDefault(Locale.ROOT); // default language for tests - $$$setupUI$$$(); - guiTranslation = ResourceBundle.getBundle(LANGUAGE_RESOURCE_BUNDLE, Locale.getDefault()); - buttonLoadFile.addActionListener(actionListener -> { - JFileChooser fc = new JFileChooser(); - String filePath = textFieldFileName.getText(); + String getTranslation(String text) { + return $$$getMessageFromBundle$$$(LANGUAGE_RESOURCE_BUNDLE, text); + } - if (filePath != null && !filePath.isEmpty()) { - fc.setCurrentDirectory(new File(filePath).getParentFile()); - } - fc.setFileFilter(new FileNameExtensionFilter("HEX", "hex")); - int result = fc.showOpenDialog(panelMain); + private boolean getUpdaterIsRunning() { + if (updaterThread == null) { + return false; + } + return updaterThread.getState() != Thread.State.TERMINATED; + } - if (result == JFileChooser.APPROVE_OPTION) { - if (fc.getSelectedFile().exists()) { - textFieldFileName.setText(fc.getSelectedFile().toString()); - } - } - }); + private void handleStartStopFlashAction() { + if (getUpdaterIsRunning()) { + updaterThread.interrupt(); + updaterFinished(); + logger.info(getTranslation("logMessageCanceledFlashing")); + return; + } - buttonStartStopFlash.addActionListener(actionListener -> { - String stopFlash = guiTranslation.getString("stopFlash"); - String startFlash = guiTranslation.getString("startFlash"); + jLoggingPane.setText(""); + jLoggingPane.setFocusable(false); // Needed for the SpinningCursor to work properly + updaterThread = new Thread(() -> { + setCliOptions(); + final Updater updater = new Updater(cliOptions); + updater.run(); + SwingUtilities.invokeLater(this::updaterFinished); + }); + updaterThread.start(); + buttonStartStopFlash.setText(getTranslation("stopFlash")); + } - if (Objects.equals(buttonStartStopFlash.getText(), stopFlash)) { - if (updaterThread != null) { - updaterThread.interrupt(); - buttonStartStopFlash.setText(startFlash); - logger.info(guiTranslation.getString("logMessageCanceledFlashing")); - return; - } - } + private void updaterFinished() { + buttonStartStopFlash.setText(getTranslation("startFlash")); + jLoggingPane.setFocusable(true); + } - jLoggingPane.setText(""); - updaterThread = new Thread(() -> { - setCliOptions(); + private void handleLoadFileAction() { + JFileChooser fc = new JFileChooser(); + String filePath = textFieldFileName.getText(); - final Updater updater = new Updater(cliOptions); - updater.run(); - SwingUtilities.invokeLater(() -> guiMainInstance.buttonStartStopFlash.setText(startFlash)); - }); - updaterThread.start(); - buttonStartStopFlash.setText(stopFlash); - }); + if (filePath != null && !filePath.isEmpty()) { + fc.setCurrentDirectory(new File(filePath).getParentFile()); + } + fc.setFileFilter(new FileNameExtensionFilter("HEX", "hex")); + int result = fc.showOpenDialog(panelMain); - buttonRequestUid.addActionListener(actionListener -> { + if (result == JFileChooser.APPROVE_OPTION) { + if (fc.getSelectedFile().exists()) { + textFieldFileName.setText(fc.getSelectedFile().toString()); + } + } + } - updaterThread = new Thread(() -> { - setCliOptions(); - final Updater upd = new Updater(cliOptions); - String uid = upd.requestUid(); - SwingUtilities.invokeLater(() -> guiMainInstance.textFieldUid.setText(uid)); - }); - updaterThread.start(); + private void handleRequestUidAction() { + updaterThread = new Thread(() -> { + setCliOptions(); + final Updater upd = new Updater(cliOptions); + String uid = upd.requestUid(); + SwingUtilities.invokeLater(() -> guiMainInstance.textFieldUid.setText(uid)); }); + updaterThread.start(); + } - comboBoxScenario.addActionListener(actionListener -> setGuiElementsVisibility()); + private void handleReloadGatewaysAction() { + new Thread(this::loadKnxIpInterfacesAndFillComboBox).start(); + } - advancedSettingsCheckBox.addActionListener(actionListener -> setGuiElementsVisibility()); - reloadGatewaysButton.addActionListener(actionListener -> new Thread(this::loadKnxIpInterfacesAndFillComboBox).start()); + public GuiMain() { + //todo load language from FILENAME_SETTINGS and set it here. + // Has to be done before calling $$$setupUI$$$(); + //Locale.setDefault(Locale.ENGLISH); // for tests + //Locale.setDefault(Locale.ROOT); // default language for tests + $$$setupUI$$$(); + guiTranslation = ResourceBundle.getBundle(LANGUAGE_RESOURCE_BUNDLE, Locale.getDefault()); + buttonLoadFile.addActionListener(actionEvent -> handleLoadFileAction()); + buttonStartStopFlash.addActionListener(actionEvent -> handleStartStopFlashAction()); + buttonRequestUid.addActionListener(actionEvent -> handleRequestUidAction()); + comboBoxScenario.addActionListener(actionEvent -> setGuiElementsVisibility()); + advancedSettingsCheckBox.addActionListener(actionEvent -> setGuiElementsVisibility()); + reloadGatewaysButton.addActionListener(actionEvent -> handleReloadGatewaysAction()); comboBoxIpGateways.addActionListener(comboBoxIpGatewaysActionListener); } From 101b974f95953302cb159e9523d28b7f8b12ade1 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 17 Aug 2024 20:54:24 +0200 Subject: [PATCH 157/359] [GUI] Set minimum Size of `GuiMain` form to 400x300 --- .../updater/src/org/selfbus/updater/gui/GuiMain.form | 1 + .../updater/src/org/selfbus/updater/gui/GuiMain.java | 1 + 2 files changed, 2 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form index f63a2c74..5c1d3def 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form @@ -7,6 +7,7 @@ + diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 71070a9f..9f4479f0 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -600,6 +600,7 @@ private void setGuiElementsVisibility() { panelMain = new JPanel(); panelMain.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); panelMain.setAutoscrolls(true); + panelMain.setMinimumSize(new Dimension(400, 300)); advancedSettingsCheckBox = new JCheckBox(); advancedSettingsCheckBox.setSelected(false); this.$$$loadButtonText$$$(advancedSettingsCheckBox, this.$$$getMessageFromBundle$$$("language/GuiMain", "advancedSettings")); From 422572d2a362d78762fa280a3363719a14b33025 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 17 Aug 2024 21:06:05 +0200 Subject: [PATCH 158/359] =?UTF-8?q?[GUI]=20Use=20working=20directory=20in?= =?UTF-8?q?=20case=20last=20hex=20file=20directory=20doesn=C2=B4t=20exist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/org/selfbus/updater/gui/GuiMain.java | 22 +++++++++++++++---- .../src/resources/language/GuiMain.properties | 6 ++++- .../resources/language/GuiMain_de.properties | 6 ++++- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 9f4479f0..f83b13a9 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -137,12 +137,26 @@ private void updaterFinished() { private void handleLoadFileAction() { JFileChooser fc = new JFileChooser(); - String filePath = textFieldFileName.getText(); + String fileName = textFieldFileName.getText(); + + File initialDirectory = null; + if (fileName != null && !fileName.isEmpty()) { + // try to set to the directory of the last .hex firmware + initialDirectory = new File(fileName).getParentFile(); + if (!initialDirectory.exists() || !initialDirectory.isDirectory()) { + initialDirectory = null; + } + } - if (filePath != null && !filePath.isEmpty()) { - fc.setCurrentDirectory(new File(filePath).getParentFile()); + if (initialDirectory == null) { + // set to current working directory + String currentDirectory = System.getProperty("user.dir"); + initialDirectory = new File(currentDirectory); } - fc.setFileFilter(new FileNameExtensionFilter("HEX", "hex")); + + fc.setCurrentDirectory(initialDirectory); + + fc.setFileFilter(new FileNameExtensionFilter(getTranslation("loadFile.firmwareFilterDescription"), "hex")); int result = fc.showOpenDialog(panelMain); if (result == JFileChooser.APPROVE_OPTION) { diff --git a/firmware_updater/updater/src/resources/language/GuiMain.properties b/firmware_updater/updater/src/resources/language/GuiMain.properties index 91dc9475..7c653fe6 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain.properties @@ -60,4 +60,8 @@ selectInterface=select Interface diffFlash=differential Flash diffFlashHint=flash only changed parts of software
\\ [experimental] logMessageCanceledFlashing=Flash process canceled. -labelScenarioHint=Label \ No newline at end of file +labelScenarioHint=Label +loadFile.firmwareFilterDescription=Firmware (*.hex) +Warning=Warning +IOException.loadingSettings.Message=Could not load settings from file %s.
%s +IOException.savingSettings.Message=Could not save settings to file %s.
%s \ No newline at end of file diff --git a/firmware_updater/updater/src/resources/language/GuiMain_de.properties b/firmware_updater/updater/src/resources/language/GuiMain_de.properties index 3398d6d2..3cfd57e6 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain_de.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain_de.properties @@ -62,4 +62,8 @@ diffFlash=differenzieller Flash Vorgang diffFlashHint=Es werden nur veränderte Teile der Software
\ übertragen [experimentell] logMessageCanceledFlashing=Flash Vorgang wurde abgebrochen. -labelScenarioHint=Label \ No newline at end of file +labelScenarioHint=Label +loadFile.firmwareFilterDescription=Firmware (*.hex) +Warning=Warning +IOException.loadingSettings.Message=Die Einstellungen konnten nicht aus der Datei file %s geladen werden.
%s +IOException.savingSettings.Message=Die Einstellungen konnten nicht in die Datei %s gespeichert werden.
%s \ No newline at end of file From 4af47e627f64dff437dad4c9b2f315daf1dcd625 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 17 Aug 2024 21:14:02 +0200 Subject: [PATCH 159/359] [GUI] Use `getTranslation` which was added in ab53dfa --- .../src/org/selfbus/updater/gui/GuiMain.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index f83b13a9..5578b126 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -90,7 +90,6 @@ public class GuiMain extends JFrame { private JScrollPane mainScrollPane; private JButton reloadGatewaysButton; - private final ResourceBundle guiTranslation; private CliOptions cliOptions; private Thread updaterThread; public static GuiMain guiMainInstance; @@ -186,8 +185,6 @@ public GuiMain() { //Locale.setDefault(Locale.ENGLISH); // for tests //Locale.setDefault(Locale.ROOT); // default language for tests $$$setupUI$$$(); - guiTranslation = ResourceBundle.getBundle(LANGUAGE_RESOURCE_BUNDLE, Locale.getDefault()); - buttonLoadFile.addActionListener(actionEvent -> handleLoadFileAction()); buttonStartStopFlash.addActionListener(actionEvent -> handleStartStopFlashAction()); buttonRequestUid.addActionListener(actionEvent -> handleRequestUidAction()); @@ -377,7 +374,7 @@ private void loadKnxIpInterfacesAndFillComboBox() { comboBoxIpGateways.removeAllItems(); - comboBoxIpGateways.addItem(new CalimeroSearchComboItem(guiTranslation.getString("selectInterface"), null)); + comboBoxIpGateways.addItem(new CalimeroSearchComboItem(getTranslation("selectInterface"), null)); DiscoverKnxInterfaces.getAllInterfaces().forEach(r -> comboBoxIpGateways.addItem(new CalimeroSearchComboItem(r.response().getDevice().getName() + @@ -389,9 +386,9 @@ private void loadKnxIpInterfacesAndFillComboBox() { private void fillScenarios() { if (comboBoxScenario == null) return; - comboBoxScenario.addItem(new ComboItem(guiTranslation.getString(GuiObjsVisOpts.NEWDEV.getVisOption()), GuiObjsVisOpts.NEWDEV)); - comboBoxScenario.addItem(new ComboItem(guiTranslation.getString(GuiObjsVisOpts.APPDEV.getVisOption()), GuiObjsVisOpts.APPDEV)); - comboBoxScenario.addItem(new ComboItem(guiTranslation.getString(GuiObjsVisOpts.REQUID.getVisOption()), GuiObjsVisOpts.REQUID)); + comboBoxScenario.addItem(new ComboItem(getTranslation(GuiObjsVisOpts.NEWDEV.getVisOption()), GuiObjsVisOpts.NEWDEV)); + comboBoxScenario.addItem(new ComboItem(getTranslation(GuiObjsVisOpts.APPDEV.getVisOption()), GuiObjsVisOpts.APPDEV)); + comboBoxScenario.addItem(new ComboItem(getTranslation(GuiObjsVisOpts.REQUID.getVisOption()), GuiObjsVisOpts.REQUID)); } private void fillMediumComboBox() { @@ -580,13 +577,13 @@ private void setGuiElementsVisibility() { switch (selectedScenario) { case NEWDEV: - labelScenarioHint.setText(guiTranslation.getString("newDeviceHint")); + labelScenarioHint.setText(getTranslation("newDeviceHint")); break; case APPDEV: - labelScenarioHint.setText(guiTranslation.getString("appDeviceHint")); + labelScenarioHint.setText(getTranslation("appDeviceHint")); break; case REQUID: - labelScenarioHint.setText(guiTranslation.getString("requestUidHint")); + labelScenarioHint.setText(getTranslation("requestUidHint")); break; } @@ -594,8 +591,7 @@ private void setGuiElementsVisibility() { boolean isInScenario = guiObjectEntry.getValue().contains(selectedScenario); if (isInScenario && guiObjectEntry.getValue().contains(GuiObjsVisOpts.ADVSET)) { ((JComponent) guiObjectEntry.getKey()).setVisible(advancedSettingsCheckBox.isSelected()); - } - else { + } else { ((JComponent) guiObjectEntry.getKey()).setVisible(isInScenario); } } From ba6b914ffb6c21c97d5068b4eac625fbc703b8b9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 17 Aug 2024 21:17:27 +0200 Subject: [PATCH 160/359] [GUI] No need to save `guiMainInstance`, as we can access it with `this` --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 5578b126..41f80659 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -92,7 +92,6 @@ public class GuiMain extends JFrame { private CliOptions cliOptions; private Thread updaterThread; - public static GuiMain guiMainInstance; private final static Logger logger = LoggerFactory.getLogger(GuiMain.class); private static final Properties userProperties = new Properties(); private static final String FILENAME_SETTINGS = "settings.xml"; @@ -170,7 +169,7 @@ private void handleRequestUidAction() { setCliOptions(); final Updater upd = new Updater(cliOptions); String uid = upd.requestUid(); - SwingUtilities.invokeLater(() -> guiMainInstance.textFieldUid.setText(uid)); + SwingUtilities.invokeLater(() -> this.textFieldUid.setText(uid)); }); updaterThread.start(); } @@ -192,6 +191,7 @@ public GuiMain() { advancedSettingsCheckBox.addActionListener(actionEvent -> setGuiElementsVisibility()); reloadGatewaysButton.addActionListener(actionEvent -> handleReloadGatewaysAction()); comboBoxIpGateways.addActionListener(comboBoxIpGatewaysActionListener); + startUpdaterGui(); } // externe Definition des ActionListeners, weil dieser vor der Füllung der ComboBox gelöscht und anschließend @@ -325,10 +325,7 @@ private void setCliOptions() { } public static void startSwingGui() { - SwingUtilities.invokeLater(() -> { - guiMainInstance = new GuiMain(); - guiMainInstance.startUpdaterGui(); - }); + new GuiMain(); } public void startUpdaterGui() { From e6dc081f0300615a7eb7cbbcad649169e24a2b82 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 17 Aug 2024 21:34:01 +0200 Subject: [PATCH 161/359] [GUI] Use `json` to save/load settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Java doesn´t support nested `Properties`, which [FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) does. This change simplifies the saving/loading of settings by just setting the component names in `setComponentNames()`, which IntelliJ IDEA unfortunately does not do, even if the component has a name specified in the `*.form`. --- firmware_updater/updater/build.gradle | 4 + .../src/org/selfbus/updater/gui/GuiMain.java | 159 ++++++++---------- .../org/selfbus/updater/gui/GuiSettings.java | 129 ++++++++++++++ 3 files changed, 205 insertions(+), 87 deletions(-) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 538d9d72..b947344c 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -128,6 +128,10 @@ dependencies { // GUI implementation files('libs/forms_rt.jar') + // GUI settings + implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2' + //implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' + // Intel hex parser // commit 9dec823 of 2023/02/27 // https://github.com/j123b567/java-intelhex-parser/commit/9dec82355697dba028e8aa1832b8b9ee1b66d9e2 diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 41f80659..9bed6fe0 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -19,8 +19,6 @@ import java.awt.*; import java.awt.event.*; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; @@ -93,8 +91,8 @@ public class GuiMain extends JFrame { private CliOptions cliOptions; private Thread updaterThread; private final static Logger logger = LoggerFactory.getLogger(GuiMain.class); - private static final Properties userProperties = new Properties(); - private static final String FILENAME_SETTINGS = "settings.xml"; + private final GuiSettings guiSettings = new GuiSettings(this); + private static final String FILENAME_SETTINGS = "settings.json"; private static final String LANGUAGE_RESOURCE_BUNDLE = "language/GuiMain"; String getTranslation(String text) { @@ -207,82 +205,64 @@ public void actionPerformed(ActionEvent e) { } }; + private void setComponentNames() { + setName("MainFrame"); + advancedSettingsCheckBox.setName("AdvancedSettings"); // 1 + textBoxKnxGatewayIpAddr.setName("GatewayIpAddr"); + textFieldPort.setName("GatewayPort"); + natCheckBox.setName("UseNat"); + textFieldTpuart.setName("TpUart"); // 5 + textFieldSerial.setName("Serial"); + comboBoxMedium.setName("Medium"); + comboBoxScenario.setName("Scenario"); + textFieldFileName.setName("FlashFilePath"); + textFieldUid.setName("Uid"); // 10 + CheckBoxDiffFlash.setName("DiffFlash"); + eraseCompleteFlashCheckBox.setName("EraseCompleteFlash"); + noFlashCheckBox.setName("NoFlash"); + textFieldBootloaderDeviceAddress.setName("BootloaderDeviceAddress"); + textFieldDeviceAddress.setName("DeviceAddress"); // 15 + textFieldOwnAddress.setName("OwnAddress"); + textFieldDelay.setName("DelayMs"); + comboBoxKnxTelegramPriority.setName("TelegramPriority"); + textFieldKnxSecureUser.setName("SecureUser"); + textFieldKnxSecureUserPwd.setName("SecureUserPassword"); //20 + textFieldKnxSecureDevicePwd.setName("SecureDevicePassword"); //21 + } + @SuppressWarnings("SameParameterValue") private void saveAllParameters(String fileName) { - userProperties.setProperty("AdvancedSettings", advancedSettingsCheckBox.isSelected() ? "true" : "false"); - userProperties.setProperty("GatewayIpAddr", textBoxKnxGatewayIpAddr.getText()); - userProperties.setProperty("GatewayPort", textFieldPort.getText()); - userProperties.setProperty("UseNat", natCheckBox.isSelected() ? "true" : "false"); - userProperties.setProperty("TpUart", textFieldTpuart.getText()); - userProperties.setProperty("Serial", textFieldSerial.getText()); - userProperties.setProperty("Medium", Objects.requireNonNull(comboBoxMedium.getSelectedItem()).toString()); - userProperties.setProperty("Scenario", String.valueOf(((ComboItem) Objects.requireNonNull(comboBoxScenario.getSelectedItem())).getValue())); - userProperties.setProperty("FlashFilePath", textFieldFileName.getText()); - userProperties.setProperty("Uid", textFieldUid.getText()); - userProperties.setProperty("DiffFlash", CheckBoxDiffFlash.isSelected() ? "true" : "false"); - userProperties.setProperty("EraseCompleteFlash", eraseCompleteFlashCheckBox.isSelected() ? "true" : "false"); - userProperties.setProperty("NoFlash", noFlashCheckBox.isSelected() ? "true" : "false"); - userProperties.setProperty("BootloaderDeviceAddress", textFieldBootloaderDeviceAddress.getText()); - userProperties.setProperty("DeviceAddress", textFieldDeviceAddress.getText()); - userProperties.setProperty("OwnAddress", textFieldOwnAddress.getText()); - userProperties.setProperty("DelayMs", textFieldDelay.getText()); - userProperties.setProperty("TelegramPriority", Objects.requireNonNull(comboBoxKnxTelegramPriority.getSelectedItem()).toString()); - userProperties.setProperty("SecureUser", textFieldKnxSecureUser.getText()); - userProperties.setProperty("SecureUserPassword", textFieldKnxSecureUserPwd.getText()); - userProperties.setProperty("SecureDevicePassword", textFieldKnxSecureDevicePwd.getText()); - userProperties.setProperty("WindowSizeHeight", String.valueOf(this.getSize().height)); - userProperties.setProperty("WindowSizeWidth", String.valueOf(this.getSize().width)); - try { - userProperties.storeToXML(new FileOutputStream(fileName), ""); - } catch (IOException ex) { - throw new RuntimeException(ex); + guiSettings.writeComponentSettings(fileName); + } catch (IOException e) { + JOptionPane.showMessageDialog(this, + String.format(getTranslation("IOException.savingSettings.Message"), fileName, e.getMessage()), + this.getTranslation("Warning"), JOptionPane.WARNING_MESSAGE); } } @SuppressWarnings("SameParameterValue") - private void loadAllParameters(String fileName) { - - if (new File(fileName).exists()) { - try { - userProperties.loadFromXML(new FileInputStream(fileName)); - - advancedSettingsCheckBox.setSelected(Boolean.parseBoolean(userProperties.getProperty("AdvancedSettings"))); - textBoxKnxGatewayIpAddr.setText(userProperties.getProperty("GatewayIpAddr")); - textFieldPort.setText(userProperties.getProperty("GatewayPort")); - natCheckBox.setSelected(Boolean.parseBoolean(userProperties.getProperty("UseNat"))); - textFieldTpuart.setText(userProperties.getProperty("TpUart")); - textFieldSerial.setText(userProperties.getProperty("Serial")); - comboBoxMedium.setSelectedItem(userProperties.getProperty("Medium")); - textFieldFileName.setText(userProperties.getProperty("FlashFilePath")); - textFieldUid.setText(userProperties.getProperty("Uid")); - CheckBoxDiffFlash.setSelected(Boolean.parseBoolean(userProperties.getProperty("DiffFlash"))); - eraseCompleteFlashCheckBox.setSelected(Boolean.parseBoolean(userProperties.getProperty("EraseCompleteFlash"))); - noFlashCheckBox.setSelected(Boolean.parseBoolean(userProperties.getProperty("NoFlash"))); - textFieldBootloaderDeviceAddress.setText(userProperties.getProperty("BootloaderDeviceAddress")); - textFieldDeviceAddress.setText(userProperties.getProperty("DeviceAddress")); - textFieldOwnAddress.setText(userProperties.getProperty("OwnAddress")); - textFieldDelay.setText(userProperties.getProperty("DelayMs")); - comboBoxKnxTelegramPriority.setSelectedItem(userProperties.getProperty("TelegramPriority")); - textFieldKnxSecureUser.setText(userProperties.getProperty("SecureUser")); - textFieldKnxSecureUserPwd.setText(userProperties.getProperty("SecureUserPassword")); - textFieldKnxSecureDevicePwd.setText(userProperties.getProperty("SecureDevicePassword")); - - this.setSize(Integer.parseInt(userProperties.getProperty("WindowSizeWidth")), Integer.parseInt(userProperties.getProperty("WindowSizeHeight"))); - - for (int i = 0; i < comboBoxScenario.getItemCount(); i++) { - if (Objects.equals(String.valueOf(comboBoxScenario.getItemAt(i).value), userProperties.getProperty("Scenario"))) { - comboBoxScenario.setSelectedItem(comboBoxScenario.getItemAt(i)); - break; - } - } - - setGuiElementsVisibility(); - - } catch (IOException ex) { - throw new RuntimeException(ex); + private boolean loadAllParameters(String fileName) { + try { + setComponentNames(); + guiSettings.readComponentsSettings(fileName); + } catch (IOException e) { + JOptionPane.showMessageDialog(this, + String.format(getTranslation("IOException.loadingSettings.Message"), fileName, e.getMessage()), + this.getTranslation("Warning"), JOptionPane.WARNING_MESSAGE); + return false; + } + //todo fix combobox selection +/* + for (int i = 0; i < comboBoxScenario.getItemCount(); i++) { + if (Objects.equals(String.valueOf(comboBoxScenario.getItemAt(i).value), guiSettings.getProperty("Scenario"))) { + comboBoxScenario.setSelectedItem(comboBoxScenario.getItemAt(i)); + break; } } +*/ + setGuiElementsVisibility(); + return true; } private void setCliOptions() { @@ -328,30 +308,31 @@ public static void startSwingGui() { new GuiMain(); } - public void startUpdaterGui() { - this.setContentPane(this.panelMain); - this.setTitle(ToolInfo.getToolAndVersion()); - this.setSize(1000, 800); - this.jLoggingPane.setFont(new Font(Font.MONOSPACED, PLAIN, 12)); - this.jLoggingPane.setBackground(DefaultBackgroundColor); - this.jLoggingPane.setForeground(DefaultForegroundColor); - this.setVisible(true); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - new Thread(this::loadKnxIpInterfacesAndFillComboBox).start(); - + private void startUpdaterGui() { + setContentPane(panelMain); + setTitle(ToolInfo.getToolAndVersion()); + jLoggingPane.setFont(new Font(Font.MONOSPACED, PLAIN, 12)); + jLoggingPane.setBackground(DefaultBackgroundColor); + jLoggingPane.setForeground(DefaultForegroundColor); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); TextAppender textAppender = ListTextAppenders.searchAppender(); if (textAppender != null) { textAppender.setTextPane(this.jLoggingPane); } - - initGuiElementsVisibility(); fillScenarios(); fillMediumComboBox(); fillTelegramPriorityComboBox(); - loadAllParameters(FILENAME_SETTINGS); + setMinimumSize(new Dimension(800, 600)); + if (!loadAllParameters(FILENAME_SETTINGS)) { + setLocationRelativeTo(null); + } + int windowExtendedState = getExtendedState(); + if ((windowExtendedState & Frame.ICONIFIED) == Frame.ICONIFIED) { + windowExtendedState &= ~Frame.ICONIFIED; + } + setExtendedState(windowExtendedState); mainScrollPane.getVerticalScrollBar().setUnitIncrement(10); @@ -361,6 +342,10 @@ public void windowClosing(WindowEvent e) { saveAllParameters(FILENAME_SETTINGS); } }); + setVisible(true); + if (textBoxKnxGatewayIpAddr.getText().isEmpty()) { + new Thread(this::loadKnxIpInterfacesAndFillComboBox).start(); + } } private void loadKnxIpInterfacesAndFillComboBox() { @@ -435,7 +420,7 @@ private void createUIComponents() { comboBoxMedium = new JComboBox<>(); comboBoxScenario = new JComboBox<>(); comboBoxKnxTelegramPriority = new JComboBox<>(); - this.setFrameImages(); + setFrameImages(); } public static class ComboItem { diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java new file mode 100644 index 00000000..ef9a825c --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java @@ -0,0 +1,129 @@ +package org.selfbus.updater.gui; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.*; +import java.awt.*; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +public class GuiSettings { + private static final Logger logger = LoggerFactory.getLogger(GuiSettings.class); + private final JFrame parent; + private static final ObjectMapper objectMapper = new ObjectMapper() + .enable(SerializationFeature.INDENT_OUTPUT) + .enable(SerializationFeature.WRAP_ROOT_VALUE); + + @SuppressWarnings("unused") + private GuiSettings() { + this.parent = null; + } + + public GuiSettings(JFrame parent) { + this.parent = parent; + logger.debug("parent: {} name: {}", this.parent.getClass().getName(), this.parent.getName()); + } + + public void readComponentsSettings(String fileName) throws IOException { + logger.debug("reading '{}'", fileName); + JsonNode rootNode = objectMapper.readTree(new File(fileName)); + readComponentData(parent, rootNode); + } + + public void writeComponentSettings(String fileName) throws IOException { + logger.debug("saving '{}'", fileName); + JsonNode rootNode = writeComponentsData(parent); + objectMapper.writeValue(new File(fileName), rootNode); + } + + private void readComponentData(Component component, JsonNode node) { + String nodeKey = getComponentSettingKey(component); + //todo this is a bad hack, because our GuiMain.MainFrame is saved with json key `ObjectNode` + // not sure why + if (component == parent) { + nodeKey = "ObjectNode"; + } + else { + nodeKey = getComponentSettingKey(component); + } + JsonNode componentNode = node.path(nodeKey); + + if (component instanceof JCheckBox checkBox) { + checkBox.setSelected(componentNode.path("selected").asBoolean()); + } + else if (component instanceof JTextField textField) { + textField.setText(componentNode.path("text").asText()); + } + else if (component instanceof JComboBox comboBox) { + String value = componentNode.path("selected_item").asText(null); + if (value != null) { + comboBox.setSelectedItem(value); + } + } + else if (component instanceof Container container) { + if (component instanceof JFrame frame) { + readFrame(frame, componentNode); + } + for (Component child : container.getComponents()) { + readComponentData(child, componentNode); + } + } + } + + private JsonNode writeComponentsData(Component component) { + ObjectNode node = objectMapper.createObjectNode(); + if (component instanceof JCheckBox checkBox) { + node.put("selected", checkBox.isSelected()); + } + else if (component instanceof JTextField textField) { + node.put("text", textField.getText()); + } + else if (component instanceof JComboBox comboBox) { + Object selection = comboBox.getSelectedItem(); + node.put("selected_item", selection != null ? selection.toString() : ""); + } + else if (component instanceof Container container) { + if (component instanceof JFrame frame) { + writeFrame(frame, node); + } + for (Component child : container.getComponents()) { + JsonNode childNode = writeComponentsData(child); + if (!childNode.isEmpty(null)) { + node.set(getComponentSettingKey(child), childNode); + } + } + } + return node; + } + + private String getComponentSettingKey(Component component) { + String key = component.getClass().getSimpleName(); + if (component.getName() != null) { + key += "." + component.getName(); + } + else { + key += ".null"; + } + return key; + } + + private void readFrame(JFrame frame, JsonNode node) { + frame.setSize(node.path("width").asInt(), node.path("height").asInt()); + frame.setLocation(node.path("x").asInt(), node.path("y").asInt()); + frame.setExtendedState(node.path("extended_state").asInt()); + } + + private void writeFrame(JFrame frame, ObjectNode node) { + node.put("width", frame.getWidth()); + node.put("height", frame.getHeight()); + node.put("x", frame.getX()); + node.put("y", frame.getY()); + node.put("extended_state", frame.getExtendedState()); + } +} \ No newline at end of file From 51eb905c8128b9263d356b990e4f659ae2b190b4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 02:17:37 +0200 Subject: [PATCH 162/359] [Updater] Throw exception not implemented for `rf` (radio frequency) medium --- .../updater/src/org/selfbus/updater/SBKNXLink.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index c2fd28d7..f6153a65 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -76,7 +76,8 @@ private static KNXMediumSettings getMedium(final String id, IndividualAddress ow if (id.equals("tp1")) { return new TPSettings(ownAddress); } else if (id.equals("rf")) { - return new RFSettings(null); + throw new KNXIllegalArgumentException("medium 'rf' not implemented"); + //return new RFSettings(ownAddress); } //else if (id.equals("tp0")) // return TPSettings.TP0; From 152837b58e779b7b639d3bc3e92794d150af39d9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 02:23:06 +0200 Subject: [PATCH 163/359] [GUI] Save/restore `comboBox` selection index Instead of saving/restoring the selection text, this makes save/restore language independent. --- .../src/org/selfbus/updater/gui/GuiMain.java | 22 ++++++------------- .../org/selfbus/updater/gui/GuiSettings.java | 10 ++++----- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 9bed6fe0..1e470776 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -207,27 +207,28 @@ public void actionPerformed(ActionEvent e) { private void setComponentNames() { setName("MainFrame"); - advancedSettingsCheckBox.setName("AdvancedSettings"); // 1 + comboBoxIpGateways.setName("comboBoxIpGateways"); + advancedSettingsCheckBox.setName("AdvancedSettings"); textBoxKnxGatewayIpAddr.setName("GatewayIpAddr"); textFieldPort.setName("GatewayPort"); natCheckBox.setName("UseNat"); - textFieldTpuart.setName("TpUart"); // 5 + textFieldTpuart.setName("TpUart"); textFieldSerial.setName("Serial"); comboBoxMedium.setName("Medium"); comboBoxScenario.setName("Scenario"); textFieldFileName.setName("FlashFilePath"); - textFieldUid.setName("Uid"); // 10 + textFieldUid.setName("Uid"); CheckBoxDiffFlash.setName("DiffFlash"); eraseCompleteFlashCheckBox.setName("EraseCompleteFlash"); noFlashCheckBox.setName("NoFlash"); textFieldBootloaderDeviceAddress.setName("BootloaderDeviceAddress"); - textFieldDeviceAddress.setName("DeviceAddress"); // 15 + textFieldDeviceAddress.setName("DeviceAddress"); textFieldOwnAddress.setName("OwnAddress"); textFieldDelay.setName("DelayMs"); comboBoxKnxTelegramPriority.setName("TelegramPriority"); textFieldKnxSecureUser.setName("SecureUser"); - textFieldKnxSecureUserPwd.setName("SecureUserPassword"); //20 - textFieldKnxSecureDevicePwd.setName("SecureDevicePassword"); //21 + textFieldKnxSecureUserPwd.setName("SecureUserPassword"); + textFieldKnxSecureDevicePwd.setName("SecureDevicePassword"); } @SuppressWarnings("SameParameterValue") @@ -252,15 +253,6 @@ private boolean loadAllParameters(String fileName) { this.getTranslation("Warning"), JOptionPane.WARNING_MESSAGE); return false; } - //todo fix combobox selection -/* - for (int i = 0; i < comboBoxScenario.getItemCount(); i++) { - if (Objects.equals(String.valueOf(comboBoxScenario.getItemAt(i).value), guiSettings.getProperty("Scenario"))) { - comboBoxScenario.setSelectedItem(comboBoxScenario.getItemAt(i)); - break; - } - } -*/ setGuiElementsVisibility(); return true; } diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java index ef9a825c..6f6bc4f7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java @@ -61,9 +61,9 @@ else if (component instanceof JTextField textField) { textField.setText(componentNode.path("text").asText()); } else if (component instanceof JComboBox comboBox) { - String value = componentNode.path("selected_item").asText(null); - if (value != null) { - comboBox.setSelectedItem(value); + int selectionIndex = componentNode.path("selectionIndex").asInt(-1); + if ((selectionIndex >= 0) && (selectionIndex < comboBox.getItemCount())) { + comboBox.setSelectedIndex(selectionIndex); } } else if (component instanceof Container container) { @@ -85,8 +85,8 @@ else if (component instanceof JTextField textField) { node.put("text", textField.getText()); } else if (component instanceof JComboBox comboBox) { - Object selection = comboBox.getSelectedItem(); - node.put("selected_item", selection != null ? selection.toString() : ""); + int selectionIndex = comboBox.getSelectedIndex(); + node.put("selectionIndex", selectionIndex); } else if (component instanceof Container container) { if (component instanceof JFrame frame) { From a7aa3c2351a4393e6fac82426850638590e236e9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 03:02:59 +0200 Subject: [PATCH 164/359] [GUI] Allow `--device` for uid requests --- .../src/org/selfbus/updater/gui/GuiMain.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 1e470776..c5af9e1e 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -163,8 +163,18 @@ private void handleLoadFileAction() { } private void handleRequestUidAction() { - updaterThread = new Thread(() -> { + String oldTextFieldFileName = textFieldFileName.getText(); + String oldTextFieldUid = textFieldUid.getText(); + textFieldFileName.setText(""); + textFieldUid.setText(""); + try { setCliOptions(); + } + finally { + textFieldFileName.setText(oldTextFieldFileName); + textFieldUid.setText(oldTextFieldUid); + } + updaterThread = new Thread(() -> { final Updater upd = new Updater(cliOptions); String uid = upd.requestUid(); SwingUtilities.invokeLater(() -> this.textFieldUid.setText(uid)); @@ -504,8 +514,8 @@ private void initGuiElementsVisibility() { GuiObjectsMap.put(labelTpuart, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID, GuiObjsVisOpts.ADVSET)); GuiObjectsMap.put(textFieldTpuart, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID, GuiObjsVisOpts.ADVSET)); - GuiObjectsMap.put(labelDeviceAddress, List.of(GuiObjsVisOpts.APPDEV)); - GuiObjectsMap.put(textFieldDeviceAddress, List.of(GuiObjsVisOpts.APPDEV)); + GuiObjectsMap.put(labelDeviceAddress, List.of(GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID)); + GuiObjectsMap.put(textFieldDeviceAddress, List.of(GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID)); GuiObjectsMap.put(labelBootloaderDeviceAddr, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID, GuiObjsVisOpts.ADVSET)); GuiObjectsMap.put(textFieldBootloaderDeviceAddress, Arrays.asList(GuiObjsVisOpts.NEWDEV, GuiObjsVisOpts.APPDEV, GuiObjsVisOpts.REQUID, GuiObjsVisOpts.ADVSET)); From aba81764bfd87d799b2897cd676ad53a00507374 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 04:28:00 +0200 Subject: [PATCH 165/359] [GUI] Fix interacting with `jLoggingPane` broke the progress info `getCursorPosition` used the current caret position, which could easily change by interacting with the `jLoggingPane`. --- .../updater/gui/ConColorsToStyledDoc.java | 25 ++++++++++++------- .../src/org/selfbus/updater/gui/GuiMain.java | 2 -- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index 5eb35996..ad6b11c7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -1,6 +1,8 @@ package org.selfbus.updater.gui; import org.selfbus.updater.AnsiCursor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.swing.*; import javax.swing.text.*; @@ -14,6 +16,7 @@ public final class ConColorsToStyledDoc { @SuppressWarnings("unused") private ConColorsToStyledDoc() {} // avoids instance creation + private static final Logger logger = LoggerFactory.getLogger(ConColorsToStyledDoc.class); private static final Style stringStyle = new javax.swing.text.StyleContext().addStyle("testStyle", null); public static final java.awt.Color DefaultForegroundColor = java.awt.Color.white; @@ -400,15 +403,16 @@ private static Style colorCodeToStyle(String [] colorCodes) { * number and the y-coordinate is the row number, both 1-based */ private static Point getCursorPosition(JTextPane textPane) { - int caretPosition = textPane.getCaretPosition(); - // Get the row at the caret position - Element docRoot = textPane.getStyledDocument().getDefaultRootElement(); - int row = docRoot.getElementIndex(caretPosition); + StyledDocument doc = textPane.getStyledDocument(); + int docLength = doc.getLength(); + // Get the row at the end of the document + Element docRoot = doc.getDefaultRootElement(); + int row = docRoot.getElementIndex(docLength); // Get the start offset of the column Element lineElement = docRoot.getElement(row); int lineStartOffset = lineElement.getStartOffset(); - int column = caretPosition - lineStartOffset; + int column = docLength - lineStartOffset; return new Point(column + 1, row + 1); // 1-based indexing for ANSI console } @@ -431,23 +435,26 @@ public static void setCursorPosition(JTextPane textPane, Point newPosition) thro // Calculate the new caret position int newCaretPosition = lineStartOffset + columnIndex; + logger.debug("lineStartOffset={}", lineStartOffset); + logger.debug("newCaretPosition={}", newCaretPosition); if (newCaretPosition < 0) { + logger.debug("newCaretPosition set to 0"); newCaretPosition = 0; } if (newCaretPosition > doc.getLength()) { newCaretPosition = doc.getLength(); + logger.debug("newCaretPosition set to {}", newCaretPosition); } - StyledDocument document = (StyledDocument) textPane.getDocument(); - int currentLength = document.getLength(); + int currentLength = doc.getLength(); // Set the caret position if (newCaretPosition < currentLength) { - document.remove(newCaretPosition, currentLength - newCaretPosition); + doc.remove(newCaretPosition, currentLength - newCaretPosition); } - textPane.setCaretPosition(document.getLength()); + textPane.setCaretPosition(doc.getLength()); } /** diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index c5af9e1e..fa817a48 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -115,7 +115,6 @@ private void handleStartStopFlashAction() { } jLoggingPane.setText(""); - jLoggingPane.setFocusable(false); // Needed for the SpinningCursor to work properly updaterThread = new Thread(() -> { setCliOptions(); final Updater updater = new Updater(cliOptions); @@ -128,7 +127,6 @@ private void handleStartStopFlashAction() { private void updaterFinished() { buttonStartStopFlash.setText(getTranslation("startFlash")); - jLoggingPane.setFocusable(true); } private void handleLoadFileAction() { From 6380cb81423cf8f6a8092aac47dedcbaf71555f8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 04:29:31 +0200 Subject: [PATCH 166/359] [Updater] Cleanup imports, delete duplicate code, add todo --- .../updater/src/org/selfbus/updater/SBKNXLink.java | 1 - .../updater/src/org/selfbus/updater/gui/GuiMain.java | 2 +- .../updater/src/org/selfbus/updater/gui/GuiSettings.java | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index f6153a65..cf55a053 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -13,7 +13,6 @@ import tuwien.auto.calimero.link.KNXNetworkLinkTpuart; import tuwien.auto.calimero.link.KNXNetworkLinkUsb; import tuwien.auto.calimero.link.medium.KNXMediumSettings; -import tuwien.auto.calimero.link.medium.RFSettings; import tuwien.auto.calimero.link.medium.TPSettings; import java.net.InetAddress; diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index fa817a48..999dc281 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -119,7 +119,7 @@ private void handleStartStopFlashAction() { setCliOptions(); final Updater updater = new Updater(cliOptions); updater.run(); - SwingUtilities.invokeLater(this::updaterFinished); + SwingUtilities.invokeLater(this::updaterFinished); //todo implement callback in Updater when done }); updaterThread.start(); buttonStartStopFlash.setText(getTranslation("stopFlash")); diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java index 6f6bc4f7..05b9c0e7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java @@ -10,7 +10,6 @@ import javax.swing.*; import java.awt.*; import java.io.File; -import java.io.FileWriter; import java.io.IOException; public class GuiSettings { @@ -43,7 +42,7 @@ public void writeComponentSettings(String fileName) throws IOException { } private void readComponentData(Component component, JsonNode node) { - String nodeKey = getComponentSettingKey(component); + String nodeKey; //todo this is a bad hack, because our GuiMain.MainFrame is saved with json key `ObjectNode` // not sure why if (component == parent) { From 53b565a1a7afebe821f2f407dd674e0eb30fdb5a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 04:40:12 +0200 Subject: [PATCH 167/359] [Updater] Use try-with-resources --- .../updater/src/org/selfbus/updater/SBKNXLink.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index cf55a053..5d86261f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -7,6 +7,7 @@ import tuwien.auto.calimero.KNXIllegalArgumentException; import tuwien.auto.calimero.knxnetip.KNXnetIPRouting; import tuwien.auto.calimero.knxnetip.SecureConnection; +import tuwien.auto.calimero.knxnetip.TcpConnection; import tuwien.auto.calimero.link.KNXNetworkLink; import tuwien.auto.calimero.link.KNXNetworkLinkFT12; import tuwien.auto.calimero.link.KNXNetworkLinkIP; @@ -38,7 +39,10 @@ private KNXNetworkLink createSecureTunnelingLink(InetSocketAddress local, InetSo logger.info("Connect using KNX IP Secure tunneling"); byte[] deviceAuthCode = SecureConnection.hashDeviceAuthenticationPassword(cliOptions.getKnxSecureDevicePassword().toCharArray()); byte[] userKey = SecureConnection.hashUserPassword(cliOptions.getKnxSecureUserPassword().toCharArray()); - final var session = Utils.tcpConnection(local, remote).newSecureSession(cliOptions.getKnxSecureUserId(), userKey, deviceAuthCode); + final TcpConnection.SecureSession session; + try (TcpConnection tcpConnection = Utils.tcpConnection(local, remote)) { + session = tcpConnection.newSecureSession(cliOptions.getKnxSecureUserId(), userKey, deviceAuthCode); + } return KNXNetworkLinkIP.newSecureTunnelingLink(session, medium); } From 3e532a1fdc3bddbb4d2a0dcfffd50fea23fc7a78 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 04:58:25 +0200 Subject: [PATCH 168/359] [GUI] Fix warning `No label specified` --- .../src/org/selfbus/updater/gui/GuiMain.form | 17 ++++++++++++++++ .../src/org/selfbus/updater/gui/GuiMain.java | 20 +++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form index 5c1d3def..da8e16a3 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.form @@ -61,6 +61,7 @@ +
@@ -75,6 +76,7 @@ + @@ -107,6 +109,7 @@ + @@ -139,6 +142,7 @@ + @@ -157,6 +161,7 @@ + @@ -165,6 +170,7 @@ + @@ -190,6 +196,7 @@ + @@ -222,6 +229,7 @@ + @@ -254,6 +262,7 @@ + @@ -327,6 +336,7 @@ + @@ -335,6 +345,7 @@ + @@ -343,6 +354,7 @@ + @@ -367,6 +379,7 @@ + @@ -381,6 +394,7 @@ + @@ -389,6 +403,7 @@ + @@ -413,6 +428,7 @@ + @@ -437,6 +453,7 @@ + diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 999dc281..a341511e 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -167,8 +167,7 @@ private void handleRequestUidAction() { textFieldUid.setText(""); try { setCliOptions(); - } - finally { + } finally { textFieldFileName.setText(oldTextFieldFileName); textFieldUid.setText(oldTextFieldUid); } @@ -769,6 +768,23 @@ private void setGuiElementsVisibility() { reloadGatewaysButton = new JButton(); this.$$$loadButtonText$$$(reloadGatewaysButton, this.$$$getMessageFromBundle$$$("language/GuiMain", "reloadKnxIpGateways")); panel1.add(reloadGatewaysButton, new GridConstraints(1, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + labelIpGateway.setLabelFor(comboBoxIpGateways); + label2.setLabelFor(textBoxKnxGatewayIpAddr); + label3.setLabelFor(textFieldPort); + labelTpuart.setLabelFor(textFieldTpuart); + labelSerial.setLabelFor(textFieldSerial); + labelMedium.setLabelFor(comboBoxMedium); + label5.setLabelFor(comboBoxScenario); + labelFileName.setLabelFor(textFieldFileName); + labelUid.setLabelFor(textFieldUid); + labelBootloaderDeviceAddr.setLabelFor(textFieldBootloaderDeviceAddress); + labelDeviceAddress.setLabelFor(textFieldDeviceAddress); + labelOwnAddress.setLabelFor(textFieldOwnAddress); + labelDelay.setLabelFor(textFieldDelay); + labelTelegramPriority.setLabelFor(comboBoxKnxTelegramPriority); + labelKnxSecureUser.setLabelFor(textFieldKnxSecureUser); + labelKnxSecureUserPwd.setLabelFor(textFieldKnxSecureUserPwd); + labelKnxSecureDevicePwd.setLabelFor(textFieldKnxSecureDevicePwd); } /** From 206ab395bc28d704cba91548aba67635af66c3ae Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 05:15:43 +0200 Subject: [PATCH 169/359] [GUI] Fix some shortcut warnings --- .../src/resources/language/GuiMain.properties | 36 +++++++++---------- .../resources/language/GuiMain_de.properties | 36 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/firmware_updater/updater/src/resources/language/GuiMain.properties b/firmware_updater/updater/src/resources/language/GuiMain.properties index 7c653fe6..6bc4a294 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain.properties @@ -6,17 +6,17 @@ uid=UID startFlash=start flash newDevice=new device appDevice=device with application -loadFile=load file -medium=medium +loadFile=load &file +medium=&medium serial=ft12 -tpuart=TPUART -knxDeviceAddr=device address -knxProgDeviceAddr=bootloader device address -knxOwnAddress=own KNX address -port=port -useNat=NAT -knxMessageDelay=delay [ms] -eraseFlash=erase complete flash +tpuart=&TPUART +knxDeviceAddr=d&evice address +knxProgDeviceAddr=&bootloader device address +knxOwnAddress=own KNX addres&s +port=p&ort +useNat=&NAT +knxMessageDelay=&delay [ms] +eraseFlash=erase &complete flash noFlash=no flash newDeviceHint=There is a new device with flashed bootloader\ @@ -32,32 +32,32 @@ eraseCompleteFlashHint=USE WITH CAUTION! Erases the complete flash m including the physical KNX address and all settings of
\ the device. Only the bootloader is not deleted. stopFlash=stop flashing -requestUid=request UID from device +requestUid=request U&ID from device requestUidHint=The UID can be requested from a Selfbus device.\ KnxGatewayConnectionSettings=KNX gateway connection settings KnxBusSettings=KNX bus settings UpdaterSettings=Updater settings -advancedSettings=Advanced settings -messagePriority=KNX telegram priority -knxSecureUser=KNX IP-Secure User +advancedSettings=&Advanced settings +messagePriority=KN&X telegram priority +knxSecureUser=KNX IP-Secure &User knxSecureUserHint=KNX IP Secure tunneling user identifier
\ (1..127) (default 1) -knxSecureUserPwd=User password +knxSecureUserPwd=User &password knxSecureUserPwdHint=\ KNX IP Secure tunneling user password
\ (Commissioning password),quotation marks
\ (") in password may not work\ -knxSecureDevicePwd=Device password +knxSecureDevicePwd=Device pass&word knxSecureDevicePwdHint=\ KNX IP Secure device authentication code
\ (Authentication Code) quotation marks(")
\ in password may not work\ -reloadKnxIpGateways=reload gateways +reloadKnxIpGateways=&reload gateways selectInterface=select Interface -diffFlash=differential Flash +diffFlash=differential F&lash diffFlashHint=flash only changed parts of software
\\ [experimental] logMessageCanceledFlashing=Flash process canceled. labelScenarioHint=Label diff --git a/firmware_updater/updater/src/resources/language/GuiMain_de.properties b/firmware_updater/updater/src/resources/language/GuiMain_de.properties index 3cfd57e6..5abfeec4 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain_de.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain_de.properties @@ -1,4 +1,4 @@ -loadFile=lade Firmware-Datei +loadFile=lade &Firmware-Datei fileName=Dateiname selectKnxIpGateway=KNX IP Schnittstelle ipAddress=IP-Adresse @@ -22,27 +22,27 @@ fileNameHint=Der Dateipfad kann manuell bearbeitet werden
\ ipAddrHint=IP-Adresse der KNX Schnittstelle portHint=TCP/UDP Port der KNX Schnittstelle (default 3671) stopFlash=Stoppe Flash Vorgang -medium=Medium +medium=&Medium serial=ft12 -tpuart=TPUART -knxDeviceAddr=Geräteadresse -knxProgDeviceAddr=Geräteadresse im Bootloader -knxOwnAddress=eigene KNX Adresse -port=Port -useNat=NAT -knxMessageDelay=Verzögerung [ms] -eraseFlash=Lösche kompletten Flash +tpuart=&TPUART +knxDeviceAddr=&Geräteadresse +knxProgDeviceAddr=Geräteadresse im &Bootloader +knxOwnAddress=eigene KNX Adres&se +port=P&ort +useNat=&NAT +knxMessageDelay=&Verzögerung [ms] +eraseFlash=Lösche &kompletten Flash noFlash=keine Daten übertragen -requestUid=Erfasse UID von Gerät +requestUid=Erfasse U&ID von Gerät requestUidHint=Die UID wird von einem Selfbus Gerät abgefragt.\ UpdaterSettings=Updater KnxBusSettings=KNX Bus KnxGatewayConnectionSettings=KNX Schnittstelle -advancedSettings=Erweiterte Einstellungen -knxSecureUserPwd=Benutzer Passwort -messagePriority=KNX Telegramm Priorität -knxSecureUser=KNX IP-Secure Benutzer +advancedSettings=&Erweiterte Einstellungen +knxSecureUserPwd=Benutzer &Passwort +messagePriority=KN&X Telegramm Priorität +knxSecureUser=KNX IP-Secure &Benutzer knxSecureUserHint=KNX IP Secure tunneling Benutzer Identifikator
\ (1..127) (default 1) knxSecureUserPwdHint=\ @@ -50,15 +50,15 @@ KNX IP Secure tunneling Benutzer Passwort
\ (Inbetriebnahmepasswort), Anführungszeichen
\ (") im Passwort könnten nicht funktionieren\ -knxSecureDevicePwd=Gerätepasswort +knxSecureDevicePwd=Gerätepass&wort knxSecureDevicePwdHint=\ KNX IP Secure Geräte Authentifizierung Code
\ (Authentifizierungscode), Anführungszeichen
\ (") im Passwort könnten nicht funktionieren\ -reloadKnxIpGateways=Gateways erneut laden +reloadKnxIpGateways=Gateways e&rneut laden selectInterface=wähle Schnittstelle -diffFlash=differenzieller Flash Vorgang +diffFlash=differenzieller F&lash Vorgang diffFlashHint=Es werden nur veränderte Teile der Software
\ übertragen [experimentell] logMessageCanceledFlashing=Flash Vorgang wurde abgebrochen. From d1c12a76bc38eba0912be80d40f51506dd985858 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 19 Aug 2024 17:20:15 +0200 Subject: [PATCH 170/359] [Updater] More readability by using old `ConColors` alike logging --- .../src/org/selfbus/updater/BinImage.java | 6 +- .../src/org/selfbus/updater/CliOptions.java | 62 +++++++++------- .../org/selfbus/updater/DeviceManagement.java | 32 ++++---- .../org/selfbus/updater/FlashDiffMode.java | 2 +- .../org/selfbus/updater/FlashFullMode.java | 4 +- .../src/org/selfbus/updater/ProgressInfo.java | 2 +- .../src/org/selfbus/updater/SBKNXLink.java | 7 +- .../src/org/selfbus/updater/Updater.java | 73 +++++++++++-------- .../updater/bootloader/BootDescriptor.java | 4 +- .../bootloader/BootloaderStatistic.java | 4 +- .../updater/mode/differential/FlashDiff.java | 2 +- 11 files changed, 114 insertions(+), 84 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/BinImage.java b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java index 4a558d29..fed2f7f8 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java @@ -129,8 +129,8 @@ public final void writeToBinFile(String fileName) { File binFile = new File(fileName); if (!binFile.getParentFile().exists()) { if (!binFile.getParentFile().mkdirs()) { - logger.warn(ansi().fg(RED).a("Could not create bin-file directory {}").reset().toString(), - binFile.getParentFile().toString()); + logger.warn("{}Could not create bin-file directory {}{}", ansi().fg(RED), + binFile.getParentFile().toString(), ansi().reset()); } return; } @@ -138,7 +138,7 @@ public final void writeToBinFile(String fileName) { try (FileOutputStream fos = new FileOutputStream(binFile)) { fos.write(binData); } catch (IOException e) { - logger.warn(ansi().fg(RED).a("Could not write bin-file {}").reset().toString(), binFile.getPath()); + logger.warn("{}Could not write bin-file {}{}", ansi().fg(RED), binFile.getPath(), ansi().reset()); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index b984683c..bd92ec60 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -368,7 +368,8 @@ private void parse(final String[] args) { setLogLevel(Level.toLevel(cliLogLevel)); } else { - logger.warn(ansi().fg(RED).a("invalid {} {}, using {}").reset().toString(), OPT_LONG_LOGLEVEL, cliLogLevel, getLogLevel().toString()); + logger.warn("{}invalid {} {}, using {}{}", ansi().fg(RED), OPT_LONG_LOGLEVEL, cliLogLevel, + getLogLevel().toString(), ansi().reset()); } } logger.debug("logLevel={}", getLogLevel()); @@ -378,8 +379,8 @@ private void parse(final String[] args) { setPriority(Priority.get(cmdLine.getOptionValue(OPT_LONG_PRIORITY))); //todo simplify } catch (KNXIllegalArgumentException e) { - logger.warn(ansi().fg(RED).a("invalid --{} {}, using {}").reset().toString(), - OPT_LONG_PRIORITY, cmdLine.getOptionValue(OPT_LONG_PRIORITY), getPriority()); + logger.warn("{}invalid --{} {}, using {}{}",ansi().fg(RED), OPT_LONG_PRIORITY, + cmdLine.getOptionValue(OPT_LONG_PRIORITY), getPriority(), ansi().reset()); } } logger.debug("priority={}", getPriority().toString()); @@ -459,8 +460,8 @@ private void parse(final String[] args) { setBlockSize(newBlockSize); } else { - logger.info(ansi().fg(YELLOW).a("--{} {} is not supported => Set --{} to default {} bytes").reset().toString(), - OPT_LONG_BLOCKSIZE, newBlockSize, OPT_LONG_BLOCKSIZE, getBlockSize()); + logger.info("{}--{} {} is not supported => Set --{} to default {} bytes{}", ansi().fg(YELLOW), + OPT_LONG_BLOCKSIZE, newBlockSize, OPT_LONG_BLOCKSIZE, getBlockSize(), ansi().reset()); } } logger.debug("{}={}", OPT_LONG_BLOCKSIZE, getBlockSize()); @@ -532,49 +533,58 @@ private void parse(final String[] args) { // differential mode and eraseflash makes no sense if (getEraseFullFlashIsSet() && (!getFlashingFullModeIsSet())) { setFlashingFullModeIsSet(true); - logger.info(ansi().fg(RED).a("--{} is set. --> switching to full flash mode").reset().toString(), OPT_LONG_ERASEFLASH); + logger.info("{}--{} is set. --> switching to full flash mode{}", ansi().fg(RED), + OPT_LONG_ERASEFLASH, ansi().reset()); } // nat only possible with tunneling v1 if (getNatIsSet() && (!getTunnelingV1isSet())) { - throw new CliInvalidException(String.format(ansi().fg(RED).a("Option --%s can only be used together with --%s").reset().toString(), - OPT_LONG_NAT, OPT_LONG_TUNNEL_V1)); + throw new CliInvalidException(String.format("%sOption --%s can only be used together with --%s%s", + ansi().fg(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ansi().reset())); } // nat not allowed with tunneling v2 if (getNatIsSet() && (getTunnelingV2isSet())) { - throw new CliInvalidException(String.format(ansi().fg(RED).a("Option --%s can not be used together with --%s").reset().toString(), - OPT_LONG_NAT, OPT_LONG_TUNNEL_V2)); + throw new CliInvalidException(String.format("%sOption --%s can not be used together with --%s%s", + ansi().fg(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2, ansi().reset())); } // check IP-secure configuration if (!(getKnxSecureUserPassword().isEmpty()) || !(getKnxSecureDevicePassword().isEmpty())) { if (getKnxInterface().isEmpty()) { - throw new CliInvalidException(ansi().fg(RED).a("No IP-Interface specified for IP-secure").reset().toString()); + throw new CliInvalidException(String.format("%sNo IP-Interface specified for IP-secure%s", + ansi().fg(RED), ansi().reset())); } else if (!getUsbVendorIdAndProductId().isEmpty()) { - throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_USB)).reset().toString()); + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_USB, ansi().reset())); } else if (!getFt12SerialPort().isEmpty()) { - throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_FT12)).reset().toString()); + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_FT12, ansi().reset())); } else if (!getTpuartSerialPort().isEmpty()) { - throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TPUART)).reset().toString()); + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_TPUART, ansi().reset())); } else if (getNatIsSet()) { - throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_NAT)).reset().toString()); + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_NAT, ansi().reset())); } else if (getTunnelingV1isSet()) { - throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TUNNEL_V1)).reset().toString()); + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_TUNNEL_V1, ansi().reset())); } else if (getTunnelingV2isSet()) { - throw new CliInvalidException(ansi().fg(RED).a(String.format("IP-secure is not possible with --%s", OPT_LONG_TUNNEL_V2)).reset().toString()); + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_TUNNEL_V2, ansi().reset())); } // ensure that all three IP-Secure arguments are set if ((getKnxSecureUserPassword().isEmpty()) || (getKnxSecureDevicePassword().isEmpty())) { - throw new CliInvalidException(ansi().fg(RED).a(String.format("For IP-secure --%s, --%s and --%s must be set", OPT_LONG_USER_ID, - OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD)).reset().toString()); + throw new CliInvalidException(String.format("%sFor IP-secure --%s, --%s and --%s must be set%s", + ansi().fg(RED), OPT_LONG_USER_ID, OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD, + ansi().reset())); } } @@ -586,10 +596,12 @@ else if (getTunnelingV2isSet()) { if (!getUsbVendorIdAndProductId().isEmpty()) interfacesSet++; if (interfacesSet > 1) { - throw new CliInvalidException(ansi().fg(RED).a("Only one bus interface can be used.").reset().toString()); + throw new CliInvalidException(String.format("%sOnly one bus interface can be used.%s", + ansi().fg(RED), ansi().reset())); } else if (interfacesSet == 0) { - throw new CliInvalidException(ansi().fg(RED).a("No bus interface specified.").reset().toString()); + throw new CliInvalidException(String.format("%sNo bus interface specified.%s", + ansi().fg(RED), ansi().reset())); } } catch (CliInvalidException e) { @@ -607,7 +619,7 @@ private void logCliException(Exception e, String[] args, boolean verbose) { System.out.printf("Invalid command line parameters: '%s'%s", cliParsed, System.lineSeparator()); // don't log possible IP-secure parameters logger.debug("Invalid command line parameters:"); - logger.error(ansi().fg(RED).a(e.getMessage()).reset().toString()); + logger.error("{}{}{}", ansi().fg(RED), e.getMessage(), ansi().reset()); logger.error("For more information about the usage start with --{}", OPT_LONG_HELP); if (verbose) { logger.error("", e); @@ -776,9 +788,9 @@ public int getDelayMs() { private void setDelayMs(int delayMs) { if ((delayMs < Updater.DELAY_MIN) || (delayMs > Updater.DELAY_MAX)) { - logger.warn(ansi().fg(RED).a( - String.format("option --%s {} is invalid (min:{}, max:{}) => set to {}", OPT_LONG_DELAY)).reset().toString(), - delayMs, Updater.DELAY_MIN, Updater.DELAY_MAX, Updater.DELAY_DEFAULT); + logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", + ansi().fg(RED), OPT_LONG_DELAY, delayMs, Updater.DELAY_MIN, + Updater.DELAY_MAX, Updater.DELAY_DEFAULT, ansi().reset()); delayMs = Updater.DELAY_DEFAULT; // set to DELAY_DEFAULT in case of invalid waiting time } this.delayMs = delayMs; diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index fc897eaf..6e56843c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -82,7 +82,7 @@ private void waitRestartTime(int restartTimeSeconds) { try { while (restartTimeSeconds > 0) { Thread.sleep(1000); - System.out.printf(ansi().fgBright(GREEN).a(".").reset().toString()); + System.out.printf("%s.%s", ansi().fgBright(GREEN), ansi().reset()); restartTimeSeconds--; } System.out.println(); @@ -102,12 +102,15 @@ public void restartDeviceToBootloader(IndividualAddress device) { try (Destination dest = this.mc.createDestination(device, true, false, false)) { logger.info("Restarting device {} into bootloader", device); restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); - logger.info("Device {} reported {} second(s) for restarting", device, ansi().fgBright(GREEN).a(restartProcessTime).reset().toString()); + logger.info("Device {} reported {}{}{} second(s) for restarting", + device, ansi().fgBright(GREEN), restartProcessTime, ansi().reset()); waitRestartTime(restartProcessTime); } catch (final KNXException | InterruptedException e) { - logger.info(ansi().fgBright(RED).a("Restart state of device {} unknown. {}").reset().toString(), device, e.getMessage()); + logger.info("{}Restart state of device {} unknown. {}{}", + ansi().fgBright(RED), device, e.getMessage(), ansi().reset()); logger.debug("KNXException ", e); - logger.info("Waiting {} seconds for device {} to restart", ansi().fgBright(GREEN).a(restartProcessTime).reset().toString(), device); + logger.info("Waiting {}{}{} seconds for device {} to restart", + ansi().fgBright(GREEN), restartProcessTime, ansi().reset(), device); waitRestartTime(restartProcessTime); } } @@ -151,23 +154,23 @@ public BootloaderIdentity requestBootloaderIdentity() if (command == UPDCommand.RESPONSE_BL_VERSION_MISMATCH) { long minMajorVersion = result[DATA_POSITION] & 0xff; long minMinorVersion = result[DATA_POSITION + 1] & 0xff; - logger.error(ansi().fg(RED).a("Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.").reset().toString(), - ToolInfo.getVersion(), minMajorVersion, minMinorVersion); + logger.error("{}Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.{}", + ansi().fg(RED), ToolInfo.getVersion(), minMajorVersion, minMinorVersion, ansi().reset()); } restartProgrammingDevice(); throw new UpdaterException("Requesting Bootloader Identity failed!"); } BootloaderIdentity bl = BootloaderIdentity.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info("Device Bootloader: {}", ansi().fgBright(YELLOW).a(bl).reset().toString()); + logger.info("Device Bootloader: {}{}{}", ansi().fgBright(YELLOW), bl, ansi().reset()); boolean versionsMatch = (bl.versionMajor() > ToolInfo.minMajorVersionBootloader()) || ((bl.versionMajor() == ToolInfo.minMajorVersionBootloader()) && (bl.versionMinor() >= ToolInfo.minMinorVersionBootloader())); if (!versionsMatch) { - logger.error(ansi().fg(RED).a("Bootloader version {} is not compatible, please update Bootloader to version {} or higher").reset().toString(), - bl.getVersion(), ToolInfo.minVersionBootloader()); + logger.error("{}Bootloader version {} is not compatible, please update Bootloader to version {} or higher{}", + ansi().fg(RED), bl.getVersion(), ToolInfo.minVersionBootloader(), ansi().reset()); throw new UpdaterException("Bootloader version not compatible!"); } return bl; @@ -307,7 +310,7 @@ public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { // console output String logText = String.format("%s%s%s%s", AnsiCursor.off(), - ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset().toString(), + ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset(), progressInfo, AnsiCursor.on()); System.out.print(logText); @@ -381,11 +384,13 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr return result; } catch (KNXTimeoutException e) { - logger.warn(ansi().fg(RED).a("{} {} : {}").reset().toString(), command, e.getMessage(), e.getClass().getSimpleName()); + logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), + e.getClass().getSimpleName(), ansi().reset()); result.incTimeoutCount(); } catch (KNXDisconnectException | KNXRemoteException e) { ///\todo check causes of KNXRemoteException, if think they are unrecoverable - logger.warn(ansi().fg(RED).a("{} {} : {}").reset().toString(), command, e.getMessage(), e.getClass().getSimpleName()); + logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), + e.getClass().getSimpleName(), ansi().reset()); result.incDropCount(); } catch (Throwable e) { @@ -415,7 +420,8 @@ public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throw else if ((devices.length == 1) && (progDeviceAddr != null) && (progDeviceAddr.equals(devices[0]))) { // correct device in prog mode return; } - logger.warn(ansi().fgBright(RED).a("{} Device(s) in bootloader/programming mode: {}").reset().toString(), devices.length, Arrays.stream(devices).toArray()); + logger.warn("{}{} Device(s) in bootloader/programming mode: {}{}", + ansi().fgBright(RED), devices.length, Arrays.stream(devices).toArray(), ansi().reset()); if (devices.length == 0) { throw new UpdaterException("No device in programming mode."); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index 5badd32a..46d7a96f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -63,7 +63,7 @@ public static boolean setupDifferentialMode(BootDescriptor bootDescriptor) { } else { logger.warn("Current device firmware not found in cache {}", Utils.shortenPath(oldFileName, DEFAULT_DISPLAYED_PATH_DEPTH)); - logger.warn(ansi().fgBright(RED).a(" --> switching to FULL upload mode").reset().toString()); + logger.warn("{} --> switching to FULL upload mode{}", ansi().fgBright(RED), ansi().reset()); initialized = false; } return initialized; diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index 080ebb6d..cdfbf85d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -89,8 +89,8 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa progressInfo.update(-txBuffer.length); } else if (result == UDPResult.IAP_COMPARE_ERROR) { - throw new UpdaterException(String.format("ProgramData update failed. %s", - String.format(ansi().fg(RED).a("Try again with option '--%s 256'").reset().toString(), CliOptions.OPT_LONG_BLOCKSIZE))); + throw new UpdaterException(String.format("ProgramData update failed. %sTry again with option '--%s 256'%s", + ansi().fg(RED), CliOptions.OPT_LONG_BLOCKSIZE, ansi().reset())); } else if (result == UDPResult.IAP_SUCCESS) { progAddress += txBuffer.length; diff --git a/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java index 4e126ef5..e3719ba8 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java @@ -65,7 +65,7 @@ private String getSpeed(boolean averageSpeed) { else { color = YELLOW; } - return ansi().fgBright(color).a(String.format("%5.1f", bytesPerSecond)).reset().toString(); + return String.format("%s%5.1f%s", ansi().fgBright(color), bytesPerSecond, ansi().reset()); } public String toString() { diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index 5d86261f..51d11fcd 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -55,12 +55,13 @@ private KNXNetworkLink createTunnelingLinkV2(InetSocketAddress local, InetSocket private KNXNetworkLink createTunnelingLinkV1(InetSocketAddress local, InetSocketAddress remote, boolean useNat, KNXMediumSettings medium) throws KNXException, InterruptedException { - logger.info(ansi().fg(YELLOW).a("Connect using UDP tunneling v1 (nat:{})").reset().toString(), useNat); + logger.info("{}Connect using UDP tunneling v1 (nat:{}){}", ansi().fg(YELLOW), useNat, ansi().reset()); return KNXNetworkLinkIP.newTunnelingLink(local, remote, useNat, medium); } private KNXNetworkLink createRoutingLink(InetSocketAddress local, KNXMediumSettings medium) throws KNXException { - logger.info(ansi().fg(YELLOW).a("Connect using routing (multicast:{})").reset().toString(), KNXnetIPRouting.DefaultMulticast); + logger.info("{}Connect using routing (multicast:{}){}", + ansi().fg(YELLOW), KNXnetIPRouting.DefaultMulticast, ansi().reset()); return KNXNetworkLinkIP.newRoutingLink(local.getAddress(), KNXnetIPRouting.DefaultMulticast, medium); } @@ -167,7 +168,7 @@ private KNXNetworkLink doOpenLink() throws KNXException, try { return createTunnelingLinkV1(local, remote, cliOptions.getNatIsSet(), medium); } catch (final KNXException | InterruptedException e) { - logger.info(ansi().fg(YELLOW).a("failed with {}").reset().toString(), e.toString()); + logger.info("{}failed with {}{}", ansi().fg(YELLOW), e, ansi().reset()); } // last chance try unsecure UDP tunneling v1 connection with INVERTED nat option set on cli diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 0679af68..bdb6a911 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -75,14 +75,14 @@ public static void main(final String[] args) { CliOptions options = new CliOptions(args, String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()) , "Selfbus KNX-Firmware update tool options", "", PHYS_ADDRESS_BOOTLOADER, PHYS_ADDRESS_OWN); if (options.getVersionIsSet()) { - logger.info(ansi().fgBright(GREEN).bold().a(Credits.getAsciiLogo()).reset().toString()); - logger.info(ansi().fgBright(GREEN).bold().a(Credits.getAuthors()).reset().toString()); + logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), Credits.getAsciiLogo(), ansi().reset()); + logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), Credits.getAuthors(), ansi().reset()); ToolInfo.showVersion(); finalizeJansi(); return; } - logger.info(ansi().fgBright(GREEN).bold().a(ToolInfo.getToolAndVersion()).reset().toString()); + logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), ToolInfo.getToolAndVersion(), ansi().reset()); if (options.getHelpIsSet()) { logger.info(options.helpToString()); finalizeJansi(); @@ -171,7 +171,7 @@ public void run() { if (!hexFileName.isEmpty()) { // check if the firmware file exists if (!Utils.fileExists(hexFileName)) { - logger.error(ansi().fg(RED).a("File '{}' does not exist!").reset().toString(), cliOptions.getFileName()); + logger.error("{}File '{}' does not exist!{}", ansi().fg(RED), cliOptions.getFileName(), ansi().reset()); throw new UpdaterException("Selfbus update failed."); } // Load Firmware hex file @@ -179,15 +179,15 @@ public void run() { newFirmware = BinImage.readFromHex(hexFileName); // Check for APP_VERSION string in new firmware if (newFirmware.getAppVersion().isEmpty()) { - logger.warn(ansi().fgBright(RED).a(" Missing APP_VERSION string in new firmware!").reset().toString()); + logger.warn("{} Missing APP_VERSION string in new firmware!{}", ansi().fgBright(RED), ansi().reset()); throw new UpdaterException("Missing APP_VERSION string in firmware!"); } } else { System.out.println(); - logger.info(ansi().bg(RED).fg(BLACK).a("No firmware file (*.hex) specified! Specify with --{}").reset().toString(), - CliOptions.OPT_LONG_FILENAME); - logger.info(ansi().fgBright(YELLOW).a("Reading only device information").reset().toString()); + logger.info("{}No firmware file (*.hex) specified! Specify with --{}{}", + ansi().bg(RED).fg(BLACK), CliOptions.OPT_LONG_FILENAME, ansi().reset()); + logger.info("{}Reading only device information{}", ansi().fgBright(YELLOW), ansi().reset()); System.out.println(); } @@ -212,8 +212,10 @@ public void run() { dm.unlockDeviceWithUID(uid); if ((cliOptions.getDumpFlashStartAddress() >= 0) && (cliOptions.getDumpFlashEndAddress() >= 0)) { - logger.warn(ansi().fgBright(GREEN).a("Dumping flash content range 0x{}-0x{} to bootloader's serial port.").reset().toString(), - String.format("%04X", cliOptions.getDumpFlashStartAddress()), String.format("%04X", cliOptions.getDumpFlashEndAddress())); + logger.warn("{}Dumping flash content range {} to bootloader's serial port.{}", + ansi().fgBright(GREEN), + String.format("0x%04X-0x%04X", cliOptions.getDumpFlashStartAddress(), cliOptions.getDumpFlashEndAddress()), + ansi().reset()); dm.dumpFlashRange(cliOptions.getDumpFlashStartAddress(), cliOptions.getDumpFlashEndAddress()); return; } @@ -249,24 +251,28 @@ public void run() { BinImage imageCache = BinImage.copyFromArray(newFirmware.getBinData(), newFirmware.startAddress()); imageCache.writeToBinFile(cacheFileName); - logger.info("File APP_VERSION : {}", ansi().fgBright(GREEN).a(newFirmware.getAppVersion()).reset().toString()); + logger.info("File APP_VERSION : {}{}{}", ansi().fgBright(GREEN), newFirmware.getAppVersion(), ansi().reset()); // Check if FW image has correct offset for MCUs bootloader size if (newFirmware.startAddress() < bootLoaderIdentity.applicationFirstAddress()) { - logger.error(ansi().fgBright(RED).a(" Error! The specified firmware image would overwrite parts of the bootloader. Check FW offset setting in the linker!").reset().toString()); - logger.error(ansi().fgBright(RED).a(" Firmware needs to start at or beyond 0x{}").reset().toString(), String.format("%04X", bootLoaderIdentity.applicationFirstAddress())); + logger.error("{} Error! The specified firmware image would overwrite parts of the bootloader. Check FW offset setting in the linker!{}", + ansi().fgBright(RED), ansi().reset()); + logger.error("{} Firmware needs to start at or beyond 0x{}{}", ansi().fgBright(RED), + String.format("%04X", bootLoaderIdentity.applicationFirstAddress()), ansi().reset()); throw new UpdaterException("Firmware offset not correct!"); } else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddress()) { - logger.debug(ansi().fgBright(GREEN).a(" Firmware starts directly beyond bootloader.").reset().toString()); + logger.debug("{} Firmware starts directly beyond bootloader.{}", ansi().fgBright(GREEN), ansi().reset()); } else { - logger.debug(ansi().fgBright(YELLOW).a(" Info: There are {} bytes of unused flash between bootloader and firmware.").reset().toString(), - newFirmware.startAddress() - bootLoaderIdentity.applicationFirstAddress()); + logger.debug("{} Info: There are {} bytes of unused flash between bootloader and firmware.{}", + ansi().fgBright(YELLOW), newFirmware.startAddress() - bootLoaderIdentity.applicationFirstAddress(), + ansi().reset()); } if (cliOptions.getEraseFullFlashIsSet()) { - logger.warn(ansi().fgBright(RED).a("Deleting the entire flash except from the bootloader itself!").reset().toString()); + logger.warn("{}Deleting the entire flash except from the bootloader itself!{}", + ansi().fgBright(RED), ansi().reset()); dm.eraseFlash(); } @@ -276,7 +282,8 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres diffMode = FlashDiffMode.setupDifferentialMode(bootDescriptor); } else { - logger.error(ansi().fgBright(RED).a(" BootDescriptor is not valid -> switching to full mode").reset().toString()); + logger.error("{} BootDescriptor is not valid -> switching to full mode{}", + ansi().fgBright(RED), ansi().reset()); } } @@ -288,16 +295,17 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } if (!dm.setBlockSize(cliOptions.getBlockSize())) { - logger.info(ansi().fg(YELLOW).a("Connected bootloader doesn't support block size {}. Using {} bytes.").reset().toString(), - cliOptions.getBlockSize(), dm.getBlockSize()); + logger.info("{}Connected bootloader doesn't support block size {}. Using {} bytes.{}", + ansi().fg(YELLOW), cliOptions.getBlockSize(), dm.getBlockSize(), ansi().reset()); } if (!cliOptions.getNoFlashIsSet()) { // is flashing firmware disabled? for debugging use only! // Start to flash the new firmware ResponseResult resultTotal; - logger.info(ansi().bg(GREEN).fg(BLACK).a("Starting to send new firmware now:").reset().toString()); + logger.info("{}Starting to send new firmware now:{}", ansi().bg(GREEN).fg(BLACK), ansi().reset()); if (diffMode && FlashDiffMode.isInitialized()) { - logger.warn(ansi().fgBright(RED).a("Differential mode is EXPERIMENTAL -> Use with caution.").reset().toString()); + logger.warn("{}Differential mode is EXPERIMENTAL -> Use with caution.{}", + ansi().fgBright(RED), ansi().reset()); resultTotal = FlashDiffMode.doDifferentialFlash(dm, newFirmware.startAddress(), newFirmware.getBinData()); } else { @@ -312,18 +320,18 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } else { colored = ansi().fgBright(GREEN).toString(); } - updaterStatisticMsg += String.format("#Disconnect: %s%2d%s", colored, resultTotal.dropCount(), ansi().reset().toString()); + updaterStatisticMsg += String.format("#Disconnect: %s%2d%s", colored, resultTotal.dropCount(), ansi().reset()); if (resultTotal.timeoutCount() > BootloaderStatistic.THRESHOLD_REPEATED) { colored = ansi().fgBright(YELLOW).toString(); } else { colored = ansi().fgBright(GREEN).toString(); } - updaterStatisticMsg += String.format(" #Timeout : %s%2d%s", colored, resultTotal.timeoutCount(), ansi().reset().toString()); + updaterStatisticMsg += String.format(" #Timeout : %s%2d%s", colored, resultTotal.timeoutCount(), ansi().reset()); logger.info("{}", updaterStatisticMsg); } else { - logger.warn("--{} => {}", CliOptions.OPT_LONG_NO_FLASH, - ansi().fg(RED).a("only boot description block will be written").reset().toString()); + logger.warn("--{} => {}only boot description block will be written{}", CliOptions.OPT_LONG_NO_FLASH, + ansi().fg(RED), ansi().reset()); } BootDescriptor newBootDescriptor = new BootDescriptor(newFirmware.startAddress(), @@ -334,14 +342,17 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres if (cliOptions.getDevicePhysicalAddress() != null) { deviceInfo = cliOptions.getDevicePhysicalAddress().toString(); } - logger.info("Finished programming device {} with '{}'", ansi().fgBright(YELLOW).a(deviceInfo).reset().toString(), - ansi().fgBright(YELLOW).a(shortenPath(cliOptions.getFileName(), 1)).reset().toString()); - logger.info(ansi().bg(GREEN).fg(BLACK).a("Firmware Update done, Restarting device").reset().toString()); + logger.info("Finished programming device {}{}{} with '{}{}{}'", + ansi().fgBright(YELLOW), deviceInfo, ansi().reset(), + ansi().fgBright(YELLOW), shortenPath(cliOptions.getFileName(), 1), ansi().reset()); + logger.info("{}Firmware Update done, Restarting device{}", ansi().bg(GREEN).fg(BLACK), ansi().reset()); dm.restartProgrammingDevice(); if (newFirmware.getAppVersion().contains(BootloaderUpdater.BOOTLOADER_UPDATER_ID_STRING)) { - logger.info(ansi().bg(GREEN).fg(BLACK).a("Wait {} second(s) for Bootloader Updater to finish its job").reset().toString(), - String.format("%.2f", BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS / 1000.0f)); + logger.info("{}Wait {} second(s) for Bootloader Updater to finish its job{}", + ansi().bg(GREEN).fg(BLACK), + String.format("%.2f", BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS / 1000.0f), + ansi().reset()); Thread.sleep(BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS); } } catch (final KNXException | UpdaterException | RuntimeException e) { diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java index 2cb28569..6254ded5 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java @@ -86,10 +86,10 @@ private short long2short(long a) { public String toString() { String res; if (valid()) { - res = ansi().fgBright(GREEN).a(" valid").reset().toString(); + res = String.format("%s valid%s", ansi().fgBright(GREEN), ansi().reset()); } else { - res = ansi().fg(RED).a("invalid").reset().toString(); + res = String.format("%sinvalid%s", ansi().fg(RED), ansi().reset()); } res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X, APP_VERSION pointer: 0x%04X", res, long2short(startAddress()), long2short(endAddress()), length(), crc32(), long2short(appVersionAddress())); diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java index 671389ba..bf915420 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java @@ -31,13 +31,13 @@ public String toString() { } else { colored = ansi().fgBright(GREEN).toString(); } - result = String.format("#Disconnect: %s%2d%s", colored, getDisconnectCount(), ansi().reset().toString()); + result = String.format("#Disconnect: %s%2d%s", colored, getDisconnectCount(), ansi().reset()); if (getRepeatedT_ACKcount() > BootloaderStatistic.THRESHOLD_REPEATED) { colored = ansi().fgBright(YELLOW).toString(); } else { colored = ansi().fgBright(GREEN).toString(); } - result += String.format(" #repeated T_ACK: %s%2d%s", colored, getRepeatedT_ACKcount(), ansi().reset().toString()); + result += String.format(" #repeated T_ACK: %s%2d%s", colored, getRepeatedT_ACKcount(), ansi().reset()); return result; } diff --git a/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java index 860659f7..2e736e87 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java @@ -248,7 +248,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash logger.trace("Page {}, ", pages); flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); totalBytesTransferred = size; - logger.trace("OK! Total diff stream length={} bytes", ansi().fgBright(GREEN).a(size).reset().toString()); + logger.trace("OK! Total diff stream length={}{}{} bytes", ansi().fgBright(GREEN), size, ansi().reset()); } //dumpSideBySide(img1, img2); } From 7ec0a5e394def779b9850cc03864e52e362949b9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 21 Aug 2024 23:58:38 +0200 Subject: [PATCH 171/359] [Updater] Fix `--statistic` output with `jansi` Since switching to `jansi` the `--statistic` output was only for a very short time visible. --- .../org/selfbus/updater/DeviceManagement.java | 12 ++++--- .../org/selfbus/updater/FlashDiffMode.java | 2 +- .../org/selfbus/updater/FlashFullMode.java | 19 +++++++--- .../src/org/selfbus/updater/ProgressInfo.java | 4 +++ .../selfbus/updater/ProgressInfoAdvanced.java | 28 +++++++++++++++ .../src/org/selfbus/updater/Updater.java | 24 +++++-------- .../bootloader/BootloaderStatistic.java | 36 +++++++++++-------- .../updater/BootloaderStatisticTest.java | 4 +-- 8 files changed, 86 insertions(+), 43 deletions(-) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/ProgressInfoAdvanced.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 6e56843c..d3f3f999 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -295,8 +295,8 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo return result; } - public void startProgressInfo() { - logger.info(" Done Speed Avg Min Max Time"); + public void startProgressInfo(ProgressInfo progressInfo) { + logger.info(progressInfo.getHeader()); // We need one newLine for the gui ListTextAppenders.appendEvent(Level.INFO, System.lineSeparator()); } @@ -362,16 +362,18 @@ public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) } } - public void requestBootLoaderStatistic() throws UpdaterException { + public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException { logger.debug("Requesting Bootloader statistic"); byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); UPDCommand command = UPDCommand.tryFromByteArray(result); if (command != UPDCommand.RESPONSE_STATISTIC) { logger.warn("Requesting Bootloader statistic {}failed!{}", ansi().fg(RED), ansi().reset()); + return null; } - BootloaderStatistic blStatistic = BootloaderStatistic.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info("Bootloader: {}", blStatistic); //todo does´nt work with ProgressInfo and SpinningCursor + logger.debug("#Disconnect: {} #repeated T_ACK: {}", blStatistic.getDisconnectCountColored(), + blStatistic.getRepeatedT_ACKcount()); + return blStatistic; } public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetry) diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index 46d7a96f..3e410401 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -100,7 +100,7 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start ProgressInfo progressInfo = new ProgressInfo(bytesToFlash); SpinningCursor.reset(); logger.info("Start sending differential data ({} bytes)", bytesToFlash); - dm.startProgressInfo(); + dm.startProgressInfo(progressInfo); AtomicReference result = new AtomicReference<>(); differ = new FlashDiff(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index cdfbf85d..8c427522 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -1,5 +1,6 @@ package org.selfbus.updater; +import org.selfbus.updater.bootloader.BootloaderStatistic; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; import org.selfbus.updater.upd.UPDProtocol; @@ -44,13 +45,23 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa logMessage += String.format(" with telegram delay of %dms", dataSendDelay); } logger.info(logMessage); - dm.startProgressInfo(); int nRead = 0; boolean repeat = false; SpinningCursor.reset(); - ProgressInfo progressInfo = new ProgressInfo(totalLength); + ProgressInfo progressInfo; + BootloaderStatistic bootloaderStatistic; + if (logStatistics) { + bootloaderStatistic = dm.requestBootLoaderStatistic(); + progressInfo = new ProgressInfoAdvanced(totalLength, bootloaderStatistic); + } + else { + bootloaderStatistic = null; + progressInfo = new ProgressInfo(totalLength); + } + dm.startProgressInfo(progressInfo); + while (fis.available() > 0) { if (!repeat) { nRead = fis.read(buffer); // Read up to size of buffer @@ -94,8 +105,8 @@ else if (result == UDPResult.IAP_COMPARE_ERROR) { } else if (result == UDPResult.IAP_SUCCESS) { progAddress += txBuffer.length; - if (logStatistics) { - dm.requestBootLoaderStatistic(); + if (bootloaderStatistic != null) { + bootloaderStatistic = dm.requestBootLoaderStatistic(); } } else { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java index e3719ba8..0170db04 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java @@ -68,6 +68,10 @@ private String getSpeed(boolean averageSpeed) { return String.format("%s%5.1f%s", ansi().fgBright(color), bytesPerSecond, ansi().reset()); } + public String getHeader() { + return " Done Speed Avg Min Max Time"; + } + public String toString() { return String.format("%5.1f%% %s %s %5.1f %5.1f %tM:% BootloaderStatistic.THRESHOLD_DISCONNECT) { - colored = ansi().fgBright(YELLOW).toString(); - } else { - colored = ansi().fgBright(GREEN).toString(); + BootloaderStatistic bootloaderStatistic = dm.requestBootLoaderStatistic(); + if (bootloaderStatistic != null) { + logger.info("Bootloader: #Disconnect: {} #repeated T_ACK: {}", + bootloaderStatistic.getDisconnectCountColored(), bootloaderStatistic.getRepeatedT_ACKcountColored()); } - updaterStatisticMsg += String.format("#Disconnect: %s%2d%s", colored, resultTotal.dropCount(), ansi().reset()); - if (resultTotal.timeoutCount() > BootloaderStatistic.THRESHOLD_REPEATED) { - colored = ansi().fgBright(YELLOW).toString(); - } else { - colored = ansi().fgBright(GREEN).toString(); - } - updaterStatisticMsg += String.format(" #Timeout : %s%2d%s", colored, resultTotal.timeoutCount(), ansi().reset()); - logger.info("{}", updaterStatisticMsg); + BootloaderStatistic updaterStatistic = new BootloaderStatistic((int)resultTotal.dropCount(), + (int)resultTotal.timeoutCount()); + logger.info("Updater : #Disconnect: {} #repeated T_ACK: {}", + updaterStatistic.getDisconnectCountColored(), updaterStatistic.getRepeatedT_ACKcountColored()); } else { logger.warn("--{} => {}only boot description block will be written{}", CliOptions.OPT_LONG_NO_FLASH, diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java index bf915420..cf80a0b2 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java @@ -23,31 +23,37 @@ public static BootloaderStatistic fromArray(byte[] parse) { return new BootloaderStatistic(disConnectCount, repeatedT_ACKcount); } - public String toString() { - String result; - String colored; - if (getDisconnectCount() > BootloaderStatistic.THRESHOLD_DISCONNECT) { - colored = ansi().fgBright(YELLOW).toString(); - } else { - colored = ansi().fgBright(GREEN).toString(); - } - result = String.format("#Disconnect: %s%2d%s", colored, getDisconnectCount(), ansi().reset()); - if (getRepeatedT_ACKcount() > BootloaderStatistic.THRESHOLD_REPEATED) { - colored = ansi().fgBright(YELLOW).toString(); + private static String toColoredThreshold(int value, int threshold) { + String ansiColor; + if (value > threshold) { + ansiColor = ansi().fgBright(YELLOW).toString(); } else { - colored = ansi().fgBright(GREEN).toString(); + ansiColor = ansi().fgBright(GREEN).toString(); } - result += String.format(" #repeated T_ACK: %s%2d%s", colored, getRepeatedT_ACKcount(), ansi().reset()); + return String.format("%s%2d%s", ansiColor, value, ansi().reset()); + } + + public String toString() { + String result = getDisconnectCountColored(); + result += " " + getRepeatedT_ACKcountColored(); return result; } - public long getDisconnectCount() + public int getDisconnectCount() { return disconnectCount; } - public long getRepeatedT_ACKcount() + public int getRepeatedT_ACKcount() { return repeatedT_ACKcount; } + + public String getDisconnectCountColored() { + return toColoredThreshold(getDisconnectCount(), BootloaderStatistic.THRESHOLD_DISCONNECT); + } + + public String getRepeatedT_ACKcountColored() { + return toColoredThreshold(getRepeatedT_ACKcount(), BootloaderStatistic.THRESHOLD_REPEATED); + } } diff --git a/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java b/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java index 1ca41f0e..39cba2cb 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java @@ -13,8 +13,8 @@ public void testToString() { String colorOK = ansi().fgBright(GREEN).toString(); String colorWarn = ansi().fgBright(YELLOW).toString(); String colorReset = ansi().reset().toString(); - String info1 = "#Disconnect: "; - String info2 = " #repeated T_ACK: "; + String info1 = ""; + String info2 = " "; BootloaderStatistic test1 = new BootloaderStatistic(0, 0); BootloaderStatistic test2 = new BootloaderStatistic(BootloaderStatistic.THRESHOLD_DISCONNECT, 0); From db7f2bd8fc6b0979e6ace224ab43ed21e20f4d0b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 22 Nov 2024 18:40:40 +0100 Subject: [PATCH 172/359] [Updater] Fix missing `calimero-usb` dependency --- firmware_updater/updater/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index b947344c..ebf14490 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -110,6 +110,9 @@ dependencies { // calimero serial tx/rx lib for ft1.2 and tpuart support implementation 'com.github.calimero:calimero-rxtx:2.6-rc1' + // calimero usb support + implementation 'io.calimero:calimero-usb:2.6-rc1' + // find specific directories under linux and windows implementation 'net.harawata:appdirs:1.2.2' From e23f2b34be7aa74b09e91bb217ef7c8f7744e70f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 22 Nov 2024 18:45:23 +0100 Subject: [PATCH 173/359] [Updater] Fix `null` strings --- .../src/org/selfbus/updater/CliOptions.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index bd92ec60..4d08462b 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -507,7 +507,7 @@ private void parse(final String[] args) { setKnxInterface(cmdLine.getArgs()[0]); } else { - setKnxInterface(null); + setKnxInterface(""); } logger.debug("knxInterface={}", getKnxInterface()); @@ -646,8 +646,12 @@ public String helpToString() { return sw.toString(); } + private String nonNullString(String s) { + return s != null ? s : ""; + } + public String getKnxInterface() { - return knxInterface; + return nonNullString(knxInterface); } private void setKnxInterface(String knxInterface) { @@ -695,7 +699,7 @@ private void setNatIsSet(boolean natIsSet) { } public String getFt12SerialPort() { - return ft12SerialPort; + return nonNullString(ft12SerialPort); } private void setFt12SerialPort(String ft12SerialPort) { @@ -703,7 +707,7 @@ private void setFt12SerialPort(String ft12SerialPort) { } public String getTpuartSerialPort() { - return tpuartSerialPort; + return nonNullString(tpuartSerialPort); } private void setTpuartSerialPort(String tpuartSerialPort) { @@ -895,7 +899,7 @@ private void setBlockSize(int blockSize) { } public String getUsbVendorIdAndProductId() { - return usbVendorIdAndProductId; + return nonNullString(usbVendorIdAndProductId); } public void setUsbVendorIdAndProductId(String usbVendorIdAndProductId) { From 5c1202a3989e52b4105fd6ed27b58bdf40871bcb Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 22 Nov 2024 18:49:26 +0100 Subject: [PATCH 174/359] [Updater] Use `.isBlank` for interface checks --- .../src/org/selfbus/updater/CliOptions.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 4d08462b..e632a687 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -551,19 +551,19 @@ private void parse(final String[] args) { // check IP-secure configuration if (!(getKnxSecureUserPassword().isEmpty()) || !(getKnxSecureDevicePassword().isEmpty())) { - if (getKnxInterface().isEmpty()) { + if (getKnxInterface().isBlank()) { throw new CliInvalidException(String.format("%sNo IP-Interface specified for IP-secure%s", ansi().fg(RED), ansi().reset())); } - else if (!getUsbVendorIdAndProductId().isEmpty()) { + else if (!getUsbVendorIdAndProductId().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", ansi().fg(RED), OPT_LONG_USB, ansi().reset())); } - else if (!getFt12SerialPort().isEmpty()) { + else if (!getFt12SerialPort().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", ansi().fg(RED), OPT_LONG_FT12, ansi().reset())); } - else if (!getTpuartSerialPort().isEmpty()) { + else if (!getTpuartSerialPort().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", ansi().fg(RED), OPT_LONG_TPUART, ansi().reset())); } @@ -589,11 +589,11 @@ else if (getTunnelingV2isSet()) { } int interfacesSet = 0; - if (!getKnxInterface().isEmpty()) interfacesSet++; + if (!getKnxInterface().isBlank()) interfacesSet++; if (getRoutingIsSet()) interfacesSet++; - if (!getFt12SerialPort().isEmpty()) interfacesSet++; - if (!getTpuartSerialPort().isEmpty()) interfacesSet++; - if (!getUsbVendorIdAndProductId().isEmpty()) interfacesSet++; + if (!getFt12SerialPort().isBlank()) interfacesSet++; + if (!getTpuartSerialPort().isBlank()) interfacesSet++; + if (!getUsbVendorIdAndProductId().isBlank()) interfacesSet++; if (interfacesSet > 1) { throw new CliInvalidException(String.format("%sOnly one bus interface can be used.%s", From 50ab459607f7a6322a01c8d5a2a70de0938cfb9c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 24 Nov 2024 17:12:37 +0100 Subject: [PATCH 175/359] [Updater] Fix close `DeviceManagement` gracefully --- .../org/selfbus/updater/DeviceManagement.java | 26 +++++++++++++++---- .../src/org/selfbus/updater/Updater.java | 3 +++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index d3f3f999..651b2e8c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -29,7 +29,7 @@ /** * Provides methods to send firmware update telegrams to the bootloader (MCU) */ -public final class DeviceManagement { +public final class DeviceManagement implements AutoCloseable { /** * EraseCode for the APCI_MASTER_RESET_PDU (valid from 1..7) */ @@ -50,10 +50,6 @@ public final class DeviceManagement { private int maxPayload; private int updSendDataOffset; - private DeviceManagement () { - setProtocolVersion(UDPProtocolVersion.UDP_V1); - } - private final static Logger logger = LoggerFactory.getLogger(DeviceManagement.class); /** * Calimero device management client @@ -62,6 +58,10 @@ private DeviceManagement () { private Destination progDestination; private KNXNetworkLink link; + private DeviceManagement () { + setProtocolVersion(UDPProtocolVersion.UDP_V1); + } + public DeviceManagement(KNXNetworkLink link, IndividualAddress progDevice, Priority priority) throws KNXLinkClosedException { this(); @@ -72,6 +72,22 @@ public DeviceManagement(KNXNetworkLink link, IndividualAddress progDevice, Prior this.progDestination = this.mc.createDestination(progDevice, true, false, false); } + @Override + public void close() { + if (mc != null) { + logger.debug("Releasing mc"); + mc.detach(); + mc.close(); + } + if (link != null) { + logger.debug("Releasing link"); + link.close(); + } + progDestination = null; + mc = null; + link = null; + } + public void restartProgrammingDevice() throws KNXTimeoutException, KNXLinkClosedException, InterruptedException { logger.info("Restarting device {}", progDestination); diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 3ad336c2..9dcf975c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -242,6 +242,7 @@ public void run() { dm.restartProgrammingDevice(); } // to get here `uid == null` must be true, so it's fine to exit with no-error + dm.close(); link.close(); System.exit(0); } @@ -339,6 +340,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres ansi().fgBright(YELLOW), shortenPath(cliOptions.getFileName(), 1), ansi().reset()); logger.info("{}Firmware Update done, Restarting device{}", ansi().bg(GREEN).fg(BLACK), ansi().reset()); dm.restartProgrammingDevice(); + dm.close(); if (newFirmware.getAppVersion().contains(BootloaderUpdater.BOOTLOADER_UPDATER_ID_STRING)) { logger.info("{}Wait {} second(s) for Bootloader Updater to finish its job{}", @@ -382,6 +384,7 @@ public String requestUid(){ if (cliOptions.getDevicePhysicalAddress() != null) { dm.restartProgrammingDevice(); } + dm.close(); link.close(); return uid; From 637ef45fbc8d33b736b67327b32dd432c2b41856 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 24 Nov 2024 17:15:50 +0100 Subject: [PATCH 176/359] [Updater] Fix shortcut shown in combobox --- .../updater/src/resources/language/GuiMain_de.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/resources/language/GuiMain_de.properties b/firmware_updater/updater/src/resources/language/GuiMain_de.properties index 5abfeec4..a853d71a 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain_de.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain_de.properties @@ -33,7 +33,7 @@ useNat=&NAT knxMessageDelay=&Verzögerung [ms] eraseFlash=Lösche &kompletten Flash noFlash=keine Daten übertragen -requestUid=Erfasse U&ID von Gerät +requestUid=Erfasse UID von Gerät requestUidHint=Die UID wird von einem Selfbus Gerät abgefragt.\ UpdaterSettings=Updater From 08257d3073b2bbc39c15a90b5ffd0cc95ca63947 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 24 Nov 2024 17:17:32 +0100 Subject: [PATCH 177/359] [Updater] Add USB Interface discovery --- .../updater/DiscoverKnxInterfaces.java | 42 ++++++++++++++++++- .../src/org/selfbus/updater/gui/GuiMain.java | 10 ++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java index 58a09d0f..8655d778 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -1,14 +1,25 @@ package org.selfbus.updater; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tuwien.auto.calimero.KNXException; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; +import tuwien.auto.calimero.link.medium.KNXMediumSettings; +import tuwien.auto.calimero.serial.usb.Device; +import tuwien.auto.calimero.serial.usb.UsbConnection; +import tuwien.auto.calimero.serial.usb.UsbConnectionFactory; import java.time.Duration; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutionException; public class DiscoverKnxInterfaces { - public static List> getAllInterfaces(){ + private final static Logger logger = LoggerFactory.getLogger(DiscoverKnxInterfaces.class); + + public static List> getAllnetIPInterfaces() { List> interfacesResult = null; try { @@ -16,9 +27,36 @@ public static List> getAllInterfaces(){ final boolean useNAT = false; interfacesResult = Discoverer.udp(useNAT).timeout(Duration.ofSeconds(3)).search().get(); }catch (InterruptedException | ExecutionException e) { - System.err.println("Error during KNXnet/IP discovery: " + e); + logger.warn("Error during KNXnet/IP discovery: {}", e.getMessage()); } return interfacesResult; } + + public static Set getUsbInterfaces() { + Set knxUsbDevices = UsbConnectionFactory.attachedKnxUsbDevices(); + Iterator iterator = knxUsbDevices.iterator(); + while (iterator.hasNext()) { + Device d = iterator.next(); + try { + // check knx medium, defaults to TP1 + try (UsbConnection c = UsbConnectionFactory.open(d)) { + if (c.deviceDescriptor().medium().getMedium() != KNXMediumSettings.MEDIUM_TP1) { + iterator.remove(); + continue; + }; + } + catch (KNXException | InterruptedException | RuntimeException e) { + iterator.remove(); + logger.error("error: reading KNX device descriptor of {} ({})", d, e.getMessage()); + } + + } + catch (final RuntimeException e) { + iterator.remove(); + logger.error("error: {}", e.getMessage()); + } + } + return knxUsbDevices; + } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index a341511e..bf9b5584 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -10,6 +10,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tuwien.auto.calimero.serial.usb.Device; import javax.imageio.ImageIO; import javax.swing.*; @@ -357,10 +358,17 @@ private void loadKnxIpInterfacesAndFillComboBox() { comboBoxIpGateways.addItem(new CalimeroSearchComboItem(getTranslation("selectInterface"), null)); - DiscoverKnxInterfaces.getAllInterfaces().forEach(r -> + DiscoverKnxInterfaces.getAllnetIPInterfaces().forEach(r -> comboBoxIpGateways.addItem(new CalimeroSearchComboItem(r.response().getDevice().getName() + " (" + r.response().getControlEndpoint().endpoint().getAddress().getHostAddress() + ")", r))); + Set usbInterfaces = DiscoverKnxInterfaces.getUsbInterfaces(); + for (final var d : usbInterfaces) { + final String vp = String.format("%04x:%04x", d.vendorId(), d.productId()); + logger.info("Found USB Interface: {} {} S/N {} VID/PID {}", + d.manufacturer(), d.product(), d.serialNumber(), vp); + } + comboBoxIpGateways.addActionListener(comboBoxIpGatewaysActionListener); reloadGatewaysButton.setEnabled(true); } From dd66f48d90e7b11b42d45a822157399045acae8e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 24 Nov 2024 18:10:59 +0100 Subject: [PATCH 178/359] [Updater] Fix logged twice `crc32` --- .../updater/src/org/selfbus/updater/FlashFullMode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index 8c427522..7a52d8a2 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -89,7 +89,7 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa if (txBuffer.length != dm.getBlockSize()) { debugInfo = String.format("%s (%d bytes)", debugInfo, txBuffer.length); } - logger.debug("{} with crc32 {}", debugInfo, String.format("crc32 0x%08X", crc32)); + logger.debug("{} with crc32 {}", debugInfo, String.format("0x%08X", crc32)); resultProgramData = dm.sendWithRetry(UPDCommand.PROGRAM, progPars, dm.getMaxUpdCommandRetry()); From 1f0f19d90f05441d9ab97fb0b3c9631c77baed67 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 24 Nov 2024 19:03:51 +0100 Subject: [PATCH 179/359] [Updater] Fix `UDP_V0` protocol progress calculation --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 651b2e8c..1572b431 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -300,7 +300,7 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo restartProgrammingDevice(); throw new UpdaterException("doFlash failed."); } - updateProgressInfo(progressInfo, txBuffer.length); + updateProgressInfo(progressInfo, txBuffer.length - updSendDataOffset); nIndex += txBuffer.length - updSendDataOffset; if (delay > 0) { From 4fdeded5c87d20c967eb680d14a4289940e8a48d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 26 Nov 2024 14:17:10 +0100 Subject: [PATCH 180/359] [Updater] Update readme --- firmware_updater/updater/README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index e1f4e936..437b8f3f 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -3,17 +3,17 @@ ## Requirements * JDK 17+ -* gradle >=8.5 +* gradle >=8.9 * Selfbus device with flashed [bootloader](../bootloader) version 1.00 or higher ## Build ``` -gradle fatJar +gradle shadowJar ``` *or* ``` -linux: gradlew fatJar -windows: gradlew.bat fatJar +linux: gradlew shadowJar +windows: gradlew.bat shadowJar ``` *SB_updater-x.xx-all.jar* file is created in [build/libs](build/libs) directory. @@ -83,17 +83,13 @@ Read UID of the device: ``` java -jar SB_updater-x.xx-all.jar ``` -Recommended for new firmware versions if UID is unknown (requires active programming mode to unlock the device): +Recommended for new firmware versions if UID is unknown: ``` -java -jar SB_updater-x.xx-all.jar -fileName "out8-bcu1.hex" -nat +java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1.hex" ``` Recommended for new firmware versions with known UID: ``` -java -jar SB_updater-x.xx-all.jar -fileName "out8-bcu1.hex" -uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 -nat -``` -Manual specification of parameters if the App-Version pointer is not found/integrated in the firmware file: -``` -java -jar SB_updater-x.xx-all.jar -fileName "in16-bim112.hex" -appVersionPtr 0x3263 -uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 -nat +java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1.hex" --uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 ``` ## Development From 9709715101f97d35aa9cc1bcf23149c3407fafd2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 29 Dec 2024 21:15:05 +0100 Subject: [PATCH 181/359] [Updater] Replace deprecated `jansi` with `jline` --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index ebf14490..cfc4223c 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -126,7 +126,7 @@ dependencies { implementation 'ch.qos.logback:logback-classic:1.5.6' // Console ansi color support - implementation 'org.fusesource.jansi:jansi:2.4.1' + implementation 'org.jline:jline-terminal-jansi:3.28.0' // GUI implementation files('libs/forms_rt.jar') From d6990130c65a25cc27f2e8c9960a99dc3c3b57ab Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 30 Dec 2024 02:48:26 +0100 Subject: [PATCH 182/359] [Updater] Add UncaughtExceptionHandler to show a dialog --- .../src/org/selfbus/updater/gui/GuiMain.java | 4 + .../gui/GuiUncaughtExceptionHandler.java | 75 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index bf9b5584..13a0c8b5 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -190,6 +190,10 @@ public GuiMain() { //Locale.setDefault(Locale.ENGLISH); // for tests //Locale.setDefault(Locale.ROOT); // default language for tests $$$setupUI$$$(); + + // Set uncaught exception handler to show a dialog + Thread.setDefaultUncaughtExceptionHandler(new GuiUncaughtExceptionHandler(this)); + buttonLoadFile.addActionListener(actionEvent -> handleLoadFileAction()); buttonStartStopFlash.addActionListener(actionEvent -> handleStartStopFlashAction()); buttonRequestUid.addActionListener(actionEvent -> handleRequestUidAction()); diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java new file mode 100644 index 00000000..1b3bb795 --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java @@ -0,0 +1,75 @@ +package org.selfbus.updater.gui; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.*; +import java.io.PrintWriter; +import java.io.StringWriter; + +import static org.fusesource.jansi.Ansi.Color.*; +import static org.fusesource.jansi.Ansi.ansi; + +public class GuiUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { + private final static Logger logger = LoggerFactory.getLogger(GuiUncaughtExceptionHandler.class); + private final JFrame parentFrame; + + public GuiUncaughtExceptionHandler(JFrame parentFrame) { + this.parentFrame = parentFrame; + } + + /** + * Handles uncaught exceptions in threads by logging the error details and displaying + * an error dialog to inform the user. + * + * @param thread The thread in which the uncaught exception occurred. + * @param throwable The uncaught exception or error. + */ + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + logger.error("{}Uncaught exception in thread {} {}{}", + ansi().fg(RED) , thread.getName(), throwable.toString(), ansi().reset(), throwable); + + // Show exception details in a dialog + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog( + parentFrame, + "An unexpected error occurred:" + System.lineSeparator() + + throwable.toString() + System.lineSeparator() + + getLimitedStackTrace(throwable, 7), + "Exception", + JOptionPane.ERROR_MESSAGE + ); + }); + } + + /** + * Converts the stack trace to a String and limits its depth. + * + * @param throwable The exception or error + * @param maxDepth The maximum number of stack trace elements to include + * @return A String containing the limited stack trace + */ + private static String getLimitedStackTrace(Throwable throwable, int maxDepth) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + + // Get the stack trace elements + StackTraceElement[] stackTrace = throwable.getStackTrace(); + if (maxDepth < 0) + { + maxDepth = stackTrace.length; + } + int limit = Math.min(stackTrace.length, maxDepth); // Ensure we don't exceed the actual trace length + for (int i = 0; i < limit; i++) { + printWriter.println(" at " + stackTrace[i].toString()); + } + + // Indicate if there are more stack trace elements beyond the limit + if (stackTrace.length > maxDepth) { + printWriter.println(" ... (" + (stackTrace.length - maxDepth) + " more elements)"); + } + + return stringWriter.toString(); + } +} From 35dca824a01118c2bf9f2ee8195981db7babe350 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 31 Dec 2024 08:21:26 +0100 Subject: [PATCH 183/359] [Updater] Upgrade to logback-classic 1.5.15 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index cfc4223c..c70926b9 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -123,7 +123,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.8.0' // console and file logging - implementation 'ch.qos.logback:logback-classic:1.5.6' + implementation 'ch.qos.logback:logback-classic:1.5.15' // Console ansi color support implementation 'org.jline:jline-terminal-jansi:3.28.0' From ffc31a44ae38ff610d36b9f4aa7b2b686f27f2aa Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 31 Dec 2024 09:25:38 +0100 Subject: [PATCH 184/359] [Updater] Reduce loglevel to trace in `setCursorPosition` --- .../src/org/selfbus/updater/gui/ConColorsToStyledDoc.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index ad6b11c7..e4082695 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -435,17 +435,17 @@ public static void setCursorPosition(JTextPane textPane, Point newPosition) thro // Calculate the new caret position int newCaretPosition = lineStartOffset + columnIndex; - logger.debug("lineStartOffset={}", lineStartOffset); - logger.debug("newCaretPosition={}", newCaretPosition); + logger.trace("lineStartOffset={}", lineStartOffset); + logger.trace("newCaretPosition={}", newCaretPosition); if (newCaretPosition < 0) { - logger.debug("newCaretPosition set to 0"); + logger.trace("newCaretPosition set to 0"); newCaretPosition = 0; } if (newCaretPosition > doc.getLength()) { newCaretPosition = doc.getLength(); - logger.debug("newCaretPosition set to {}", newCaretPosition); + logger.trace("newCaretPosition set to {}", newCaretPosition); } int currentLength = doc.getLength(); From 155414dde05f870e3af81734f2de60c6b1105d87 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 20:16:02 +0100 Subject: [PATCH 185/359] [Updater] Upgrade to appdirs:1.3.0 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index c70926b9..9a79eaf8 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -114,7 +114,7 @@ dependencies { implementation 'io.calimero:calimero-usb:2.6-rc1' // find specific directories under linux and windows - implementation 'net.harawata:appdirs:1.2.2' + implementation 'net.harawata:appdirs:1.3.0' // For search in byte array implementation 'com.google.guava:guava:33.2.1-jre' From ab6cd22779c72fd2880647c1846fa61d671559f2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 20:22:43 +0100 Subject: [PATCH 186/359] [Updater] Upgrade to guava:33.4.0-jre --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 9a79eaf8..ce9d7051 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -117,7 +117,7 @@ dependencies { implementation 'net.harawata:appdirs:1.3.0' // For search in byte array - implementation 'com.google.guava:guava:33.2.1-jre' + implementation 'com.google.guava:guava:33.4.0-jre' // command line option implementation 'commons-cli:commons-cli:1.8.0' From 935b7acf248aba1b4c84c14fe465273a8e0668e7 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 20:23:10 +0100 Subject: [PATCH 187/359] [Updater] Upgrade to commons-cli:1.9.0 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index ce9d7051..08509310 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -120,7 +120,7 @@ dependencies { implementation 'com.google.guava:guava:33.4.0-jre' // command line option - implementation 'commons-cli:commons-cli:1.8.0' + implementation 'commons-cli:commons-cli:1.9.0' // console and file logging implementation 'ch.qos.logback:logback-classic:1.5.15' From 1bda6d76adf8d6ebef817cf540447c6815f7e644 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 20:23:37 +0100 Subject: [PATCH 188/359] [Updater] Upgrade to jackson-databind:2.18.2 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 08509310..25ceba3c 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -132,7 +132,7 @@ dependencies { implementation files('libs/forms_rt.jar') // GUI settings - implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.2' //implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' // Intel hex parser From 30d2225904f7d18c61009d56e3ea55a2c4066a1e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 20:26:24 +0100 Subject: [PATCH 189/359] [Updater] Add missing SB_updater.main.iml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don´t know why it wasn´t added with 6f230f1 --- firmware_updater/updater/.idea/modules.xml | 1 + .../updater/.idea/modules/SB_updater.main.iml | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 firmware_updater/updater/.idea/modules/SB_updater.main.iml diff --git a/firmware_updater/updater/.idea/modules.xml b/firmware_updater/updater/.idea/modules.xml index b6e5eb49..c81f6bdf 100644 --- a/firmware_updater/updater/.idea/modules.xml +++ b/firmware_updater/updater/.idea/modules.xml @@ -2,6 +2,7 @@ + diff --git a/firmware_updater/updater/.idea/modules/SB_updater.main.iml b/firmware_updater/updater/.idea/modules/SB_updater.main.iml new file mode 100644 index 00000000..0f14c0d5 --- /dev/null +++ b/firmware_updater/updater/.idea/modules/SB_updater.main.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file From d638bec420b1e89d1ba10db6d92df89a46828b28 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 20:51:21 +0100 Subject: [PATCH 190/359] [Updater] More detailed exception messages get rid of non-specific exception message "Selfbus update failed." --- .../src/org/selfbus/updater/DeviceManagement.java | 9 +++++---- .../updater/src/org/selfbus/updater/FlashDiffMode.java | 4 ++-- .../updater/src/org/selfbus/updater/Updater.java | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 1572b431..e0be393b 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -148,10 +148,11 @@ public String requestUIDFromDevice() return UPDProtocol.byteArrayToHex(uid); } else { uid = Arrays.copyOfRange(result, DATA_POSITION, result.length - DATA_POSITION); - logger.error("Request UID failed {} result.length={}, UID_LENGTH_USED={}, UID_LENGTH_MAX={}", - UPDProtocol.byteArrayToHex(uid),uid.length, UPDProtocol.UID_LENGTH_USED, UPDProtocol.UID_LENGTH_MAX); + String errorMsg = String.format("Request UID failed %s result.length=%d, UID_LENGTH_USED=%d, UID_LENGTH_MAX=%d", + UPDProtocol.byteArrayToHex(uid), uid.length, UPDProtocol.UID_LENGTH_USED, UPDProtocol.UID_LENGTH_MAX); + logger.error(errorMsg); restartProgrammingDevice(); - throw new UpdaterException("Selfbus update failed."); + throw new UpdaterException(errorMsg); } } @@ -223,7 +224,7 @@ public void unlockDeviceWithUID(String uid) byte[] result = sendWithRetry(UPDCommand.UNLOCK_DEVICE, UPDProtocol.uidToByteArray(uid), getMaxUpdCommandRetry()).data(); if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { restartProgrammingDevice(); - throw new UpdaterException("Selfbus update failed."); + throw new UpdaterException(String.format("Unlocking device %s failed.", progDestination.getAddress())); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index 3e410401..84e05564 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -122,7 +122,7 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start //\todo switch to full flash mode on a NOT_IMPLEMENTED instead of exiting if (UPDProtocol.checkResult(result.get().data(), false) != UDPResult.IAP_SUCCESS) { dm.restartProgrammingDevice(); - throw new UpdaterException("Selfbus update failed."); + throw new UpdaterException("DiffMode SEND_DATA_TO_DECOMPRESS failed."); } dm.updateProgressInfo(progressInfo, txBuf.length); } @@ -133,7 +133,7 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start logger.debug("Program device next page diff, crc32 0x{}", String.format("%08X", crc32)); result.set(dm.sendWithRetry(UPDCommand.PROGRAM_DECOMPRESSED_DATA, progPars, dm.getMaxUpdCommandRetry())); if (UPDProtocol.checkResult(result.get().data()) != UDPResult.IAP_SUCCESS) { - throw new UpdaterException("Selfbus update failed."); + throw new UpdaterException("DiffMode PROGRAM_DECOMPRESSED_DATA failed."); } }); dm.finalProgressInfo(progressInfo); diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 9dcf975c..c8a87390 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -172,7 +172,7 @@ public void run() { // check if the firmware file exists if (!Utils.fileExists(hexFileName)) { logger.error("{}File '{}' does not exist!{}", ansi().fg(RED), cliOptions.getFileName(), ansi().reset()); - throw new UpdaterException("Selfbus update failed."); + throw new UpdaterException(String.format("File '%s' does not exist!", cliOptions.getFileName())); } // Load Firmware hex file logger.info("Loading file '{}'", hexFileName); From 776613d5d708709bda7fb7df367f89a3ad6f69ec Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 20:58:35 +0100 Subject: [PATCH 191/359] [Updater] Fix `TextAppender` does not log stacktrace of exception --- .../org/selfbus/updater/gui/TextAppender.java | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java b/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java index 70b67aa5..521fc164 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java @@ -1,13 +1,13 @@ package org.selfbus.updater.gui; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; import ch.qos.logback.core.AppenderBase; import javax.swing.*; import javax.swing.text.BadLocationException; -import static javax.swing.SwingUtilities.invokeLater; - public class TextAppender extends AppenderBase { private JTextPane textPane; @@ -25,24 +25,60 @@ private void updateTextPane(String message) throws BadLocationException { @Override protected void append(ILoggingEvent event) { - String eventMessage = event.getFormattedMessage(); - // Make sure, that every eventMessage ends with a line separator - if (eventMessage.lastIndexOf(System.lineSeparator()) != eventMessage.length() - System.lineSeparator().length()) { - eventMessage += System.lineSeparator(); + if (getTextPane() == null) { + return; + } + + StringBuilder eventMessage = new StringBuilder(event.getFormattedMessage()); + + // Check for ThrowableProxy to retrieve the stack trace + IThrowableProxy throwableProxy = event.getThrowableProxy(); + if (throwableProxy != null) { + // Append the stack trace to the log message + eventMessage.append(System.lineSeparator()); + appendStackTrace(throwableProxy, eventMessage); } - final String finalMessage = eventMessage; - invokeLater(() -> { + String logMessage = eventMessage.toString(); + + // Ensure the message ends with a line separator + if (!logMessage.endsWith(System.lineSeparator())) { + logMessage += System.lineSeparator(); + } + + String finalLogMessage = logMessage; + SwingUtilities.invokeLater(() -> { try { - // Append formatted message to text area using the Thread. - updateTextPane(finalMessage); + // Append formatted message to text area using the Thread + updateTextPane(finalLogMessage); } - catch (Throwable e) { + catch (BadLocationException e) { throw new RuntimeException(e); } }); } + private void appendStackTrace(IThrowableProxy throwableProxy, StringBuilder builder) { + builder.append(throwableProxy.getClassName()) + .append(": ") + .append(throwableProxy.getMessage()) + .append(System.lineSeparator()); + + // Loop through each stack trace element and append it + for (StackTraceElementProxy elementProxy : throwableProxy.getStackTraceElementProxyArray()) { + builder.append("\t ") + .append(elementProxy.toString()) + .append(System.lineSeparator()); + } + + // If there are any nested causes, recursively append them + IThrowableProxy cause = throwableProxy.getCause(); + if (cause != null) { + builder.append("Caused by: "); + appendStackTrace(cause, builder); + } + } + public JTextPane getTextPane() { return textPane; } From f9ec15249326838669afdba66800735f3d467e4c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 21:45:06 +0100 Subject: [PATCH 192/359] [Updater] Major fixes/refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Catch `InterruptedException` only on "top-level". The gui starts the updater as a new thread and `InterruptedException` intended to stop a thread gracefully. - Move cli argument validation and logging to corresponding set methods - Don´t `System.exit()` on invalid cli arguments (kills also the gui otherwise) - use calimero´s `KNX` constants if available - New class `LoggingManager` to simplify logging configuration, e.g. switching console logging to `WARN` then the gui is started. - Show IP-Interface´s tunneling version and physical address in `CalimeroSearchCombo` - Add todos for cli arguments that are not implemented in gui --- .../src/org/selfbus/updater/CliOptions.java | 773 ++++++++++++------ .../org/selfbus/updater/DeviceManagement.java | 50 +- .../updater/DiscoverKnxInterfaces.java | 12 +- .../org/selfbus/updater/LoggingManager.java | 86 ++ .../src/org/selfbus/updater/SBKNXLink.java | 39 +- .../updater/SBManagementClientImpl.java | 5 +- .../src/org/selfbus/updater/ToolInfo.java | 4 + .../src/org/selfbus/updater/Updater.java | 134 +-- .../org/selfbus/updater/gui/CliConverter.java | 11 - .../src/org/selfbus/updater/gui/GuiMain.java | 228 ++++-- .../gui/GuiUncaughtExceptionHandler.java | 21 +- .../src/resources/language/GuiMain.properties | 8 +- .../resources/language/GuiMain_de.properties | 8 +- .../updater/src/resources/logback.xml | 4 + 14 files changed, 913 insertions(+), 470 deletions(-) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index e632a687..52b99c92 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -19,12 +19,15 @@ import tuwien.auto.calimero.KNXIllegalArgumentException; import tuwien.auto.calimero.Priority; import tuwien.auto.calimero.knxnetip.KNXnetIPConnection; +import tuwien.auto.calimero.link.medium.KNXMediumSettings; import tuwien.auto.calimero.link.medium.TPSettings; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; @@ -115,15 +118,16 @@ public class CliOptions { private static final int PRINT_WIDTH = 100; private final static List VALID_LOG_LEVELS = Arrays.asList("TRACE", "DEBUG", "INFO"); + private final static int INVALID_SECURE_USER_ID = -1; private final Options cliOptions = new Options(); // define parser CommandLine cmdLine; final HelpFormatter helper = new HelpFormatter(); - private final String helpHeader; - private final String helpFooter; - private final String helpApplicationName; + private String helpHeader = ""; + private String helpFooter = ""; + private String helpApplicationName = ""; private String knxInterface =""; private String fileName = ""; @@ -137,15 +141,14 @@ public class CliOptions { private boolean tunnelingV2isSet = false; private boolean tunnelingV1isSet = false; private boolean routingIsSet = false; - private String medium = "tp1"; + private String medium = KNXMediumSettings.getMediumString(KNXMediumSettings.MEDIUM_TP1); - private int knxSecureUserId = 1; + private int knxSecureUserId = INVALID_SECURE_USER_ID; private String knxSecureUserPassword = ""; private String knxSecureDevicePassword = ""; - - private IndividualAddress progDevicePhysicalAddress; - private IndividualAddress ownPhysicalAddress; + private IndividualAddress progDevicePhysicalAddress = Updater.PHYS_ADDRESS_BOOTLOADER; + private IndividualAddress ownPhysicalAddress = Updater.PHYS_ADDRESS_OWN; private IndividualAddress devicePhysicalAddress = null; private String uid = ""; private boolean flashingFullModeIsSet = false; @@ -161,13 +164,10 @@ public class CliOptions { private boolean logStatisticsIsSet = false; private int blockSize = Mcu.UPD_PROGRAM_SIZE; - public CliOptions(final String[] args, String helpApplicationName, String helpHeader, String helpFooter, - IndividualAddress progDevicePhysicalAddress, IndividualAddress ownAddress) { - this.helpApplicationName = helpApplicationName; - this.helpHeader = helpHeader; - this.helpFooter = helpFooter; - setProgDevicePhysicalAddress(progDevicePhysicalAddress); //todo check why we have this as a method parameter and why is it set here? - setOwnPhysicalAddress(ownAddress); //todo check why we have this as a method parameter and why is it set here? + private final Level defaultLogLevel; + + private CliOptions() { + defaultLogLevel = getLogLevel(); Option tunnelingV2 = new Option(OPT_SHORT_TUNNEL_V2, OPT_LONG_TUNNEL_V2, false, "use KNXnet/IP tunneling v2 (TCP) (experimental)"); Option tunnelingV1 = new Option(OPT_SHORT_TUNNEL_V1, OPT_LONG_TUNNEL_V1, false, "use KNXnet/IP tunneling v1 (UDP)"); @@ -266,7 +266,7 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe .numberOfArgs(1) .required(false) .type(String.class) - .desc(String.format("Logfile logging level [TRACE|DEBUG|INFO] (default %s)", getLogLevel().toString())).build(); + .desc(String.format("Logfile logging level [TRACE|DEBUG|INFO] (default %s)", defaultLogLevel.toString())).build(); Option userId = Option.builder(null).longOpt(OPT_LONG_USER_ID) .argName("id") @@ -309,7 +309,7 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe cliOptions.addOption(medium); - // usb, ft12 or tpuart, not both + // usb, ft12 or tpuart OptionGroup grpBusAccess = new OptionGroup(); grpBusAccess.addOption(ft12); grpBusAccess.addOption(tpuart); @@ -353,285 +353,335 @@ public CliOptions(final String[] args, String helpApplicationName, String helpHe cliOptions.addOption(dumpFlash); cliOptions.addOption(NO_FLASH); cliOptions.addOption(logStatistic); + } + public CliOptions(final String[] args, String helpApplicationName, String helpHeader, String helpFooter) + throws KNXFormatException, ParseException { + this(); + this.helpApplicationName = helpApplicationName; + this.helpHeader = helpHeader; + this.helpFooter = helpFooter; + parse(args); + } + public CliOptions(final ArrayList argsList) throws KNXFormatException, ParseException { + this(); + this.helpApplicationName = ""; + this.helpHeader = ""; + this.helpFooter = ""; + String[] args = new String[argsList.size()]; + args = argsList.toArray(args); parse(args); } - private void parse(final String[] args) { + private void parse(final String[] args) throws ParseException, KNXFormatException { CommandLineParser parser = new DefaultParser(); - try { - cmdLine = parser.parse(cliOptions, args); - if (cmdLine.hasOption(OPT_SHORT_LOGLEVEL)) { - String cliLogLevel = cmdLine.getOptionValue(OPT_SHORT_LOGLEVEL).toUpperCase(); - if (VALID_LOG_LEVELS.contains(cliLogLevel)) { - setLogLevel(Level.toLevel(cliLogLevel)); - } - else { - logger.warn("{}invalid {} {}, using {}{}", ansi().fg(RED), OPT_LONG_LOGLEVEL, cliLogLevel, - getLogLevel().toString(), ansi().reset()); - } + cmdLine = parser.parse(cliOptions, args); + + if (cmdLine.hasOption(OPT_LONG_LOGLEVEL)) + setLogLevel(cmdLine.getOptionValue(OPT_LONG_LOGLEVEL).toUpperCase()); + else + setLogLevel(defaultLogLevel); + + if (cmdLine.hasOption(OPT_LONG_PRIORITY)) + setPriority(cmdLine.getOptionValue(OPT_LONG_PRIORITY)); + else + setPriority("LOW"); + + if (getHelpIsSet()) { + logger.debug("{}={}", OPT_LONG_HELP, getHelpIsSet()); + return; + } + + if (getVersionIsSet()) { + logger.debug("{}={}", OPT_LONG_VERSION, getVersionIsSet()); + return; + } + + setNoFlashIsSet(cmdLine.hasOption(OPT_LONG_NO_FLASH)); + setEraseFullFlashIsSet(cmdLine.hasOption(OPT_LONG_ERASEFLASH)); + + if (cmdLine.hasOption(OPT_LONG_DUMPFLASH)) { + String[] optArgs = cmdLine.getOptionValues(OPT_LONG_DUMPFLASH); + setDumpFlashStartAddress(Long.decode(optArgs[0])); + setDumpFlashEndAddress(Long.decode(optArgs[1])); + } + + setFlashingFullModeIsSet(cmdLine.hasOption(OPT_LONG_FULL)); + setTunnelingV2isSet(cmdLine.hasOption(OPT_LONG_TUNNEL_V2)); + setTunnelingV1isSet(cmdLine.hasOption(OPT_LONG_TUNNEL_V1)); + setRoutingIsSet(cmdLine.hasOption(OPT_LONG_ROUTING)); + setNatIsSet(cmdLine.hasOption(OPT_LONG_NAT)); + + if (cmdLine.hasOption(OPT_LONG_FILENAME)) + setFileName(cmdLine.getOptionValue(OPT_LONG_FILENAME)); + else + setFileName(""); + + + if (cmdLine.hasOption(OPT_LONG_LOCALHOST)) + setLocalhost(cmdLine.getOptionValue(OPT_LONG_LOCALHOST)); + else + setLocalhost(""); + + if (cmdLine.hasOption(OPT_LONG_LOCALPORT)) + setLocalPort(cmdLine.getOptionValue(OPT_LONG_LOCALPORT)); + else + setLocalPort(0); + + if (cmdLine.hasOption(OPT_LONG_PORT)) + setPort(cmdLine.getOptionValue(OPT_LONG_PORT)); + else + setPort(KNXnetIPConnection.DEFAULT_PORT); + + if (cmdLine.hasOption(OPT_LONG_MEDIUM)) + setMedium(cmdLine.getOptionValue(OPT_LONG_MEDIUM)); + else + setMedium(KNXMediumSettings.getMediumString(KNXMediumSettings.MEDIUM_TP1)); + + if (cmdLine.hasOption(OPT_LONG_DELAY)) + setDelayMs(cmdLine.getOptionValue(OPT_LONG_DELAY)); + else + setDelayMs(Updater.DELAY_MIN); + + if (cmdLine.hasOption(OPT_LONG_BLOCKSIZE)) + setBlockSize(cmdLine.getOptionValue(OPT_LONG_BLOCKSIZE)); + else + setBlockSize(Mcu.UPD_PROGRAM_SIZE); + + if (cmdLine.hasOption(OPT_LONG_UID)) + setUid(cmdLine.getOptionValue(OPT_LONG_UID)); + else + setUid(""); + + if (cmdLine.hasOption(OPT_LONG_DEVICE)) + setDevicePhysicalAddress(cmdLine.getOptionValue(OPT_LONG_DEVICE)); + else + setDevicePhysicalAddress(null); + + if (cmdLine.hasOption(OPT_LONG_PROG_DEVICE)) + setProgDevicePhysicalAddress(cmdLine.getOptionValue(OPT_LONG_PROG_DEVICE)); + else + setProgDevicePhysicalAddress(Updater.PHYS_ADDRESS_BOOTLOADER.toString()); + + if (cmdLine.hasOption(OPT_LONG_OWN_ADDRESS)) + setOwnPhysicalAddress(cmdLine.getOptionValue(OPT_LONG_OWN_ADDRESS)); + else + setOwnPhysicalAddress(Updater.PHYS_ADDRESS_OWN.toString()); + + if (cmdLine.hasOption(OPT_LONG_FT12)) + setFt12SerialPort(cmdLine.getOptionValue(OPT_LONG_FT12)); + else + setFt12SerialPort(""); + + if (cmdLine.hasOption(OPT_LONG_TPUART)) + setTpuartSerialPort(cmdLine.getOptionValue(OPT_LONG_TPUART)); + else + setTpuartSerialPort(""); + + if (cmdLine.hasOption(OPT_LONG_USB)) + setUsbVendorIdAndProductId(cmdLine.getOptionValue(OPT_LONG_USB)); + else + setUsbVendorIdAndProductId(""); + + if (cmdLine.getArgs().length > 0) + setKnxInterface(cmdLine.getArgs()[0]); + else + setKnxInterface(""); + + if (cmdLine.hasOption(OPT_LONG_USER_ID)) + setKnxSecureUserId(cmdLine.getOptionValue(OPT_LONG_USER_ID)); + else + setKnxSecureUserId(INVALID_SECURE_USER_ID); + + if (cmdLine.hasOption(OPT_LONG_USER_PASSWORD)) + setKnxSecureUserPassword(cmdLine.getOptionValue(OPT_LONG_USER_PASSWORD)); + else + setKnxSecureUserPassword(""); + + if (cmdLine.hasOption(OPT_LONG_DEVICE_PASSWORD)) + setKnxSecureDevicePassword(cmdLine.getOptionValue(OPT_LONG_DEVICE_PASSWORD)); + else + setKnxSecureDevicePassword(""); + + setLogStatisticsIsSet(cmdLine.hasOption(OPT_LONG_LOGSTATISTIC)); + + validateCliParameters(); + } + + private void validateCliParameters() throws CliInvalidException { + // some logical checks for options which exclude each other + // differential mode and eraseflash makes no sense + if (getEraseFullFlashIsSet() && (!getFlashingFullModeIsSet())) { + setFlashingFullModeIsSet(true); + logger.info("{}--{} is set. --> switching to full flash mode{}", ansi().fg(RED), + OPT_LONG_ERASEFLASH, ansi().reset()); + } + + // nat only possible with tunneling v1 + if (getNatIsSet() && (!getTunnelingV1isSet())) { + throw new CliInvalidException(String.format("%sOption --%s can only be used together with --%s%s", + ansi().fg(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ansi().reset())); + } + + // nat not allowed with tunneling v2 + if (getNatIsSet() && (getTunnelingV2isSet())) { + throw new CliInvalidException(String.format("%sOption --%s can not be used together with --%s%s", + ansi().fg(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2, ansi().reset())); + } + + // check IP-secure configuration + if (!(getKnxSecureUserPassword().isEmpty()) || !(getKnxSecureDevicePassword().isEmpty())) { + if (getKnxInterface().isBlank()) { + throw new CliInvalidException(String.format("%sNo IP-Interface specified for IP-secure%s", + ansi().fg(RED), ansi().reset())); } - logger.debug("logLevel={}", getLogLevel()); - - if (cmdLine.hasOption(OPT_LONG_PRIORITY)) { - try { - setPriority(Priority.get(cmdLine.getOptionValue(OPT_LONG_PRIORITY))); //todo simplify - } - catch (KNXIllegalArgumentException e) { - logger.warn("{}invalid --{} {}, using {}{}",ansi().fg(RED), OPT_LONG_PRIORITY, - cmdLine.getOptionValue(OPT_LONG_PRIORITY), getPriority(), ansi().reset()); - } + else if (!getUsbVendorIdAndProductId().isBlank()) { + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_USB, ansi().reset())); } - logger.debug("priority={}", getPriority().toString()); - - if (getHelpIsSet()) { - logger.debug("help={}", getHelpIsSet()); - return; + else if (!getFt12SerialPort().isBlank()) { + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_FT12, ansi().reset())); + } + else if (!getTpuartSerialPort().isBlank()) { + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_TPUART, ansi().reset())); + } + else if (getNatIsSet()) { + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_NAT, ansi().reset())); + } + else if (getTunnelingV1isSet()) { + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_TUNNEL_V1, ansi().reset())); + } + else if (getTunnelingV2isSet()) { + throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", + ansi().fg(RED), OPT_LONG_TUNNEL_V2, ansi().reset())); } - if (getVersionIsSet()) { - logger.debug("version={}", getVersionIsSet()); - return; + // ensure that all three IP-Secure arguments are set + if ((getKnxSecureUserPassword().isEmpty()) || (getKnxSecureDevicePassword().isEmpty())) { + throw new CliInvalidException(String.format("%sFor IP-secure --%s, --%s and --%s must be set%s", + ansi().fg(RED), OPT_LONG_USER_ID, OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD, + ansi().reset())); } + } - setNoFlashIsSet(cmdLine.hasOption(OPT_SHORT_NO_FLASH)); - logger.debug("NO_FLASH={}", getNoFlashIsSet()); + int interfacesSet = 0; + if (!getKnxInterface().isBlank()) interfacesSet++; + if (getRoutingIsSet()) interfacesSet++; + if (!getFt12SerialPort().isBlank()) interfacesSet++; + if (!getTpuartSerialPort().isBlank()) interfacesSet++; + if (!getUsbVendorIdAndProductId().isBlank()) interfacesSet++; - setEraseFullFlashIsSet(cmdLine.hasOption(OPT_LONG_ERASEFLASH)); - logger.debug("eraseFlash={}", getEraseFullFlashIsSet()); + if (interfacesSet > 1) { + throw new CliInvalidException("Only one bus interface can be used."); + } + else if (interfacesSet == 0) { + throw new CliInvalidException("No bus interface specified."); + } + } - if (cmdLine.hasOption(OPT_LONG_DUMPFLASH)) { - String[] optArgs = cmdLine.getOptionValues(OPT_LONG_DUMPFLASH); - setDumpFlashStartAddress(Long.decode(optArgs[0])); - setDumpFlashEndAddress(Long.decode(optArgs[1])); - } - logger.debug("dumpFlashStartAddress={}", getDumpFlashStartAddress()); - logger.debug("dumpFlashEndAddress={}", getDumpFlashEndAddress()); + public String reconstructCommandLine() { + String builder = ""; - setFlashingFullModeIsSet(cmdLine.hasOption(OPT_SHORT_FULL)); - logger.debug("full={}", getFlashingFullModeIsSet()); + if (!getKnxInterface().isBlank()) + builder += getKnxInterface(); - setTunnelingV2isSet(cmdLine.hasOption(OPT_SHORT_TUNNEL_V2)); - logger.debug("tunnelingV2={}", getTunnelingV2isSet()); + if (!getFileName().isBlank()) + builder += String.format(" --%s %s", OPT_LONG_FILENAME, getFileName()); - setTunnelingV1isSet(cmdLine.hasOption(OPT_SHORT_TUNNEL_V1)); - logger.debug("tunneling={}", getTunnelingV2isSet()); + if (!getLocalhost().isBlank()) + builder += String.format(" --%s %s", OPT_LONG_LOCALHOST, getLocalhost()); - setRoutingIsSet(cmdLine.hasOption(OPT_SHORT_ROUTING)); - logger.debug("routing={}", getRoutingIsSet()); + if (getLocalPort() != 0) + builder += String.format(" --%s %s", OPT_LONG_LOCALPORT, getLocalPort()); - setNatIsSet(cmdLine.hasOption(OPT_SHORT_NAT)); - logger.debug("nat={}", getNatIsSet()); + if (getPort() != KNXnetIPConnection.DEFAULT_PORT) + builder += String.format(" --%s %s", OPT_LONG_PORT, getPort()); - if (cmdLine.hasOption(OPT_SHORT_FILENAME)) { - setFileName(cmdLine.getOptionValue(OPT_SHORT_FILENAME)); - } - logger.debug("fileName={}", getFileName()); + if (!getFt12SerialPort().isBlank()) + builder += String.format(" --%s %s", OPT_LONG_FT12, getFt12SerialPort()); - if (cmdLine.hasOption(OPT_SHORT_LOCALHOST)) { - setLocalhost(cmdLine.getOptionValue(OPT_SHORT_LOCALHOST)); - } - logger.debug("localhost={}", getLocalhost()); + if (!getTpuartSerialPort().isBlank()) + builder += String.format(" --%s %s", OPT_LONG_TPUART, getTpuartSerialPort()); - if (cmdLine.hasOption(OPT_SHORT_LOCALPORT)) { - setLocalPort(((Number)cmdLine.getParsedOptionValue(OPT_SHORT_LOCALPORT)).intValue()); - } - logger.debug("localport={}", getLocalPort()); + if (!getUsbVendorIdAndProductId().isBlank()) + builder += String.format(" --%s %s", OPT_LONG_USB, getUsbVendorIdAndProductId()); - if (cmdLine.hasOption(OPT_SHORT_PORT)) { - setPort(((Number)cmdLine.getParsedOptionValue(OPT_SHORT_PORT)).intValue()); - } - logger.debug("port={}", getPort()); + if (!Objects.equals(getMedium(), KNXMediumSettings.getMediumString(KNXMediumSettings.MEDIUM_TP1)) && (!getMedium().isBlank())) + builder += String.format(" --%s %s", OPT_LONG_MEDIUM, getMedium()); - if (cmdLine.hasOption(OPT_SHORT_MEDIUM)) { - setMedium(cmdLine.getOptionValue(OPT_SHORT_MEDIUM)); - } - logger.debug("medium={}", getMedium()); + if (getKnxSecureUserId() >= 0) + builder += String.format(" --%s ***", OPT_LONG_USER_ID); - if (cmdLine.hasOption(OPT_LONG_DELAY)) { - setDelayMs(((Number)cmdLine.getParsedOptionValue(OPT_LONG_DELAY)).intValue()); - } - logger.debug("delay={}", getDelayMs()); - - if (cmdLine.hasOption(OPT_SHORT_BLOCKSIZE)) { - int newBlockSize = ((Number)cmdLine.getParsedOptionValue(OPT_LONG_BLOCKSIZE)).intValue(); - if (VALID_BLOCKSIZES.contains(newBlockSize)) { - setBlockSize(newBlockSize); - } - else { - logger.info("{}--{} {} is not supported => Set --{} to default {} bytes{}", ansi().fg(YELLOW), - OPT_LONG_BLOCKSIZE, newBlockSize, OPT_LONG_BLOCKSIZE, getBlockSize(), ansi().reset()); - } - } - logger.debug("{}={}", OPT_LONG_BLOCKSIZE, getBlockSize()); + if (!getKnxSecureUserPassword().isBlank()) + builder += String.format(" --%s ***", OPT_LONG_USER_PASSWORD); - if (cmdLine.hasOption(OPT_SHORT_UID)) { - setUid(cmdLine.getOptionValue(OPT_SHORT_UID)); - } - logger.debug("uid={}", getUid()); + if (!getKnxSecureDevicePassword().isBlank()) + builder += String.format(" --%s ***", OPT_LONG_DEVICE_PASSWORD); - if (cmdLine.hasOption(OPT_SHORT_DEVICE)) { - setDevicePhysicalAddress(new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_DEVICE))); - } else { - setDevicePhysicalAddress(null); - } - logger.debug("device={}", getDevicePhysicalAddress()); + if (!getProgDevicePhysicalAddress().toString().equals(Updater.PHYS_ADDRESS_BOOTLOADER.toString())) + builder += String.format(" --%s %s", OPT_LONG_PROG_DEVICE, getProgDevicePhysicalAddress()); - if (cmdLine.hasOption(OPT_SHORT_PROG_DEVICE)) { - setProgDevicePhysicalAddress(new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_PROG_DEVICE))); - } - logger.debug("progDevice={}", getProgDevicePhysicalAddress()); + if (getDevicePhysicalAddress() != null) + builder += String.format(" --%s %s", OPT_LONG_DEVICE, getDevicePhysicalAddress()); - if (cmdLine.hasOption(OPT_SHORT_OWN_ADDRESS)) { - setOwnPhysicalAddress(new IndividualAddress(cmdLine.getOptionValue(OPT_SHORT_OWN_ADDRESS))); - } - logger.debug("ownAddress={}", getOwnPhysicalAddress()); + if (!getOwnPhysicalAddress().toString().equals(Updater.PHYS_ADDRESS_OWN.toString())) + builder += String.format(" --%s %s", OPT_LONG_OWN_ADDRESS, getOwnPhysicalAddress()); - if (cmdLine.hasOption(OPT_SHORT_FT12)) { - setFt12SerialPort(cmdLine.getOptionValue(OPT_SHORT_FT12)); - } - logger.debug("ft12={}", getFt12SerialPort()); + if (!getUid().isBlank()) + builder += String.format(" --%s %s", OPT_LONG_UID, getUid()); - if (cmdLine.hasOption(OPT_SHORT_TPUART)) { - setTpuartSerialPort(cmdLine.getOptionValue(OPT_SHORT_TPUART)); - } - logger.debug("tpuart={}", getTpuartSerialPort()); + if (getDelayMs() > 0) + builder += String.format(" --%s %d", OPT_LONG_DELAY, getDelayMs()); - if (cmdLine.hasOption(OPT_LONG_USB)) { - setUsbVendorIdAndProductId(cmdLine.getOptionValue(OPT_LONG_USB)); - } - logger.debug("usb={}", getUsbVendorIdAndProductId()); + if (getTunnelingV2isSet()) + builder += String.format(" --%s", OPT_LONG_TUNNEL_V2); - if (cmdLine.getArgs().length > 0) { - setKnxInterface(cmdLine.getArgs()[0]); - } - else { - setKnxInterface(""); - } - logger.debug("knxInterface={}", getKnxInterface()); + if (getTunnelingV1isSet()) + builder += String.format(" --%s", OPT_LONG_TUNNEL_V1); - if (cmdLine.hasOption(OPT_LONG_USER_ID)) { - logger.debug("KNX IP Secure --{} is set", OPT_LONG_USER_ID); // log only that it´s, but not the actual value - setKnxSecureUserId(((Number)cmdLine.getParsedOptionValue(OPT_LONG_USER_ID)).intValue()); - } + if (getNatIsSet()) + builder += String.format(" --%s", OPT_LONG_NAT); - if (cmdLine.hasOption(OPT_LONG_USER_PASSWORD)) { - logger.debug("KNX IP Secure --{} is set", OPT_LONG_USER_PASSWORD); // log only that it´s, but not the actual value - setKnxSecureUserPassword(cmdLine.getOptionValue(OPT_LONG_USER_PASSWORD)); - } + if (getRoutingIsSet()) + builder += String.format(" --%s", OPT_LONG_ROUTING); - if (cmdLine.hasOption(OPT_LONG_DEVICE_PASSWORD)) { - logger.debug("KNX IP Secure --{} is set", OPT_LONG_DEVICE_PASSWORD); // log only that it´s, but not the actual value - setKnxSecureDevicePassword(cmdLine.getOptionValue(OPT_LONG_DEVICE_PASSWORD)); - } + if (getFlashingFullModeIsSet()) + builder += String.format(" --%s", OPT_LONG_FULL); - setLogStatisticsIsSet(cmdLine.hasOption(OPT_LONG_LOGSTATISTIC)); - logger.debug("logStatistics={}", getLogStatisticsIsSet()); + if (getHelpIsSet()) + builder += String.format(" --%s", OPT_LONG_HELP); - // some logical checks for options which exclude each other - // differential mode and eraseflash makes no sense - if (getEraseFullFlashIsSet() && (!getFlashingFullModeIsSet())) { - setFlashingFullModeIsSet(true); - logger.info("{}--{} is set. --> switching to full flash mode{}", ansi().fg(RED), - OPT_LONG_ERASEFLASH, ansi().reset()); - } + if (getVersionIsSet()) + builder += String.format(" --%s", OPT_LONG_VERSION); - // nat only possible with tunneling v1 - if (getNatIsSet() && (!getTunnelingV1isSet())) { - throw new CliInvalidException(String.format("%sOption --%s can only be used together with --%s%s", - ansi().fg(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ansi().reset())); - } + if (getNoFlashIsSet()) + builder += String.format(" --%s", OPT_LONG_NO_FLASH); - // nat not allowed with tunneling v2 - if (getNatIsSet() && (getTunnelingV2isSet())) { - throw new CliInvalidException(String.format("%sOption --%s can not be used together with --%s%s", - ansi().fg(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2, ansi().reset())); - } + if (getLogLevel() != defaultLogLevel) + builder += String.format(" --%s %s", OPT_LONG_LOGLEVEL, getLogLevel()); - // check IP-secure configuration - if (!(getKnxSecureUserPassword().isEmpty()) || !(getKnxSecureDevicePassword().isEmpty())) { - if (getKnxInterface().isBlank()) { - throw new CliInvalidException(String.format("%sNo IP-Interface specified for IP-secure%s", - ansi().fg(RED), ansi().reset())); - } - else if (!getUsbVendorIdAndProductId().isBlank()) { - throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_USB, ansi().reset())); - } - else if (!getFt12SerialPort().isBlank()) { - throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_FT12, ansi().reset())); - } - else if (!getTpuartSerialPort().isBlank()) { - throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_TPUART, ansi().reset())); - } - else if (getNatIsSet()) { - throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_NAT, ansi().reset())); - } - else if (getTunnelingV1isSet()) { - throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_TUNNEL_V1, ansi().reset())); - } - else if (getTunnelingV2isSet()) { - throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_TUNNEL_V2, ansi().reset())); - } - - // ensure that all three IP-Secure arguments are set - if ((getKnxSecureUserPassword().isEmpty()) || (getKnxSecureDevicePassword().isEmpty())) { - throw new CliInvalidException(String.format("%sFor IP-secure --%s, --%s and --%s must be set%s", - ansi().fg(RED), OPT_LONG_USER_ID, OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD, - ansi().reset())); - } - } + if (getPriority() != Priority.LOW) + builder += String.format(" --%s %s", OPT_LONG_PRIORITY, getPriority()); - int interfacesSet = 0; - if (!getKnxInterface().isBlank()) interfacesSet++; - if (getRoutingIsSet()) interfacesSet++; - if (!getFt12SerialPort().isBlank()) interfacesSet++; - if (!getTpuartSerialPort().isBlank()) interfacesSet++; - if (!getUsbVendorIdAndProductId().isBlank()) interfacesSet++; + if (getEraseFullFlashIsSet()) + builder += String.format(" --%s", OPT_LONG_ERASEFLASH); - if (interfacesSet > 1) { - throw new CliInvalidException(String.format("%sOnly one bus interface can be used.%s", - ansi().fg(RED), ansi().reset())); - } - else if (interfacesSet == 0) { - throw new CliInvalidException(String.format("%sNo bus interface specified.%s", - ansi().fg(RED), ansi().reset())); - } - } - catch (CliInvalidException e) { - logCliException(e, args, false); - System.exit(0); - } - catch (ParseException | KNXFormatException e) { - logCliException(e, args, true); - System.exit(0); - } - } + if (getDumpFlashStartAddress() >= 0) + builder += String.format(" --%s 0x%08X 0x%08X", OPT_LONG_DUMPFLASH, + getDumpFlashStartAddress(), getDumpFlashEndAddress()); - private void logCliException(Exception e, String[] args, boolean verbose) { - String cliParsed = argsToString(args); - System.out.printf("Invalid command line parameters: '%s'%s", cliParsed, - System.lineSeparator()); // don't log possible IP-secure parameters - logger.debug("Invalid command line parameters:"); - logger.error("{}{}{}", ansi().fg(RED), e.getMessage(), ansi().reset()); - logger.error("For more information about the usage start with --{}", OPT_LONG_HELP); - if (verbose) { - logger.error("", e); - } - } + if (getLogStatisticsIsSet()) + builder += String.format(" --%s", OPT_LONG_LOGSTATISTIC); - private String argsToString(final String[] args) { - StringBuilder result = new StringBuilder(); - for (String arg : args) { - result.append(String.format("%s ", arg)); - } - return result.toString().trim(); + if (getBlockSize() != Mcu.UPD_PROGRAM_SIZE) + builder += String.format(" --%s %s", OPT_LONG_BLOCKSIZE, getBlockSize()); + + return builder.trim(); } public String helpToString() { @@ -656,14 +706,16 @@ public String getKnxInterface() { private void setKnxInterface(String knxInterface) { this.knxInterface = knxInterface; + logger.debug("knxInterface={}", getKnxInterface()); } public String getFileName() { return fileName; } - private void setFileName(String fileName) { + public void setFileName(String fileName) { this.fileName = fileName; + logger.debug("{}={}", OPT_LONG_FILENAME, getFileName()); } public String getLocalhost() { @@ -672,6 +724,7 @@ public String getLocalhost() { private void setLocalhost(String localhost) { this.localhost = localhost; + logger.debug("{}={}", OPT_LONG_LOCALHOST, getLocalhost()); } public int getLocalPort() { @@ -679,7 +732,29 @@ public int getLocalPort() { } private void setLocalPort(int localPort) { - this.localPort = localPort; + if ((localPort >= 0) && (localPort <= 65535)) { + this.localPort = localPort; + logger.debug("{}={}", OPT_LONG_LOCALPORT, getLocalPort()); + } + else { + this.localPort = 0; + logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", + ansi().fg(RED), OPT_LONG_LOCALPORT, localPort, 0, 65535, this.localPort, ansi().reset()); + } + } + + private void setLocalPort(String localPort) { + try { + if (localPort == null || localPort.isBlank()) + setLocalPort(KNXnetIPConnection.DEFAULT_PORT); + else + setLocalPort(Integer.parseInt(localPort)); + } + catch (NumberFormatException e) { + setLocalPort(0); + logger.warn("{}option --{} {} is invalid => set to default {}{}", + ansi().fg(RED), OPT_LONG_LOCALPORT, localPort, getLocalPort(), ansi().reset()); + } } public int getPort() { @@ -687,7 +762,29 @@ public int getPort() { } private void setPort(int port) { - this.port = port; + if ((port >= 1) && (port <= 65535)) { + this.port = port; + logger.debug("{}={}", OPT_LONG_PORT, getPort()); + } + else { + this.port = KNXnetIPConnection.DEFAULT_PORT; + logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", + ansi().fg(RED), OPT_LONG_PORT, port, 1, 65535, this.port, ansi().reset()); + } + } + + private void setPort(String port) { + try { + if (port == null || port.isBlank()) + setPort(KNXnetIPConnection.DEFAULT_PORT); + else + setPort(Integer.parseInt(port)); + } + catch (NumberFormatException e) { + setPort(KNXnetIPConnection.DEFAULT_PORT); + logger.warn("{}option --{} {} is invalid => set to default {}{}", + ansi().fg(RED), OPT_LONG_PORT, port, getPort(), ansi().reset()); + } } public boolean getNatIsSet() { @@ -696,6 +793,7 @@ public boolean getNatIsSet() { private void setNatIsSet(boolean natIsSet) { this.natIsSet = natIsSet; + logger.debug("{}={}", OPT_LONG_NAT, getNatIsSet()); } public String getFt12SerialPort() { @@ -704,6 +802,7 @@ public String getFt12SerialPort() { private void setFt12SerialPort(String ft12SerialPort) { this.ft12SerialPort = ft12SerialPort; + logger.debug("{}={}", OPT_LONG_FT12, getFt12SerialPort()); } public String getTpuartSerialPort() { @@ -712,6 +811,7 @@ public String getTpuartSerialPort() { private void setTpuartSerialPort(String tpuartSerialPort) { this.tpuartSerialPort = tpuartSerialPort; + logger.debug("{}={}", OPT_LONG_TPUART, getTpuartSerialPort()); } public boolean getTunnelingV2isSet() { @@ -720,6 +820,7 @@ public boolean getTunnelingV2isSet() { private void setTunnelingV2isSet(boolean tunnelingV2isSet) { this.tunnelingV2isSet = tunnelingV2isSet; + logger.debug("{}={}", OPT_LONG_TUNNEL_V2, getTunnelingV2isSet()); } public boolean getTunnelingV1isSet() { @@ -728,6 +829,7 @@ public boolean getTunnelingV1isSet() { private void setTunnelingV1isSet(boolean tunnelingV1isSet) { this.tunnelingV1isSet = tunnelingV1isSet; + logger.debug("{}={}", OPT_LONG_TUNNEL_V1, getTunnelingV2isSet()); } public boolean getRoutingIsSet() { @@ -736,6 +838,7 @@ public boolean getRoutingIsSet() { private void setRoutingIsSet(boolean routingIsSet) { this.routingIsSet = routingIsSet; + logger.debug("{}={}", OPT_LONG_ROUTING, getRoutingIsSet()); } public String getMedium() { @@ -744,38 +847,58 @@ public String getMedium() { private void setMedium(String medium) { this.medium = medium; + logger.debug("{}={}", OPT_LONG_MEDIUM, getMedium()); } public IndividualAddress getProgDevicePhysicalAddress() { return progDevicePhysicalAddress; } - private void setProgDevicePhysicalAddress(IndividualAddress progDevicePhysicalAddress){ - this.progDevicePhysicalAddress = progDevicePhysicalAddress; + private void setProgDevicePhysicalAddress(String progDevicePhysicalAddress) throws KNXFormatException { + if ((progDevicePhysicalAddress != null) && (!progDevicePhysicalAddress.isBlank())) { + this.progDevicePhysicalAddress = new IndividualAddress(progDevicePhysicalAddress); + } + else { + this.progDevicePhysicalAddress = Updater.PHYS_ADDRESS_BOOTLOADER; + } + logger.debug("{}={}", OPT_LONG_PROG_DEVICE, getProgDevicePhysicalAddress()); } public IndividualAddress getDevicePhysicalAddress() { return devicePhysicalAddress; } - private void setDevicePhysicalAddress(IndividualAddress devicePhysicalAddress) { - this.devicePhysicalAddress = devicePhysicalAddress; + private void setDevicePhysicalAddress(String devicePhysicalAddress) throws KNXFormatException { + if ((devicePhysicalAddress != null) && (!devicePhysicalAddress.isBlank())) { + this.devicePhysicalAddress = new IndividualAddress(devicePhysicalAddress); + } + else { + this.devicePhysicalAddress = null; + } + logger.debug("{}={}", OPT_LONG_DEVICE, getDevicePhysicalAddress()); } public IndividualAddress getOwnPhysicalAddress() { return ownPhysicalAddress; } - private void setOwnPhysicalAddress(IndividualAddress ownPhysicalAddress) { - this.ownPhysicalAddress = ownPhysicalAddress; + private void setOwnPhysicalAddress(String ownPhysicalAddress) throws KNXFormatException { + if ((ownPhysicalAddress != null) && (!ownPhysicalAddress.isBlank())) { + this.ownPhysicalAddress = new IndividualAddress(ownPhysicalAddress); + } + else { + this.ownPhysicalAddress = Updater.PHYS_ADDRESS_OWN; + } + logger.debug("{}={}", OPT_LONG_OWN_ADDRESS, getOwnPhysicalAddress()); } public String getUid() { return uid; } - private void setUid(String uidString) { + public void setUid(String uidString) { this.uid = uidString; + logger.debug("{}={}", OPT_LONG_UID, getUid()); } public boolean getFlashingFullModeIsSet() { @@ -784,6 +907,7 @@ public boolean getFlashingFullModeIsSet() { private void setFlashingFullModeIsSet(boolean flashingFullModeIsSet) { this.flashingFullModeIsSet = flashingFullModeIsSet; + logger.debug("{}={}", OPT_LONG_FULL, getFlashingFullModeIsSet()); } public int getDelayMs() { @@ -794,10 +918,25 @@ private void setDelayMs(int delayMs) { if ((delayMs < Updater.DELAY_MIN) || (delayMs > Updater.DELAY_MAX)) { logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", ansi().fg(RED), OPT_LONG_DELAY, delayMs, Updater.DELAY_MIN, - Updater.DELAY_MAX, Updater.DELAY_DEFAULT, ansi().reset()); - delayMs = Updater.DELAY_DEFAULT; // set to DELAY_DEFAULT in case of invalid waiting time + Updater.DELAY_MAX, Updater.DELAY_MIN, ansi().reset()); + delayMs = Updater.DELAY_MIN; // set to DELAY_MIN in case of invalid waiting time } this.delayMs = delayMs; + logger.debug("{}={}", OPT_LONG_DELAY, getDelayMs()); + } + + private void setDelayMs(String delayMs) { + try { + if (delayMs == null || delayMs.isBlank()) + setDelayMs(Updater.DELAY_MIN); + else + setDelayMs(Integer.parseInt(delayMs)); + } + catch (NumberFormatException e) { + setDelayMs(Updater.DELAY_MIN); + logger.warn("{}option --{} {} is invalid => set to default {}{}", + ansi().fg(RED), OPT_LONG_DELAY, delayMs, getDelayMs(), ansi().reset()); + } } public boolean getNoFlashIsSet() { @@ -806,6 +945,7 @@ public boolean getNoFlashIsSet() { private void setNoFlashIsSet(boolean noFlashIsSet) { this.noFlashIsSet = noFlashIsSet; + logger.debug("{}={}", OPT_LONG_NO_FLASH, getNoFlashIsSet()); } public boolean getEraseFullFlashIsSet() { @@ -814,6 +954,7 @@ public boolean getEraseFullFlashIsSet() { private void setEraseFullFlashIsSet(boolean eraseFullFlashIsSet) { this.eraseFullFlashIsSet = eraseFullFlashIsSet; + logger.debug("{}={}", OPT_LONG_ERASEFLASH, getEraseFullFlashIsSet()); } public long getDumpFlashStartAddress() { @@ -822,6 +963,7 @@ public long getDumpFlashStartAddress() { private void setDumpFlashStartAddress(long dumpFlashStartAddress) { this.dumpFlashStartAddress = dumpFlashStartAddress; + logger.debug("dumpFlashStartAddress={}", getDumpFlashStartAddress()); } public long getDumpFlashEndAddress() { @@ -830,14 +972,21 @@ public long getDumpFlashEndAddress() { private void setDumpFlashEndAddress(long dumpFlashEndAddress) { this.dumpFlashEndAddress = dumpFlashEndAddress; + logger.debug("dumpFlashEndAddress={}", getDumpFlashEndAddress()); } public boolean getHelpIsSet() { - return cmdLine.hasOption(OPT_SHORT_HELP); + if (cmdLine == null) + return false; + else + return cmdLine.hasOption(OPT_LONG_HELP); } public boolean getVersionIsSet() { - return cmdLine.hasOption(OPT_SHORT_VERSION); + if (cmdLine == null) + return false; + else + return cmdLine.hasOption(OPT_LONG_VERSION); } public Level getLogLevel() { @@ -845,9 +994,21 @@ public Level getLogLevel() { return root.getLevel(); } - private void setLogLevel(Level newLevel) { + private void setLogLevel(Level logLevel) { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - root.setLevel(newLevel); + root.setLevel(logLevel); + logger.debug("{}={}", OPT_LONG_LOGLEVEL, getLogLevel()); + } + + private void setLogLevel(String newLevel) { + newLevel = newLevel.toUpperCase(); + if (VALID_LOG_LEVELS.contains(newLevel)) { + setLogLevel(Level.toLevel(newLevel)); + } + else { + logger.warn("{}invalid {} {}, using {}{}", ansi().fg(RED), OPT_LONG_LOGLEVEL, newLevel, + getLogLevel().toString(), ansi().reset()); + } } public int getKnxSecureUserId() { @@ -855,7 +1016,36 @@ public int getKnxSecureUserId() { } private void setKnxSecureUserId(int knxSecureUserId) { - this.knxSecureUserId = knxSecureUserId; + if (knxSecureUserId == INVALID_SECURE_USER_ID) { + this.knxSecureUserId = INVALID_SECURE_USER_ID; + logger.debug("{}=", OPT_LONG_USER_ID); + return; + } + + if (knxSecureUserId >= 1 && knxSecureUserId <= 127) { + logger.debug("{}=***", OPT_LONG_USER_ID); // log only that it´s, but not the actual value + this.knxSecureUserId = knxSecureUserId; + } + else { + logger.warn("{}option --{} *** is invalid => set to default{}", + ansi().fg(RED), OPT_LONG_USER_ID, ansi().reset()); + this.knxSecureUserId = INVALID_SECURE_USER_ID; + logger.debug("{}=", OPT_LONG_USER_ID); + } + } + + private void setKnxSecureUserId(String knxSecureUserId) { + try { + if (knxSecureUserId == null || knxSecureUserId.isBlank()) + setKnxSecureUserId(INVALID_SECURE_USER_ID); + else + setKnxSecureUserId(Integer.parseInt(knxSecureUserId)); + } + catch (NumberFormatException e) { + setKnxSecureUserId(INVALID_SECURE_USER_ID); + logger.warn("{}option --{} *** is invalid => set to default{}", + ansi().fg(RED), OPT_LONG_USER_ID, ansi().reset()); + } } public String getKnxSecureUserPassword() { @@ -864,6 +1054,10 @@ public String getKnxSecureUserPassword() { private void setKnxSecureUserPassword(String knxSecureUserPassword) { this.knxSecureUserPassword = knxSecureUserPassword; + if (!nonNullString(this.knxSecureUserPassword).isBlank()) + logger.debug("{}=****", OPT_LONG_USER_PASSWORD); // log only that it´s, but not the actual value + else + logger.debug("{}=", OPT_LONG_USER_PASSWORD); } public String getKnxSecureDevicePassword() { @@ -872,14 +1066,25 @@ public String getKnxSecureDevicePassword() { private void setKnxSecureDevicePassword(String knxSecureDevicePassword) { this.knxSecureDevicePassword = knxSecureDevicePassword; + if (!nonNullString(this.knxSecureDevicePassword).isBlank()) + logger.debug("{}=*****", OPT_LONG_DEVICE_PASSWORD); // log only that it´s, but not the actual value + else + logger.debug("{}=", OPT_LONG_DEVICE_PASSWORD); } public Priority getPriority() { return priority; } - private void setPriority(Priority priority) { - this.priority = priority; + private void setPriority(String priority) { + try { + this.priority = Priority.get(priority); + logger.debug("{}={}", OPT_LONG_PRIORITY, getPriority().toString()); + } + catch (KNXIllegalArgumentException e) { + logger.warn("{}invalid --{} {}, using {}{}",ansi().fg(RED), OPT_LONG_PRIORITY, + priority, getPriority(), ansi().reset()); + } } public boolean getLogStatisticsIsSet() { @@ -888,6 +1093,7 @@ public boolean getLogStatisticsIsSet() { private void setLogStatisticsIsSet(boolean logStatisticsIsSet) { this.logStatisticsIsSet = logStatisticsIsSet; + logger.debug("{}={}", OPT_LONG_LOGSTATISTIC , getLogStatisticsIsSet()); } public int getBlockSize() { @@ -895,14 +1101,37 @@ public int getBlockSize() { } private void setBlockSize(int blockSize) { - this.blockSize = blockSize; + if (VALID_BLOCKSIZES.contains(blockSize)) { + this.blockSize = blockSize; + } + else { + this.blockSize = Mcu.UPD_PROGRAM_SIZE; + logger.info("{}--{} {} is not supported => Set --{} to default {} bytes{}", ansi().fg(YELLOW), + OPT_LONG_BLOCKSIZE, blockSize, OPT_LONG_BLOCKSIZE, getBlockSize(), ansi().reset()); + } + logger.debug("{}={}", OPT_LONG_BLOCKSIZE, getBlockSize()); + } + + private void setBlockSize(String blockSize) { + try { + if (blockSize == null || blockSize.isBlank()) + setBlockSize(Mcu.UPD_PROGRAM_SIZE); + else + setBlockSize(Integer.parseInt(blockSize)); + } + catch (NumberFormatException e) { + setBlockSize(Mcu.UPD_PROGRAM_SIZE); + logger.warn("{}option --{} {} is invalid => set to default {}{}", + ansi().fg(RED), OPT_LONG_BLOCKSIZE, blockSize, getBlockSize(), ansi().reset()); + } } public String getUsbVendorIdAndProductId() { return nonNullString(usbVendorIdAndProductId); } - public void setUsbVendorIdAndProductId(String usbVendorIdAndProductId) { + private void setUsbVendorIdAndProductId(String usbVendorIdAndProductId) { this.usbVendorIdAndProductId = usbVendorIdAndProductId; + logger.debug("{}={}", OPT_LONG_USB, getUsbVendorIdAndProductId()); } } \ No newline at end of file diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index e0be393b..890b206e 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -94,17 +94,16 @@ public void restartProgrammingDevice() mc.restart(progDestination); } - private void waitRestartTime(int restartTimeSeconds) { - try { - while (restartTimeSeconds > 0) { - Thread.sleep(1000); + private void waitRestartTime(int restartTimeSeconds) throws InterruptedException { + while (restartTimeSeconds > 0) { + Thread.sleep(1000); + if (LoggingManager.isConsoleActive()) { System.out.printf("%s.%s", ansi().fgBright(GREEN), ansi().reset()); - restartTimeSeconds--; } - System.out.println(); + restartTimeSeconds--; } - catch (final InterruptedException e) { - logger.error("InterruptedException ", e); + if (LoggingManager.isConsoleActive()) { + System.out.println(); } } @@ -113,7 +112,7 @@ private void waitRestartTime(int restartTimeSeconds) { * * @param device the IndividualAddress of the device to restart */ - public void restartDeviceToBootloader(IndividualAddress device) { + public void restartDeviceToBootloader(IndividualAddress device) throws InterruptedException { int restartProcessTime = Mcu.DEFAULT_RESTART_TIME_SECONDS; try (Destination dest = this.mc.createDestination(device, true, false, false)) { logger.info("Restarting device {} into bootloader", device); @@ -121,10 +120,10 @@ public void restartDeviceToBootloader(IndividualAddress device) { logger.info("Device {} reported {}{}{} second(s) for restarting", device, ansi().fgBright(GREEN), restartProcessTime, ansi().reset()); waitRestartTime(restartProcessTime); - } catch (final KNXException | InterruptedException e) { + } catch (final KNXException e) { logger.info("{}Restart state of device {} unknown. {}{}", ansi().fgBright(RED), device, e.getMessage(), ansi().reset()); - logger.debug("KNXException ", e); + logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 logger.info("Waiting {}{}{} seconds for device {} to restart", ansi().fgBright(GREEN), restartProcessTime, ansi().reset(), device); waitRestartTime(restartProcessTime); @@ -208,7 +207,8 @@ public BootDescriptor requestBootDescriptor() return bootDescriptor; } - public String requestAppVersionString() throws UpdaterException { + public String requestAppVersionString() throws UpdaterException, KNXLinkClosedException, + InterruptedException { byte[] result = sendWithRetry(UPDCommand.APP_VERSION_REQUEST, new byte[0], getMaxUpdCommandRetry()).data(); UPDCommand command = UPDCommand.tryFromByteArray(result); if (command != UPDCommand.APP_VERSION_RESPONSE) { @@ -324,14 +324,17 @@ public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { } progressInfo.update(bytesDone); - // console output + String logText = String.format("%s%s%s%s", AnsiCursor.off(), ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset(), progressInfo, AnsiCursor.on()); - System.out.print(logText); + // console output + if (LoggingManager.isConsoleActive()) { + System.out.print(logText); + } // gui JTextPane output logText = ansi().cursorUpLine().toString() + logText; // need this CursorUp in gui ListTextAppenders.appendEvent(Level.INFO, logText); @@ -379,7 +382,8 @@ public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) } } - public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException { + public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, KNXLinkClosedException, + InterruptedException { logger.debug("Requesting Bootloader statistic"); byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); UPDCommand command = UPDCommand.tryFromByteArray(result); @@ -394,7 +398,7 @@ public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException } public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetry) - throws UpdaterException { + throws UpdaterException, InterruptedException, KNXLinkClosedException { ResponseResult result = new ResponseResult(); while (true) { try { @@ -407,13 +411,14 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr e.getClass().getSimpleName(), ansi().reset()); result.incTimeoutCount(); } - catch (KNXDisconnectException | KNXRemoteException e) { ///\todo check causes of KNXRemoteException, if think they are unrecoverable + catch (KNXDisconnectException e) { ///\todo check causes of KNXRemoteException, if think they are unrecoverable logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), e.getClass().getSimpleName(), ansi().reset()); result.incDropCount(); } - catch (Throwable e) { - throw new UpdaterException(String.format("%s failed.", command), e); + catch (KNXInvalidResponseException e) { + logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), + e.getClass().getSimpleName(), ansi().reset()); } if (maxRetry > 0) { @@ -427,7 +432,8 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr } } - public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throws UpdaterException { + public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throws UpdaterException, + InterruptedException { try { ManagementProcedures mgmt = new ManagementProceduresImpl(link); IndividualAddress[] devices = mgmt.readAddress(); @@ -447,8 +453,8 @@ else if ((devices.length == 1) && (progDeviceAddr != null) && (progDeviceAddr.eq else { throw new UpdaterException(String.format("%d wrong device(s) %s are already in programming mode.", devices.length, Arrays.toString(devices))); } - } catch (KNXException | InterruptedException e ) { - throw new UpdaterException(String.format("checkDevicesInProgrammingMode failed. %s", e.getMessage())); + } catch (KNXException e ) { + throw new UpdaterException(String.format("checkDevicesInProgrammingMode failed. %s", e.getMessage()), e); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java index 8655d778..6215689f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -19,21 +19,22 @@ public class DiscoverKnxInterfaces { private final static Logger logger = LoggerFactory.getLogger(DiscoverKnxInterfaces.class); - public static List> getAllnetIPInterfaces() { + public static List> getAllnetIPInterfaces() throws InterruptedException { List> interfacesResult = null; try { // set true to be aware of Network Address Translation (NAT) during discovery final boolean useNAT = false; interfacesResult = Discoverer.udp(useNAT).timeout(Duration.ofSeconds(3)).search().get(); - }catch (InterruptedException | ExecutionException e) { + } + catch (ExecutionException e) { logger.warn("Error during KNXnet/IP discovery: {}", e.getMessage()); } return interfacesResult; } - public static Set getUsbInterfaces() { + public static Set getUsbInterfaces() throws InterruptedException { Set knxUsbDevices = UsbConnectionFactory.attachedKnxUsbDevices(); Iterator iterator = knxUsbDevices.iterator(); while (iterator.hasNext()) { @@ -43,10 +44,9 @@ public static Set getUsbInterfaces() { try (UsbConnection c = UsbConnectionFactory.open(d)) { if (c.deviceDescriptor().medium().getMedium() != KNXMediumSettings.MEDIUM_TP1) { iterator.remove(); - continue; - }; + } } - catch (KNXException | InterruptedException | RuntimeException e) { + catch (KNXException e) { iterator.remove(); logger.error("error: reading KNX device descriptor of {} ({})", d, e.getMessage()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java new file mode 100644 index 00000000..232ec313 --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java @@ -0,0 +1,86 @@ +package org.selfbus.updater; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.filter.ThresholdFilter; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public final class LoggingManager { + private final static Logger logger = LoggerFactory.getLogger(LoggingManager.class); + + + // In case CONSOLE_APPENDER_NAME is changed, + // change also CONSOLE appender name in src/resources/logback.xml + public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; + + public static void disableAppender(String appenderName) { + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); + + if (appender == null) { + logger.error("disableAppender: appender {} not found.", appenderName); + return; + } + + logger.trace("Stopping and disabling {} appender.", appenderName); + appender.stop(); + loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(appenderName); + logger.trace("{} appender has been disabled.", appenderName); + } + + public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); + + if (appender == null) { + logger.error("setAppenderLogLevel: appender {} not found.", appenderName); + return; + } + + appender.clearAllFilters(); + + ThresholdFilter thresholdFilter = new ThresholdFilter(); + thresholdFilter.setLevel(newLogLevel.toString()); + thresholdFilter.setContext(loggerContext); + thresholdFilter.start(); + + appender.addFilter(thresholdFilter); + logger.trace("{} appender log level set to: {}", appenderName, newLogLevel); + } + + public static Level getAppenderLogLevel(String appenderName) { + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName.toUpperCase()); + + if (appender == null) { + return null; + } + + final Level[] testLevels = {Level.INFO, Level.DEBUG, Level.WARN, Level.ERROR, Level.TRACE}; + + for (Filter filter : appender.getCopyOfAttachedFiltersList()) { + if (filter instanceof ThresholdFilter thresholdFilter) { + // Test with different levels to infer the threshold + for (Level testLevel : testLevels) { + LoggingEvent testEvent = new LoggingEvent(); + testEvent.setLevel(testLevel); + if (thresholdFilter.decide(testEvent) != FilterReply.DENY) { + return testLevel; + } + } + } + } + return null; + } + + public static boolean isConsoleActive() { + return (LoggingManager.getAppenderLogLevel(CONSOLE_APPENDER_NAME) == Level.INFO); + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index 51d11fcd..99b4b330 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -4,7 +4,6 @@ import org.slf4j.LoggerFactory; import tuwien.auto.calimero.IndividualAddress; import tuwien.auto.calimero.KNXException; -import tuwien.auto.calimero.KNXIllegalArgumentException; import tuwien.auto.calimero.knxnetip.KNXnetIPRouting; import tuwien.auto.calimero.knxnetip.SecureConnection; import tuwien.auto.calimero.knxnetip.TcpConnection; @@ -29,10 +28,15 @@ public class SBKNXLink { private static final Logger logger = LoggerFactory.getLogger(SBKNXLink.class); private CliOptions cliOptions; - public SBKNXLink() { + @SuppressWarnings("unused") + private SBKNXLink() { } + public SBKNXLink(CliOptions cliOptions) { + this.cliOptions = cliOptions; + } + private KNXNetworkLink createSecureTunnelingLink(InetSocketAddress local, InetSocketAddress remote, KNXMediumSettings medium) throws KNXException, InterruptedException { // KNX IP Secure TCP tunneling v2 connection @@ -76,25 +80,23 @@ private static InetSocketAddress createLocalSocket(String host, Integer port) { } } - private static KNXMediumSettings getMedium(final String id, IndividualAddress ownAddress) { - if (id.equals("tp1")) { + private static KNXMediumSettings getMedium(final String id, IndividualAddress ownAddress) + throws UpdaterException { + if (id.equalsIgnoreCase(KNXMediumSettings.getMediumString(KNXMediumSettings.MEDIUM_TP1))) { return new TPSettings(ownAddress); - } else if (id.equals("rf")) { - throw new KNXIllegalArgumentException("medium 'rf' not implemented"); + } + else if (id.equalsIgnoreCase(KNXMediumSettings.getMediumString(KNXMediumSettings.MEDIUM_RF))) { + throw new UpdaterException(String.format("medium '%s' not implemented", id)); //return new RFSettings(ownAddress); } - //else if (id.equals("tp0")) + //else if (id.equalsIgnoreCase("tp0")) // return TPSettings.TP0; - //else if (id.equals("p110")) + //else if (id.equalsIgnoreCase("p110")) // return new PLSettings(false); - //else if (id.equals("p132")) + //else if (id.equalsIgnoreCase("p132")) // return new PLSettings(true); else - throw new KNXIllegalArgumentException("unknown medium"); - } - - public void setCliOptions(CliOptions cliOptions) { - this.cliOptions = cliOptions; + throw new UpdaterException("unknown medium"); } private InetAddress resolveHost(final String host) throws UnknownHostException { @@ -112,14 +114,15 @@ private InetAddress resolveHost(final String host) throws UnknownHostException { * @throws KNXException on problems on link creation * @throws InterruptedException on interrupted thread */ - public KNXNetworkLink openLink() throws KNXException, InterruptedException, UnknownHostException { + public KNXNetworkLink openLink() throws KNXException, InterruptedException, + UnknownHostException, UpdaterException { KNXNetworkLink newLink = doOpenLink(); logger.info("KNX connection: {}", newLink); return newLink; } private KNXNetworkLink doOpenLink() throws KNXException, - InterruptedException, UnknownHostException { + InterruptedException, UnknownHostException, UpdaterException { final KNXMediumSettings medium = getMedium(cliOptions.getMedium(), cliOptions.getOwnPhysicalAddress()); logger.debug("Creating KNX network link {}", medium); if (!cliOptions.getFt12SerialPort().isEmpty()) { @@ -160,14 +163,14 @@ private KNXNetworkLink doOpenLink() throws KNXException, // try unsecure TCP tunneling v2 connection try { return createTunnelingLinkV2(local, remote, medium); - } catch (final KNXException | InterruptedException e) { + } catch (final KNXException e) { logger.info("failed with {}", e.toString()); } // try unsecure UDP tunneling v1 connection with nat option set on cli try { return createTunnelingLinkV1(local, remote, cliOptions.getNatIsSet(), medium); - } catch (final KNXException | InterruptedException e) { + } catch (final KNXException e) { logger.info("{}failed with {}{}", ansi().fg(YELLOW), e, ansi().reset()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index ca2a0007..2d0fbf30 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -40,13 +40,14 @@ private byte[] prepareAsdu(final int cmd, final byte[] data) { // for Selfbus updater public byte[] sendUpdateData(final Destination dst, final int cmd, final byte[] data) - throws KNXTimeoutException, KNXLinkClosedException, KNXInvalidResponseException, KNXDisconnectException, InterruptedException, UpdaterException { + throws KNXTimeoutException, KNXLinkClosedException, KNXInvalidResponseException, KNXDisconnectException, + InterruptedException, UpdaterException { final byte[] asdu = prepareAsdu(cmd, data); byte[] send; if (asdu.length > MAX_ASDU_LENGTH) { // prevent creation of long frames, sblib doesn't support them yet - throw new UpdaterException(String.format("asdu.length (%d) exceeds MAX_ASDU_LENGTH(%d)", + throw new UpdaterException(String.format("asdu.length (%d) exceeds MAX_ASDU_LENGTH (%d)", asdu.length, MAX_ASDU_LENGTH)); } send = DataUnitBuilder.createAPDU(apciWrite, asdu); diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 5f3f409c..e1668f90 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -76,4 +76,8 @@ public static String minVersionBootloader() { minMinorVersionBootloader(), 0, 0, 0, 0).getVersion(); } + + public static String getToolJarName() { + return String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()); + } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index c8a87390..abfd94cb 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -1,5 +1,7 @@ package org.selfbus.updater; +import ch.qos.logback.classic.Level; +import org.apache.commons.cli.ParseException; import org.fusesource.jansi.AnsiConsole; import org.selfbus.updater.bootloader.BootloaderStatistic; import tuwien.auto.calimero.*; @@ -11,11 +13,11 @@ import org.slf4j.LoggerFactory; import tuwien.auto.calimero.link.KNXNetworkLink; -import java.io.IOException; import java.net.UnknownHostException; import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.LoggingManager.CONSOLE_APPENDER_NAME; import static org.selfbus.updater.Utils.shortenPath; import org.selfbus.updater.gui.GuiMain; @@ -38,7 +40,6 @@ private Updater() {} // disable default constructor private final static Logger logger = LoggerFactory.getLogger(Updater.class); private CliOptions cliOptions = null; - private SBKNXLink sbKNXLink = null; /** * Constructs an instance of the {@link #Updater} class. @@ -49,8 +50,6 @@ public Updater(CliOptions cliOptions) { logger.debug(ToolInfo.getFullInfo()); logger.debug(Settings.getLibraryHeader(false)); this.cliOptions = cliOptions; - this.sbKNXLink = new SBKNXLink(); - this.sbKNXLink.setCliOptions(cliOptions); } public static void initJansi() { @@ -63,60 +62,58 @@ public static void finalizeJansi() { } public static void main(final String[] args) { + // turn off info level message + // "SLF4J(I): Connected with provider of type [ch.qos.logback.classic.spi.LogbackServiceProvider]" + // see https://github.com/qos-ch/slf4j/issues/422 + // todo test end delete with logback-classic (SLF4J) version >= 2.0.16 + // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic + // https://github.com/qos-ch/slf4j/issues/422#issuecomment-2280712623 + System.setProperty("slf4j.internal.verbosity", "WARN"); + initJansi(); if (args.length == 0) { + logger.info("GUI start => console output set to log level {}", Level.WARN); + LoggingManager.setAppenderLogLevel(CONSOLE_APPENDER_NAME, Level.WARN); GuiMain.startSwingGui(); finalizeJansi(); return; } - // read in user-supplied command line options - CliOptions options = new CliOptions(args, String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()) , - "Selfbus KNX-Firmware update tool options", "", PHYS_ADDRESS_BOOTLOADER, PHYS_ADDRESS_OWN); - if (options.getVersionIsSet()) { - logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), Credits.getAsciiLogo(), ansi().reset()); - logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), Credits.getAuthors(), ansi().reset()); - ToolInfo.showVersion(); - finalizeJansi(); - return; - } + try { + logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), ToolInfo.getToolAndVersion(), ansi().reset()); + // read in user-supplied command line options + CliOptions options = new CliOptions(args, ToolInfo.getToolJarName() , + "Selfbus KNX-Firmware update tool options", ""); + if (options.getVersionIsSet()) { + logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), Credits.getAsciiLogo(), ansi().reset()); + logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), Credits.getAuthors(), ansi().reset()); + ToolInfo.showVersion(); + finalizeJansi(); + return; + } + + if (options.getHelpIsSet()) { + logger.info(options.helpToString()); + finalizeJansi(); + return; + } - logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), ToolInfo.getToolAndVersion(), ansi().reset()); - if (options.getHelpIsSet()) { - logger.info(options.helpToString()); - finalizeJansi(); - return; - } - try { final Updater updater = new Updater(options); final ShutdownHandler shutDownHandler = new ShutdownHandler().register(); updater.run(); shutDownHandler.unregister(); - } finally { + } + catch (KNXFormatException | ParseException e) { + logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + } + finally { finalizeJansi(); logger.debug("main exit"); } } - /** - * Called by this tool on completion. - * - * @param thrown - * the thrown exception if operation completed due to a raised - * exception, null otherwise - * @param canceled - * whether the operation got canceled before its planned end - */ - protected void onCompletion(final Exception thrown, final boolean canceled) { - if (canceled) - logger.info("reading device info canceled"); - if (thrown != null) { - logger.error("Operation did not finish.", thrown); - } - } - private static final class ShutdownHandler extends Thread { private final Thread t = Thread.currentThread(); @@ -142,10 +139,6 @@ public void run() { * Maximum delay between two UPDCommand.SEND_DATA telegrams in milliseconds */ public static final int DELAY_MAX = 500; - /** - * Default delay between two UPDCommand.SEND_DATA telegrams in milliseconds - */ - public static final int DELAY_DEFAULT = 100; /** * Physical address the bootloader is using */ @@ -161,8 +154,6 @@ public void run() { * @see java.lang.Runnable#run() */ public void run() { - Exception thrown = null; - boolean canceled = false; KNXNetworkLink link = null; try { final String hexFileName = cliOptions.getFileName(); @@ -191,7 +182,7 @@ public void run() { System.out.println(); } - link = sbKNXLink.openLink(); + link = new SBKNXLink(cliOptions).openLink(); DeviceManagement dm = new DeviceManagement(link, cliOptions.getProgDevicePhysicalAddress(), cliOptions.getPriority()); @@ -349,26 +340,28 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres ansi().reset()); Thread.sleep(BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS); } - } catch (final KNXException | UpdaterException | RuntimeException e) { - thrown = e; - } catch (final InterruptedException e) { - canceled = true; + + logger.info("Operation finished successfully."); + } + catch (final InterruptedException e) { Thread.currentThread().interrupt(); - } catch (IOException e) { - logger.error("IOException ", e); - } catch (Throwable e) { - logger.error("Throwable ", e); - } finally { - if (link != null) + logger.info("Operation canceled."); + } + catch (Throwable e) { + logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + logger.error("Operation did not finish."); + } + finally { + if (link != null) { link.close(); - onCompletion(thrown, canceled); + } } } - public String requestUid(){ + public String requestUid() throws KNXException, UpdaterException, UnknownHostException { KNXNetworkLink link; try { - link = this.sbKNXLink.openLink(); + link = new SBKNXLink(cliOptions).openLink(); DeviceManagement dm = new DeviceManagement(link, cliOptions.getProgDevicePhysicalAddress(), cliOptions.getPriority()); //for option --device restart the device in bootloader mode @@ -381,15 +374,32 @@ public String requestUid(){ String uid = dm.requestUIDFromDevice(); + dm.unlockDeviceWithUID(uid); + dm.requestBootloaderIdentity(); + dm.requestBootDescriptor(); + String appVersion = dm.requestAppVersionString(); + if (appVersion.isEmpty()) { + logger.info("APP_VERSION: {}invalid{} ", ansi().fgBright(RED), ansi().reset()); + } else { + logger.info("APP_VERSION: {}{}{}", ansi().fgBright(GREEN), appVersion, ansi().reset()); + } + if (cliOptions.getDevicePhysicalAddress() != null) { dm.restartProgrammingDevice(); } dm.close(); link.close(); return uid; - - } catch (InterruptedException | UpdaterException | KNXException | UnknownHostException e) { - throw new RuntimeException(e); + } + catch (final InterruptedException e) { + logger.info("Operation requestUid canceled."); + Thread.currentThread().interrupt(); + return ""; + } + catch (UpdaterException | KNXException | UnknownHostException e) { + logger.error("{}An error occurred while retrieving the UID. {}{}{}", + ansi().fg(RED), System.lineSeparator(), e, ansi().reset()); + throw e; } } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java b/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java index b4d1455c..6763010f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java @@ -2,7 +2,6 @@ import javax.swing.JTextField; import javax.swing.JComboBox; -import java.util.ArrayList; import java.util.Objects; public final class CliConverter { @@ -21,16 +20,6 @@ public static String argument(String cliLongOption) { return argument(cliLongOption, "", false); } - public static String toString(ArrayList list) { - StringBuilder result = new StringBuilder(); - for (String s : list) { - if (!s.isEmpty()) { - result.append(" ").append(s); - } - } - return result.toString().trim(); - } - public static String argument(String cliLongOption, String argValue, boolean argValueRequired) { cliLongOption = cliLongOption.trim(); argValue = argValue.trim(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 13a0c8b5..bfcb1c3f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -3,14 +3,16 @@ import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; +import org.apache.commons.cli.ParseException; import org.selfbus.updater.*; -import tuwien.auto.calimero.KNXIllegalArgumentException; +import tuwien.auto.calimero.*; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; +import tuwien.auto.calimero.link.medium.KNXMediumSettings; +import tuwien.auto.calimero.serial.usb.Device; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.serial.usb.Device; import javax.imageio.ImageIO; import javax.swing.*; @@ -24,10 +26,13 @@ import java.io.InputStream; import java.lang.reflect.Method; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.*; import java.util.List; import static java.awt.Font.PLAIN; +import static org.fusesource.jansi.Ansi.Color.*; +import static org.fusesource.jansi.Ansi.ansi; import static org.selfbus.updater.CliOptions.*; import static org.selfbus.updater.gui.CliConverter.argument; import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultBackgroundColor; @@ -89,7 +94,6 @@ public class GuiMain extends JFrame { private JScrollPane mainScrollPane; private JButton reloadGatewaysButton; - private CliOptions cliOptions; private Thread updaterThread; private final static Logger logger = LoggerFactory.getLogger(GuiMain.class); private final GuiSettings guiSettings = new GuiSettings(this); @@ -108,19 +112,33 @@ private boolean getUpdaterIsRunning() { } private void handleStartStopFlashAction() { - if (getUpdaterIsRunning()) { - updaterThread.interrupt(); + if (getUpdaterIsRunning() && (updaterThread != null)) { + while (updaterThread.getState() != Thread.State.TERMINATED) { + updaterThread.interrupt(); + } + updaterThread = null; updaterFinished(); - logger.info(getTranslation("logMessageCanceledFlashing")); return; } jLoggingPane.setText(""); updaterThread = new Thread(() -> { - setCliOptions(); - final Updater updater = new Updater(cliOptions); - updater.run(); - SwingUtilities.invokeLater(this::updaterFinished); //todo implement callback in Updater when done + try { + Thread.currentThread().setName("guiUpdater"); + CliOptions cliOptions = getCliOptions(); + displayCommandLine(cliOptions); + final Updater updater = new Updater(cliOptions); + updater.run(); + } catch (KNXFormatException | ParseException e) { + logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + String message = String.format(getTranslation("Exception.handleStartStopFlashAction.Message"), + e.getMessage()); + JOptionPane.showMessageDialog(this, + message, getTranslation("Error"), JOptionPane.ERROR_MESSAGE); + } finally { + SwingUtilities.invokeLater(this::updaterFinished); + } + }); updaterThread.start(); buttonStartStopFlash.setText(getTranslation("stopFlash")); @@ -162,24 +180,48 @@ private void handleLoadFileAction() { } private void handleRequestUidAction() { - String oldTextFieldFileName = textFieldFileName.getText(); - String oldTextFieldUid = textFieldUid.getText(); - textFieldFileName.setText(""); - textFieldUid.setText(""); - try { - setCliOptions(); - } finally { - textFieldFileName.setText(oldTextFieldFileName); - textFieldUid.setText(oldTextFieldUid); - } + jLoggingPane.setText(""); updaterThread = new Thread(() -> { - final Updater upd = new Updater(cliOptions); - String uid = upd.requestUid(); - SwingUtilities.invokeLater(() -> this.textFieldUid.setText(uid)); + Thread.currentThread().setName("uidRequest"); + String uid = ""; + try { + CliOptions cliOptions = getCliOptions(); + cliOptions.setFileName(""); + cliOptions.setUid(""); + displayCommandLine(cliOptions); + final Updater upd = new Updater(cliOptions); + SwingUtilities.invokeLater(this::beforeUidRequest); + uid = upd.requestUid(); + } catch (ParseException | UnknownHostException | UpdaterException | KNXException e) { + logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + JOptionPane.showMessageDialog(this, + String.format(getTranslation("Exception.requestUidAction.Message"), e), + getTranslation("Error"), JOptionPane.ERROR_MESSAGE); + } finally { + String finalUid = uid; + SwingUtilities.invokeLater(() -> onRequestUidFinished(finalUid)); + } }); updaterThread.start(); } + private void setUIDComponentsEnabled(boolean enabled) { + comboBoxScenario.setEnabled(enabled); + buttonRequestUid.setEnabled(enabled); + buttonStartStopFlash.setEnabled(enabled); + textFieldUid.setEnabled(enabled); + } + + private void beforeUidRequest() { + textFieldUid.setText(""); + setUIDComponentsEnabled(false); + } + + private void onRequestUidFinished(String uid) { + textFieldUid.setText(uid); + setUIDComponentsEnabled(true); + } + private void handleReloadGatewaysAction() { new Thread(this::loadKnxIpInterfacesAndFillComboBox).start(); } @@ -210,8 +252,9 @@ public GuiMain() { @Override public void actionPerformed(ActionEvent e) { if (comboBoxIpGateways.getSelectedItem() == null) return; - if (((CalimeroSearchComboItem) comboBoxIpGateways.getSelectedItem()).value == null) return; - InetSocketAddress addr = ((CalimeroSearchComboItem) comboBoxIpGateways.getSelectedItem()).value.remoteEndpoint(); + Discoverer.Result value = ((CalimeroSearchComboItem) comboBoxIpGateways.getSelectedItem()).value; + if (value == null) return; + InetSocketAddress addr = value.remoteEndpoint(); textBoxKnxGatewayIpAddr.setText(addr.getAddress().getHostAddress()); textFieldPort.setText(String.valueOf(addr.getPort())); } @@ -248,6 +291,7 @@ private void saveAllParameters(String fileName) { try { guiSettings.writeComponentSettings(fileName); } catch (IOException e) { + logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 JOptionPane.showMessageDialog(this, String.format(getTranslation("IOException.savingSettings.Message"), fileName, e.getMessage()), this.getTranslation("Warning"), JOptionPane.WARNING_MESSAGE); @@ -260,6 +304,7 @@ private boolean loadAllParameters(String fileName) { setComponentNames(); guiSettings.readComponentsSettings(fileName); } catch (IOException e) { + logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 JOptionPane.showMessageDialog(this, String.format(getTranslation("IOException.loadingSettings.Message"), fileName, e.getMessage()), this.getTranslation("Warning"), JOptionPane.WARNING_MESSAGE); @@ -269,43 +314,74 @@ private boolean loadAllParameters(String fileName) { return true; } - private void setCliOptions() { + private CliOptions getCliOptions() throws KNXFormatException, ParseException { ArrayList argsList = new ArrayList<>(); argsList.add(argument("", textBoxKnxGatewayIpAddr)); argsList.add(argument(OPT_LONG_FILENAME, textFieldFileName)); + //argsList.add(argument(OPT_LONG_LOCALHOST, textField*)); // todo add gui textfield --localhost + //argsList.add(argument(OPT_LONG_LOCALPORT, textField*)); // todo add gui textfield --localport argsList.add(argument(OPT_LONG_PORT, textFieldPort)); - argsList.add(argument(OPT_LONG_UID, textFieldUid)); - if (comboBoxMedium.isVisible()) - argsList.add(argument(OPT_LONG_MEDIUM, comboBoxMedium)); + argsList.add(argument(OPT_LONG_FT12, textFieldSerial)); argsList.add(argument(OPT_LONG_TPUART, textFieldTpuart)); - argsList.add(argument(OPT_LONG_DEVICE, textFieldDeviceAddress)); + //argsList.add(argument(OPT_LONG_USB, textField*)); // todo add gui textfield --usb + + argsList.add(argument(OPT_LONG_MEDIUM, comboBoxMedium)); + + argsList.add(argument(OPT_LONG_USER_ID, textFieldKnxSecureUser)); + argsList.add(argument(OPT_LONG_USER_PASSWORD, textFieldKnxSecureUserPwd)); + argsList.add(argument(OPT_LONG_DEVICE_PASSWORD, textFieldKnxSecureDevicePwd)); + argsList.add(argument(OPT_LONG_PROG_DEVICE, textFieldBootloaderDeviceAddress)); - argsList.add(argument(OPT_LONG_OWN_ADDRESS, textFieldOwnAddress)); - if ((CheckBoxDiffFlash.isVisible() && !CheckBoxDiffFlash.isSelected()) || !CheckBoxDiffFlash.isVisible()) - argsList.add(argument(OPT_LONG_FULL)); + + if (textFieldDeviceAddress.isVisible()) + argsList.add(argument(OPT_LONG_DEVICE, textFieldDeviceAddress)); + + if (textFieldOwnAddress.isVisible()) + argsList.add(argument(OPT_LONG_OWN_ADDRESS, textFieldOwnAddress)); + + if (textFieldUid.isVisible()) + argsList.add(argument(OPT_LONG_UID, textFieldUid)); + + argsList.add(argument(OPT_LONG_DELAY, textFieldDelay)); +/* + if (checkBox*.isVisible() && checkBox*.isSelected()) // todo add gui checkbox --tunnelingv2 + argsList.add(argument(OPT_LONG_TUNNEL_V2)); + if (checkBox*.isVisible() && checkBox*.isSelected()) // todo add gui checkbox --tunneling + argsList.add(argument(OPT_LONG_TUNNEL_V1)); +*/ if (natCheckBox.isVisible() && natCheckBox.isSelected()) argsList.add(argument(OPT_LONG_NAT)); - argsList.add(argument(OPT_LONG_DELAY, textFieldDelay)); - if (eraseCompleteFlashCheckBox.isVisible() && eraseCompleteFlashCheckBox.isSelected()) - argsList.add(argument(OPT_LONG_ERASEFLASH)); + + // todo add gui OPT_LONG_ROUTING (--routing) + + if ((CheckBoxDiffFlash.isVisible() && !CheckBoxDiffFlash.isSelected()) || !CheckBoxDiffFlash.isVisible()) + argsList.add(argument(OPT_LONG_FULL)); + if (noFlashCheckBox.isVisible() && noFlashCheckBox.isSelected()) argsList.add(argument(OPT_LONG_NO_FLASH)); - String updaterFileName = String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()); - String[] args = new String[argsList.size()]; - args = argsList.toArray(args); - logger.info("java -jar {} {}", updaterFileName, CliConverter.toString(argsList)); - try { - // read in user-supplied command line options - this.cliOptions = new CliOptions(args, updaterFileName, - "Selfbus KNX-Firmware update tool options", "", Updater.PHYS_ADDRESS_BOOTLOADER, Updater.PHYS_ADDRESS_OWN); - } catch (final KNXIllegalArgumentException e) { - throw e; - } catch (final RuntimeException e) { - throw new KNXIllegalArgumentException(e.getMessage(), e); - } + // argsList.add(argument(OPT_LONG_LOGLEVEL, comboBox*)); // todo add gui combobox --logLevel + + argsList.add(argument(OPT_LONG_PRIORITY, comboBoxKnxTelegramPriority)); + + if (eraseCompleteFlashCheckBox.isVisible() && eraseCompleteFlashCheckBox.isSelected()) + argsList.add(argument(OPT_LONG_ERASEFLASH)); + + // argsList.add(argument(OPT_LONG_DUMPFLASH, textField*)); // todo add gui textfield --DUMPFLASH startaddress endaddress +/* + if (checkBox*.isVisible() && checkBox*.isSelected()) // todo add gui checkbox --statistic + argsList.add(argument(OPT_LONG_LOGSTATISTIC)); +*/ + // argsList.add(argument(OPT_LONG_BLOCKSIZE, comboBox*)); // todo add gui combobox --blocksize 256, 512, 1024 + + return new CliOptions(argsList); + } + + private void displayCommandLine(CliOptions cliOptions) { + logger.info("{}java -jar {} {}{}", ansi().fg(YELLOW), ToolInfo.getToolJarName(), + cliOptions.reconstructCommandLine(), ansi().reset()); } public static void startSwingGui() { @@ -362,11 +438,41 @@ private void loadKnxIpInterfacesAndFillComboBox() { comboBoxIpGateways.addItem(new CalimeroSearchComboItem(getTranslation("selectInterface"), null)); - DiscoverKnxInterfaces.getAllnetIPInterfaces().forEach(r -> - comboBoxIpGateways.addItem(new CalimeroSearchComboItem(r.response().getDevice().getName() + - " (" + r.response().getControlEndpoint().endpoint().getAddress().getHostAddress() + ")", r))); + List> result; + try { + result = DiscoverKnxInterfaces.getAllnetIPInterfaces(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Restore interrupt status + logger.info("Interrupted while discovering IP interfaces", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + return; + } + + for (Discoverer.Result r : result) { + SearchResponse sr = r.response(); + String ifName = sr.getDevice().getName(); + String ifHostAddress = sr.getControlEndpoint().endpoint().getAddress().getHostAddress(); + IndividualAddress ifPhysAddress = sr.getDevice().getAddress(); + int ifPort = sr.getControlEndpoint().endpoint().getPort(); + int ifTunnelVersion; + if (sr.v2()) + ifTunnelVersion = 2; + else + ifTunnelVersion = 1; + + CalimeroSearchComboItem comboBoxItem = new CalimeroSearchComboItem( + String.format("%s (%s:%d) v%d %s", ifName, ifHostAddress, ifPort, ifTunnelVersion, + ifPhysAddress.toString()), r); + comboBoxIpGateways.addItem(comboBoxItem); + } - Set usbInterfaces = DiscoverKnxInterfaces.getUsbInterfaces(); + Set usbInterfaces; + try { + usbInterfaces = DiscoverKnxInterfaces.getUsbInterfaces(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Restore interrupt status + logger.info("Interrupted while discovering USB interfaces", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + return; + } for (final var d : usbInterfaces) { final String vp = String.format("%04x:%04x", d.vendorId(), d.productId()); logger.info("Found USB Interface: {} {} S/N {} VID/PID {}", @@ -385,22 +491,22 @@ private void fillScenarios() { } private void fillMediumComboBox() { - comboBoxMedium.addItem("tp1"); - comboBoxMedium.addItem("rf"); + comboBoxMedium.addItem(KNXMediumSettings.getMediumString(KNXMediumSettings.MEDIUM_TP1)); + comboBoxMedium.addItem(KNXMediumSettings.getMediumString(KNXMediumSettings.MEDIUM_RF)); } private void fillTelegramPriorityComboBox() { if (comboBoxKnxTelegramPriority == null) return; - comboBoxKnxTelegramPriority.addItem("LOW"); - comboBoxKnxTelegramPriority.addItem("URGENT"); - comboBoxKnxTelegramPriority.addItem("NORMAL"); - comboBoxKnxTelegramPriority.addItem("SYSTEM"); + comboBoxKnxTelegramPriority.addItem(Priority.LOW.toString()); + comboBoxKnxTelegramPriority.addItem(Priority.NORMAL.toString()); + comboBoxKnxTelegramPriority.addItem(Priority.URGENT.toString()); + comboBoxKnxTelegramPriority.addItem(Priority.SYSTEM.toString()); } private void setFrameImages() { String resourceTemplate = "/frame_images/selfbus_logo_%sx%s.png"; - String[] resolutions = {"16"}; // "24", "32", "48", "256"}; //todo enable also these resolutions + String[] resolutions = {"16", "24", "32", "48", "256"}; List frameImageList = new ArrayList<>(); for (String resolution : resolutions) { @@ -459,7 +565,7 @@ public Object getValue() { public static class CalimeroSearchComboItem { private final String key; - Discoverer.Result value; + final Discoverer.Result value; public CalimeroSearchComboItem(String key, Discoverer.Result value) { this.key = key; diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java index 1b3bb795..c4e96015 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java @@ -30,17 +30,18 @@ public void uncaughtException(Thread thread, Throwable throwable) { logger.error("{}Uncaught exception in thread {} {}{}", ansi().fg(RED) , thread.getName(), throwable.toString(), ansi().reset(), throwable); + // todo see logback issue #876 + // https://github.com/qos-ch/logback/issues/876 + // Don´t delete .toString() // Show exception details in a dialog - SwingUtilities.invokeLater(() -> { - JOptionPane.showMessageDialog( - parentFrame, - "An unexpected error occurred:" + System.lineSeparator() + - throwable.toString() + System.lineSeparator() + - getLimitedStackTrace(throwable, 7), - "Exception", - JOptionPane.ERROR_MESSAGE - ); - }); + SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog( + parentFrame, + "An unexpected error occurred:" + System.lineSeparator() + + throwable.toString() + System.lineSeparator() + + getLimitedStackTrace(throwable, 7), + "Exception", + JOptionPane.ERROR_MESSAGE + )); } /** diff --git a/firmware_updater/updater/src/resources/language/GuiMain.properties b/firmware_updater/updater/src/resources/language/GuiMain.properties index 6bc4a294..5ecee0a8 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain.properties @@ -32,7 +32,7 @@ eraseCompleteFlashHint=USE WITH CAUTION! Erases the complete flash m including the physical KNX address and all settings of
\ the device. Only the bootloader is not deleted. stopFlash=stop flashing -requestUid=request U&ID from device +requestUid=request UID from device requestUidHint=The UID can be requested from a Selfbus device.\ KnxGatewayConnectionSettings=KNX gateway connection settings @@ -59,9 +59,11 @@ reloadKnxIpGateways=&reload gateways selectInterface=select Interface diffFlash=differential F&lash diffFlashHint=flash only changed parts of software
\\ [experimental] -logMessageCanceledFlashing=Flash process canceled. labelScenarioHint=Label loadFile.firmwareFilterDescription=Firmware (*.hex) Warning=Warning IOException.loadingSettings.Message=Could not load settings from file %s.
%s -IOException.savingSettings.Message=Could not save settings to file %s.
%s \ No newline at end of file +IOException.savingSettings.Message=Could not save settings to file %s.
%s +Exception.requestUidAction.Message=An error occurred while retrieving the UID.
%s +Exception.handleStartStopFlashAction.Message=An error has occurred while updating.
%s +Error=Error \ No newline at end of file diff --git a/firmware_updater/updater/src/resources/language/GuiMain_de.properties b/firmware_updater/updater/src/resources/language/GuiMain_de.properties index a853d71a..6c6b2d84 100644 --- a/firmware_updater/updater/src/resources/language/GuiMain_de.properties +++ b/firmware_updater/updater/src/resources/language/GuiMain_de.properties @@ -18,7 +18,7 @@ und allen Einstellungen. Nur der Bootloader bleibt bestehen. noFlashHint=Flashen der Firmware deaktivieren uidHint=die UID des Selfbus Gerätes fileNameHint=Der Dateipfad kann manuell bearbeitet werden
\ -(Enter zum bestätigen) +(Enter zum Bestätigen) ipAddrHint=IP-Adresse der KNX Schnittstelle portHint=TCP/UDP Port der KNX Schnittstelle (default 3671) stopFlash=Stoppe Flash Vorgang @@ -61,9 +61,11 @@ selectInterface=w diffFlash=differenzieller F&lash Vorgang diffFlashHint=Es werden nur veränderte Teile der Software
\ übertragen [experimentell] -logMessageCanceledFlashing=Flash Vorgang wurde abgebrochen. labelScenarioHint=Label loadFile.firmwareFilterDescription=Firmware (*.hex) Warning=Warning IOException.loadingSettings.Message=Die Einstellungen konnten nicht aus der Datei file %s geladen werden.
%s -IOException.savingSettings.Message=Die Einstellungen konnten nicht in die Datei %s gespeichert werden.
%s \ No newline at end of file +IOException.savingSettings.Message=Die Einstellungen konnten nicht in die Datei %s gespeichert werden.
%s +Exception.requestUidAction.Message=Es ist ein Fehler beim Auslesen der UID aufgetreten.
%s +Exception.handleStartStopFlashAction.Message=Es ist ein Fehler beim Flashen aufgetreten.
%s +Error=Fehler \ No newline at end of file diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index a93ed322..29b71041 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -4,6 +4,10 @@ + true From 0a28c8edb692138fb0b5a3705046e2b9c87fd823 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 22:00:00 +0100 Subject: [PATCH 193/359] [Updater] Upgrade to gradle 8.12 --- .../updater/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties index 171d8761..18362b78 100644 --- a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties +++ b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From a0e4511ad3d1ed03a3499cb5558fe7331a14d7c5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 1 Jan 2025 22:00:32 +0100 Subject: [PATCH 194/359] [Updater] Fix gradle warnings --- firmware_updater/updater/settings.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/settings.gradle b/firmware_updater/updater/settings.gradle index 2ae16b0a..46cce6f4 100644 --- a/firmware_updater/updater/settings.gradle +++ b/firmware_updater/updater/settings.gradle @@ -4,8 +4,8 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { mavenCentral() - maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots' } - maven { url 'https://jitpack.io' } // jitpack builds if needed java-intelhex-parser for us + maven { (url = 'https://s01.oss.sonatype.org/content/repositories/snapshots')} + maven { (url = 'https://jitpack.io')} // jitpack builds if needed java-intelhex-parser for us } /* sourceControl { From 4fee4f4c0c03885886d7ebcfc67d62f69c770d47 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 5 Jan 2025 09:24:45 +0100 Subject: [PATCH 195/359] [Updater] Bump Updater version to 1.21 --- firmware_updater/updater/README.md | 2 +- firmware_updater/updater/build.gradle | 2 +- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 437b8f3f..062582f0 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -1,4 +1,4 @@ -# Selfbus-Updater 1.20 +# Selfbus-Updater 1.21 ## Requirements diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 25ceba3c..af5c8d47 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.selfbus' -version = '1.20' ///\todo also change in ../README.md and ToolInfo.java (String version) +version = '1.21' ///\todo also change in ../README.md and ToolInfo.java (String version) description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index e1668f90..84c772c8 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -12,7 +12,7 @@ public final class ToolInfo { private static final long versionMajor = 1; ///\todo change also in ../README.md and build.gradle - private static final long versionMinor = 20; + private static final long versionMinor = 21; private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md private static final long minMinorVersionBootloader = 0; From f1dd7dfba3f1547065a552fbe269f3784437f86e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 5 Jan 2025 13:21:04 +0100 Subject: [PATCH 196/359] [Updater] Log last caught exception and simplify `if` --- .../src/org/selfbus/updater/DeviceManagement.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 890b206e..70ad3634 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -401,22 +401,26 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr throws UpdaterException, InterruptedException, KNXLinkClosedException { ResponseResult result = new ResponseResult(); while (true) { + KNXException lastCaughtException; try { byte[] data2 = mc.sendUpdateData(progDestination, command.toByte(), data); result.copyFromArray(data2); return result; } catch (KNXTimeoutException e) { + lastCaughtException = e; logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), e.getClass().getSimpleName(), ansi().reset()); result.incTimeoutCount(); } catch (KNXDisconnectException e) { ///\todo check causes of KNXRemoteException, if think they are unrecoverable + lastCaughtException = e; logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), e.getClass().getSimpleName(), ansi().reset()); result.incDropCount(); } catch (KNXInvalidResponseException e) { + lastCaughtException = e; logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), e.getClass().getSimpleName(), ansi().reset()); } @@ -424,10 +428,8 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr if (maxRetry > 0) { maxRetry--; } - - if (maxRetry == 0) - { - throw new UpdaterException(String.format("%s failed.", command)); + else { + throw new UpdaterException(String.format("%s failed.", command), lastCaughtException); } } } From 25a38a6ce53f48cfa0d8f3283c866e67d0d847b9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 5 Jan 2025 13:22:58 +0100 Subject: [PATCH 197/359] [Updater] Log discovered KNX netIP-Interfaces --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index bfcb1c3f..e0828396 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -459,9 +459,10 @@ private void loadKnxIpInterfacesAndFillComboBox() { else ifTunnelVersion = 1; - CalimeroSearchComboItem comboBoxItem = new CalimeroSearchComboItem( - String.format("%s (%s:%d) v%d %s", ifName, ifHostAddress, ifPort, ifTunnelVersion, - ifPhysAddress.toString()), r); + String ipInterfaceText = String.format("%s (%s:%d) v%d %s", ifName, ifHostAddress, ifPort, ifTunnelVersion, + ifPhysAddress.toString()); + logger.debug("Found IP interface: {}", ipInterfaceText); + CalimeroSearchComboItem comboBoxItem = new CalimeroSearchComboItem(ipInterfaceText, r); comboBoxIpGateways.addItem(comboBoxItem); } From 43c4616d0cd3ad8613cd79aa90d9c4d53be87f0d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 5 Jan 2025 13:24:28 +0100 Subject: [PATCH 198/359] [Updater] Default log level `TRACE` --- firmware_updater/updater/src/resources/logback.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 29b71041..d5558471 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -58,7 +58,7 @@ - + ${application.home:-.}/log/updater-${appStartTimestamp}.html @@ -61,9 +59,7 @@ - From 4db0969bb21481b0f9fb96b2108e8bdfdfccdedc Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 5 Jan 2025 17:42:28 +0100 Subject: [PATCH 200/359] [Updater] Reduce messages log level to warn --- .../src/org/selfbus/updater/DiscoverKnxInterfaces.java | 2 +- .../updater/src/org/selfbus/updater/LoggingManager.java | 4 ++-- firmware_updater/updater/src/org/selfbus/updater/Updater.java | 2 +- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java index 6215689f..fcfe15d1 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -48,7 +48,7 @@ public static Set getUsbInterfaces() throws InterruptedException { } catch (KNXException e) { iterator.remove(); - logger.error("error: reading KNX device descriptor of {} ({})", d, e.getMessage()); + logger.warn("error: reading KNX device descriptor of {} ({})", d, e.getMessage()); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java index 232ec313..f3086eca 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java @@ -25,7 +25,7 @@ public static void disableAppender(String appenderName) { Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); if (appender == null) { - logger.error("disableAppender: appender {} not found.", appenderName); + logger.warn("disableAppender: appender {} not found.", appenderName); return; } @@ -40,7 +40,7 @@ public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); if (appender == null) { - logger.error("setAppenderLogLevel: appender {} not found.", appenderName); + logger.warn("setAppenderLogLevel: appender {} not found.", appenderName); return; } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index abfd94cb..577363d4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -274,7 +274,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres diffMode = FlashDiffMode.setupDifferentialMode(bootDescriptor); } else { - logger.error("{} BootDescriptor is not valid -> switching to full mode{}", + logger.warn("{} BootDescriptor is not valid -> switching to full mode{}", ansi().fgBright(RED), ansi().reset()); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index e0828396..e6489e29 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -514,7 +514,7 @@ private void setFrameImages() { String resourceName = String.format(resourceTemplate, resolution, resolution); InputStream imageStream = this.getClass().getResourceAsStream(resourceName); if (imageStream == null) { - logger.error("getResourceAsStream({}) failed", resourceName); + logger.warn("getResourceAsStream({}) failed", resourceName); continue; } @@ -523,7 +523,7 @@ private void setFrameImages() { frameImageList.add(image); logger.debug("Added {} to frameImageList", resourceName); } catch (IOException e) { - logger.error("Could not add {} to frameImageList {}", resourceName, Arrays.toString(e.getStackTrace())); + logger.warn("Could not add {} to frameImageList {}", resourceName, Arrays.toString(e.getStackTrace())); } } From f577b226348bd58be13d427951c925c9c18b461e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 6 Jan 2025 14:15:33 +0100 Subject: [PATCH 201/359] [Updater] New SBLinkListener, SBTransportListener for logging --- .../updater/SBManagementClientImpl.java | 178 +++++++++++++++++- 1 file changed, 172 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index 2d0fbf30..c2eb5d3f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -1,13 +1,15 @@ package org.selfbus.updater; -import tuwien.auto.calimero.DataUnitBuilder; -import tuwien.auto.calimero.KNXInvalidResponseException; -import tuwien.auto.calimero.KNXTimeoutException; -import tuwien.auto.calimero.link.KNXLinkClosedException; -import tuwien.auto.calimero.link.KNXNetworkLink; +import ch.qos.logback.classic.Logger; +import org.slf4j.LoggerFactory; +import tuwien.auto.calimero.*; +import tuwien.auto.calimero.cemi.CEMI; +import tuwien.auto.calimero.cemi.CEMILData; +import tuwien.auto.calimero.link.*; import tuwien.auto.calimero.mgmt.Destination; import tuwien.auto.calimero.mgmt.KNXDisconnectException; import tuwien.auto.calimero.mgmt.ManagementClientImpl; +import tuwien.auto.calimero.mgmt.TransportListener; import static org.selfbus.updater.Mcu.MAX_ASDU_LENGTH; @@ -18,16 +20,180 @@ * with our custom UPD/UDP protocol. */ public class SBManagementClientImpl extends ManagementClientImpl { + + private CEMILData getCEMILData(final FrameEvent e, Logger eventLogger) { + final CEMI cemi = e.getFrame(); + if (!(cemi instanceof final CEMILData linkData)) { + eventLogger.debug("not a CEMILData"); + return null; + } + return linkData; + } + + /** + * Logs details about a frame event. + *

+ * This method checks if the frame retrieved from the {@link FrameEvent} is of type {@link CEMILData}. + * If the frame is valid, it logs the provided message along with the frame data. + *

+ * + * @param message A descriptive message to include in the log. + * @param e The {@link FrameEvent} containing the frame to be logged. + * @param eventLogger The logger to use for logging the frame details. + * @return The {@link CEMILData} instance if the frame is valid; {@code null} otherwise. + */ + private CEMILData logEvent(String message, final FrameEvent e, final Logger eventLogger) { + CEMILData linkData = getCEMILData(e, eventLogger); + if (linkData == null) { + return null; + } + + eventLogger.debug("{} : {}", message, linkData); + if (linkData.getSource().equals(link.getKNXMedium().getDeviceAddress())) { + eventLogger.debug("source == device address!"); + } + return linkData; + } + + private class SBLinkListener implements NetworkLinkListener + { + private final Logger linkLogger; + + SBLinkListener() { + this.linkLogger = (Logger) LoggerFactory.getLogger(SBLinkListener.class.getName() + " " + link.getName()); + } + + public void indication(final FrameEvent e) + { + CEMILData cemilData = logEvent("indication", e, linkLogger); + if (cemilData == null) { + return; + } + + if (cemilData.getPayload().length < 1) { + linkLogger.debug("no payload"); + return; + } + + int ctrl = cemilData.getPayload()[0] & 0xfc; + if (ctrl == 0) { + final KNXAddress dst = cemilData.getDestination(); + if (dst instanceof GroupAddress) + // check for broadcast or group + if (dst.getRawAddress() == 0) { + linkLogger.debug("broadcast"); + } + else { + linkLogger.debug("group"); + } + else + // individual + linkLogger.debug("individual"); + } + else { + int conControl = cemilData.getPayload()[0] & 0xc3; + int sequenceNumber = (cemilData.getPayload()[0] & 0x3c) >> 2; + switch (conControl) { + case 0x80: + linkLogger.debug("indication {} -> {} T_Connect", cemilData.getSource(), cemilData.getDestination()); + break; + + case 0x81: + linkLogger.debug("indication {} -> {} T_Disconnect", cemilData.getSource(), cemilData.getDestination()); + break; + + case 0xC2: + linkLogger.debug("indication {} -> {} T_Ack #{}", cemilData.getSource(), cemilData.getDestination(), sequenceNumber); + break; + + case 0xC3: + linkLogger.debug("indication {} -> {} T_Nack #{}", cemilData.getSource(), cemilData.getDestination(), sequenceNumber); + break; + + case 0x42: + linkLogger.debug("indication {} -> {} T_Data_Connected #{}", cemilData.getSource(), cemilData.getDestination(), sequenceNumber); + break; + + default: + linkLogger.debug("indication {} -> {} conControl == 0x{} #{}", cemilData.getSource(), cemilData.getDestination(), + String.format("%X", conControl), sequenceNumber); + break; + } + } + } + + @Override + public void linkClosed(final CloseEvent e) + { + linkLogger.debug("attached link was closed"); + } + } + + private class SBTransportListener implements TransportListener + { + private final Logger transportLogger; + + SBTransportListener() { + this.transportLogger = (Logger) LoggerFactory.getLogger(SBTransportListener.class.getName() + " " + link.getName()); + } + + @Override + public void broadcast(final FrameEvent e) + { + logEvent("broadcast", e, transportLogger); + } + + @Override + public void dataConnected(final FrameEvent e) + { + logEvent("dataConnected", e, transportLogger); + } + + @Override + public void dataIndividual(final FrameEvent e) + { + logEvent("dataIndividual", e, transportLogger); + } + + @Override + public void disconnected(final Destination d) { + transportLogger.debug("disconnected {}", d); + } + + @Override + public void group(final FrameEvent e) { + transportLogger.debug("group {}", e); + } + + @Override + public void detached(final DetachEvent e) { + transportLogger.debug("detached {}", e); + } + + @Override + public void linkClosed(final CloseEvent e) + { + transportLogger.debug("linkClosed {}", e); + } + } + private static final int USERMSG_MANUFACTURER_0_WRITE = 0x2F8; private static final int USERMSG_MANUFACTURER_6_RESPONSE = 0x2FE; private static final int apciWrite = USERMSG_MANUFACTURER_0_WRITE; private static final int apciResponse = USERMSG_MANUFACTURER_6_RESPONSE; //private final Logger logger; // uncomment when needed + KNXNetworkLink link; + public SBManagementClientImpl(KNXNetworkLink link) throws KNXLinkClosedException { super(link); - //logger = LoggerFactory.getLogger(SBManagementClientImpl.class.getName() + " " + link.getName()); + this.link = link; + SBLinkListener linkListener = new SBLinkListener(); + this.link.addLinkListener(linkListener); + //logger = (Logger) LoggerFactory.getLogger(SBManagementClientImpl.class.getName() + " " + link.getName()); + SBTransportListener transportListener = new SBTransportListener(); + this.transportLayer().addTransportListener(transportListener); } private byte[] prepareAsdu(final int cmd, final byte[] data) { From 39e4bdf14785e85905fca530868d389c8d5c3a2a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 6 Jan 2025 14:21:27 +0100 Subject: [PATCH 202/359] [Updater] Add info to build with local calimero-core --- firmware_updater/updater/build.gradle | 4 ++++ firmware_updater/updater/settings.gradle | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index af5c8d47..02ba0cb9 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -104,6 +104,10 @@ tasks.named('check') { // We need it to add java-intelhex-parser as an dependency, instead of copy/pasting the source. // dependencies { + // Use this implementation for debugging with local calimero-core + // Set location of local calimero-core in settings.gradle + //implementation 'com.github.calimero:calimero-core' + // calimero knx bus access library implementation 'com.github.calimero:calimero-core:2.6-rc1' diff --git a/firmware_updater/updater/settings.gradle b/firmware_updater/updater/settings.gradle index 46cce6f4..7e0c4219 100644 --- a/firmware_updater/updater/settings.gradle +++ b/firmware_updater/updater/settings.gradle @@ -1,5 +1,9 @@ rootProject.name = 'SB_updater' +// Use this to build with local calimero-core +// Change also dependency in build.gradle +//includeBuild('..//..//..//calimero//calimero-core') + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { From 713dfd769d9cd43d0bdb731021a671610a856aca Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 6 Jan 2025 17:49:17 +0100 Subject: [PATCH 203/359] [Updater] New `--discover` to list available KNX-Interfaces (IP and USB) --- firmware_updater/updater/README.md | 9 +++--- .../src/org/selfbus/updater/CliOptions.java | 20 ++++++++++++- .../updater/DiscoverKnxInterfaces.java | 29 +++++++++++++++++++ .../src/org/selfbus/updater/Updater.java | 9 +++++- .../src/org/selfbus/updater/gui/GuiMain.java | 5 ++++ 5 files changed, 66 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 062582f0..1c224a51 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -24,11 +24,11 @@ java -jar SB_updater-x.xx-all.jar [-f ] [-m ] ] [-bs <256|512|1024>] [--user ] [--user-pwd ] [--device-pwd ] [-u ] [-f1] [-H ] [-P ] [-p ] [-t2 | -t1 | -r] [-n] [-h | -v] [--delay ] [-l ] [--ERASEFLASH] - [--DUMPFLASH ] [-f0] [--statistic] + [--DUMPFLASH ] [-f0] [--statistic] [--discover] Selfbus KNX-Firmware update tool options: -f,--fileName Filename of hex file to program - -m,--medium KNX medium [tp1|rf] (default tp1) + -m,--medium KNX medium [tp1|rf] (default TP1) -s,--serial use FT1.2 serial communication -t,--tpuart use TPUART serial communication (experimental, needs serialcom or rxtx library in java.library.path) @@ -42,7 +42,7 @@ Selfbus KNX-Firmware update tool options: --priority KNX telegram priority (default LOW) -bs,--blocksize <256|512|1024> Block size to program (default 1024 bytes) --user KNX IP Secure tunneling user identifier (1..127) - (default 1) + (default -1) --user-pwd KNX IP Secure tunneling user password (Commissioning password/Inbetriebnahmepasswort), quotation marks (") in password may not work @@ -64,7 +64,7 @@ Selfbus KNX-Firmware update tool options: -v,--version show tool/library version --delay delay telegrams during data transmission to reduce bus load, valid 0-500ms, default 0 - -l,--logLevel Logfile logging level [TRACE|DEBUG|INFO] (default DEBUG) + -l,--logLevel Logfile logging level [TRACE|DEBUG|INFO] (default TRACE) --ERASEFLASH USE WITH CAUTION! Erases the complete flash memory including the physical KNX address and all settings of the device. Only the bootloader is not deleted. @@ -73,6 +73,7 @@ Selfbus KNX-Firmware update tool options: bootloader. -f0,--NO_FLASH for debugging use only, disable flashing firmware! --statistic show more statistic data + --discover List available KNXnet/IP interfaces and USB-Interfaces ``` ## Common use cases: Updater with graphical user interface (**experimental**) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 52b99c92..fb3998ce 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -114,6 +114,9 @@ public class CliOptions { public static final String OPT_SHORT_BLOCKSIZE = "bs"; public static final String OPT_LONG_BLOCKSIZE = "blocksize"; + + public static final String OPT_LONG_DISCOVER = "discover"; + private final static List VALID_BLOCKSIZES = Arrays.asList(256, 512, 1024); private static final int PRINT_WIDTH = 100; @@ -166,6 +169,8 @@ public class CliOptions { private final Level defaultLogLevel; + private boolean discoverIsSet = false; + private CliOptions() { defaultLogLevel = getLogLevel(); @@ -178,6 +183,8 @@ private CliOptions() { Option version = new Option(OPT_SHORT_VERSION, OPT_LONG_VERSION, false, "show tool/library version"); Option NO_FLASH = new Option(OPT_SHORT_NO_FLASH, OPT_LONG_NO_FLASH, false, "for debugging use only, disable flashing firmware!"); Option eraseFlash = new Option(null, OPT_LONG_ERASEFLASH, false, "USE WITH CAUTION! Erases the complete flash memory including the physical KNX address and all settings of the device. Only the bootloader is not deleted."); + Option discover = new Option(null, OPT_LONG_DISCOVER, false, "List available KNXnet/IP interfaces and USB-Interfaces"); + Option dumpFlash = Option.builder(null).longOpt(OPT_LONG_DUMPFLASH) .argName("start> 1) { throw new CliInvalidException("Only one bus interface can be used."); } - else if (interfacesSet == 0) { + else if ((interfacesSet == 0) && (!getDiscoverIsSet())) { throw new CliInvalidException("No bus interface specified."); } } @@ -1134,4 +1143,13 @@ private void setUsbVendorIdAndProductId(String usbVendorIdAndProductId) { this.usbVendorIdAndProductId = usbVendorIdAndProductId; logger.debug("{}={}", OPT_LONG_USB, getUsbVendorIdAndProductId()); } + + public boolean getDiscoverIsSet() { + return discoverIsSet; + } + + public void setDiscoverIsSet(boolean discoverIsSet) { + this.discoverIsSet = discoverIsSet; + logger.debug("{}={}", OPT_LONG_DISCOVER, this.discoverIsSet); + } } \ No newline at end of file diff --git a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java index fcfe15d1..8145a07a 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tuwien.auto.calimero.IndividualAddress; import tuwien.auto.calimero.KNXException; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; @@ -59,4 +60,32 @@ public static Set getUsbInterfaces() throws InterruptedException { } return knxUsbDevices; } + + public static void toText(List> ifaceList) { + for (Discoverer.Result r : ifaceList) { + SearchResponse sr = r.response(); + String ifName = sr.getDevice().getName(); + String ifHostAddress = sr.getControlEndpoint().endpoint().getAddress().getHostAddress(); + IndividualAddress ifPhysAddress = sr.getDevice().getAddress(); + int ifPort = sr.getControlEndpoint().endpoint().getPort(); + int ifTunnelVersion; + if (sr.v2()) + ifTunnelVersion = 2; + else + ifTunnelVersion = 1; + + String ipInterfaceText = String.format("%s (%s:%d) v%d %s", ifName, ifHostAddress, ifPort, ifTunnelVersion, + ifPhysAddress.toString()); + logger.info("Found IP interface: {}", ipInterfaceText); + logger.info("\t{}", sr.toString()); + } + } + + public static void toText(Set usbInterfaces) { + for (final var d : usbInterfaces) { + final String vp = String.format("%04x:%04x", d.vendorId(), d.productId()); + logger.info("Found USB Interface: {} {} S/N {} VID/PID {}", + d.manufacturer(), d.product(), d.serialNumber(), vp); + } + } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 577363d4..5d4d49d4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -99,13 +99,20 @@ public static void main(final String[] args) { return; } + if (options.getDiscoverIsSet()) { + logger.info("Discovering KNX network..."); + DiscoverKnxInterfaces.toText(DiscoverKnxInterfaces.getAllnetIPInterfaces()); + DiscoverKnxInterfaces.toText(DiscoverKnxInterfaces.getUsbInterfaces()); + finalizeJansi(); + return; + } final Updater updater = new Updater(options); final ShutdownHandler shutDownHandler = new ShutdownHandler().register(); updater.run(); shutDownHandler.unregister(); } - catch (KNXFormatException | ParseException e) { + catch (KNXFormatException | ParseException | InterruptedException e) { logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 } finally { diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index e6489e29..560440b4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -376,6 +376,11 @@ private CliOptions getCliOptions() throws KNXFormatException, ParseException { */ // argsList.add(argument(OPT_LONG_BLOCKSIZE, comboBox*)); // todo add gui combobox --blocksize 256, 512, 1024 +/* + if (checkBox*.isVisible() && checkBox*.isSelected()) // todo add gui checkbox --discover + argsList.add(argument(OPT_LONG_DISCOVER)); + */ + return new CliOptions(argsList); } From f3bdafa89e329c7c0e9a4d902e2dcb3085cf25bc Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 7 Jan 2025 13:31:14 +0100 Subject: [PATCH 204/359] [Updater] Cleanup redundant logging These events are already logged by calimero. --- .../selfbus/updater/SBManagementClientImpl.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index c2eb5d3f..9e34b1e6 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -40,19 +40,17 @@ private CEMILData getCEMILData(final FrameEvent e, Logger eventLogger) { * @param message A descriptive message to include in the log. * @param e The {@link FrameEvent} containing the frame to be logged. * @param eventLogger The logger to use for logging the frame details. - * @return The {@link CEMILData} instance if the frame is valid; {@code null} otherwise. */ - private CEMILData logEvent(String message, final FrameEvent e, final Logger eventLogger) { + private void logEvent(String message, final FrameEvent e, final Logger eventLogger) { CEMILData linkData = getCEMILData(e, eventLogger); if (linkData == null) { - return null; + return; } eventLogger.debug("{} : {}", message, linkData); if (linkData.getSource().equals(link.getKNXMedium().getDeviceAddress())) { eventLogger.debug("source == device address!"); } - return linkData; } private class SBLinkListener implements NetworkLinkListener @@ -65,7 +63,7 @@ private class SBLinkListener implements NetworkLinkListener public void indication(final FrameEvent e) { - CEMILData cemilData = logEvent("indication", e, linkLogger); + CEMILData cemilData = getCEMILData(e, linkLogger); if (cemilData == null) { return; } @@ -140,19 +138,19 @@ private class SBTransportListener implements TransportListener @Override public void broadcast(final FrameEvent e) { - logEvent("broadcast", e, transportLogger); + // logEvent("broadcast", e, transportLogger); } @Override public void dataConnected(final FrameEvent e) { - logEvent("dataConnected", e, transportLogger); + // logEvent("dataConnected", e, transportLogger); } @Override public void dataIndividual(final FrameEvent e) { - logEvent("dataIndividual", e, transportLogger); + // logEvent("dataIndividual", e, transportLogger); } @Override @@ -162,7 +160,7 @@ public void disconnected(final Destination d) { @Override public void group(final FrameEvent e) { - transportLogger.debug("group {}", e); + // transportLogger.debug("group {}", e); } @Override From 96e224b446eb9d088c18a7725719041943d0e83d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 7 Jan 2025 13:37:40 +0100 Subject: [PATCH 205/359] [Updater] Fix TL4 logging TPDU was not handled correct. E.g. a T_Connect would never be logged and occasionally the default case occurred, which should never happen. --- .../updater/SBManagementClientImpl.java | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index 9e34b1e6..0bbc2cb7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -73,33 +73,21 @@ public void indication(final FrameEvent e) return; } - int ctrl = cemilData.getPayload()[0] & 0xfc; - if (ctrl == 0) { - final KNXAddress dst = cemilData.getDestination(); - if (dst instanceof GroupAddress) - // check for broadcast or group - if (dst.getRawAddress() == 0) { - linkLogger.debug("broadcast"); - } - else { - linkLogger.debug("group"); - } - else - // individual - linkLogger.debug("individual"); + final int tpdu = cemilData.getPayload()[0] & 0xff; + if (tpdu == 0x80) { + linkLogger.debug("indication {} -> {} T_Connect", cemilData.getSource(), cemilData.getDestination()); + return; + } + else if (tpdu == 0x81) { + linkLogger.debug("indication {} -> {} T_Disconnect", cemilData.getSource(), cemilData.getDestination()); + return; } - else { - int conControl = cemilData.getPayload()[0] & 0xc3; - int sequenceNumber = (cemilData.getPayload()[0] & 0x3c) >> 2; - switch (conControl) { - case 0x80: - linkLogger.debug("indication {} -> {} T_Connect", cemilData.getSource(), cemilData.getDestination()); - break; - - case 0x81: - linkLogger.debug("indication {} -> {} T_Disconnect", cemilData.getSource(), cemilData.getDestination()); - break; + final int ctrl = tpdu & 0xc0; // is it connection oriented? (1100 0000) + if (ctrl != 0) { + final int conControl = tpdu & 0xc3; // clear sequence number bits (1100 0011) + final int sequenceNumber = (tpdu & 0x3c) >> 2; // get sequence number and shift right by 2 (0011 1100) + switch (conControl) { case 0xC2: linkLogger.debug("indication {} -> {} T_Ack #{}", cemilData.getSource(), cemilData.getDestination(), sequenceNumber); break; @@ -113,11 +101,26 @@ public void indication(final FrameEvent e) break; default: - linkLogger.debug("indication {} -> {} conControl == 0x{} #{}", cemilData.getSource(), cemilData.getDestination(), - String.format("%X", conControl), sequenceNumber); + // This should never happen + linkLogger.warn("indication {} -> {} conControl == {} #{}", cemilData.getSource(), cemilData.getDestination(), + String.format("0x%X", conControl), sequenceNumber); break; } } + else { + final KNXAddress dst = cemilData.getDestination(); + if (dst instanceof GroupAddress) + // check for broadcast or group + if (dst.getRawAddress() == 0) { + linkLogger.debug("broadcast"); + } + else { + linkLogger.debug("group"); + } + else + // individual + linkLogger.debug("individual"); + } } @Override From f97932c982442a5bb976e723f4e9f93911680d86 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 7 Jan 2025 14:41:27 +0100 Subject: [PATCH 206/359] =?UTF-8?q?[Updater]=20Don=C2=B4t=20log=20group=20?= =?UTF-8?q?or=20connectionless=20individual=20telegrams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/org/selfbus/updater/SBManagementClientImpl.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index 0bbc2cb7..4e3d6b29 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -112,14 +112,15 @@ else if (tpdu == 0x81) { if (dst instanceof GroupAddress) // check for broadcast or group if (dst.getRawAddress() == 0) { - linkLogger.debug("broadcast"); + linkLogger.debug("broadcast {} -> {}", cemilData.getSource(), dst); } else { - linkLogger.debug("group"); + // linkLogger.debug("group"); } - else + else { // individual - linkLogger.debug("individual"); + // linkLogger.debug("individual"); + } } } From 2eeee3391f1688bcca30e68b6a49a1af33a4c6ea Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 7 Jan 2025 17:12:57 +0100 Subject: [PATCH 207/359] [Updater] Fix missing T_Data_Connected cases ignoring the 4 bit sequence number these 0x40, 0x41, 0x42, 0x43 are a T_Data_Connected KNX Spec. 2.1 3/3/4 (2 TPDU) --- .../src/org/selfbus/updater/SBManagementClientImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index 4e3d6b29..0a537dde 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -96,14 +96,17 @@ else if (tpdu == 0x81) { linkLogger.debug("indication {} -> {} T_Nack #{}", cemilData.getSource(), cemilData.getDestination(), sequenceNumber); break; + case 0x40: + case 0x41: case 0x42: + case 0x43: linkLogger.debug("indication {} -> {} T_Data_Connected #{}", cemilData.getSource(), cemilData.getDestination(), sequenceNumber); break; default: // This should never happen - linkLogger.warn("indication {} -> {} conControl == {} #{}", cemilData.getSource(), cemilData.getDestination(), - String.format("0x%X", conControl), sequenceNumber); + linkLogger.warn("indication {} -> {} conControl == {} tpdu == {} #{}", cemilData.getSource(), cemilData.getDestination(), + String.format("0x%X", conControl), String.format("0x%X", tpdu), sequenceNumber); break; } } From e7d708450b5a2d241fd5be16e0aa8ad44bc7f495 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 7 Jan 2025 17:24:53 +0100 Subject: [PATCH 208/359] [Updater] Bump version to 1.22 --- firmware_updater/updater/README.md | 2 +- firmware_updater/updater/build.gradle | 2 +- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 1c224a51..01123dbf 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -1,4 +1,4 @@ -# Selfbus-Updater 1.21 +# Selfbus-Updater 1.22 ## Requirements diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 02ba0cb9..250675e1 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.selfbus' -version = '1.21' ///\todo also change in ../README.md and ToolInfo.java (String version) +version = '1.22' ///\todo also change in ../README.md and ToolInfo.java (String version) description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 84c772c8..1c62b604 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -12,7 +12,7 @@ public final class ToolInfo { private static final long versionMajor = 1; ///\todo change also in ../README.md and build.gradle - private static final long versionMinor = 21; + private static final long versionMinor = 22; private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md private static final long minMinorVersionBootloader = 0; From 6a56e35c314022d14632d115e0b6e44f6f9ae289 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 12:45:13 +0100 Subject: [PATCH 209/359] [Updater] Upgrade to logback-classic 1.5.16 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 250675e1..5701dfb3 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -127,7 +127,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.9.0' // console and file logging - implementation 'ch.qos.logback:logback-classic:1.5.15' + implementation 'ch.qos.logback:logback-classic:1.5.16' // Console ansi color support implementation 'org.jline:jline-terminal-jansi:3.28.0' From 7ea41ba0bc214ac563b3fe655b486de706da1750 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 12:46:19 +0100 Subject: [PATCH 210/359] [Updater] Cleanup commented out dependency `jackson-databind:2.13.0` --- firmware_updater/updater/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 5701dfb3..28eac3b4 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -137,7 +137,6 @@ dependencies { // GUI settings implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.2' - //implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' // Intel hex parser // commit 9dec823 of 2023/02/27 From 8032d69bb2b40d91edca440256236d363d104a4d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 14:03:29 +0100 Subject: [PATCH 211/359] [Updater] SLF4J startup message got fixed in logback-classic 2.0.16 --- .../updater/src/org/selfbus/updater/Updater.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 5d4d49d4..a42600f6 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -62,14 +62,6 @@ public static void finalizeJansi() { } public static void main(final String[] args) { - // turn off info level message - // "SLF4J(I): Connected with provider of type [ch.qos.logback.classic.spi.LogbackServiceProvider]" - // see https://github.com/qos-ch/slf4j/issues/422 - // todo test end delete with logback-classic (SLF4J) version >= 2.0.16 - // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic - // https://github.com/qos-ch/slf4j/issues/422#issuecomment-2280712623 - System.setProperty("slf4j.internal.verbosity", "WARN"); - initJansi(); if (args.length == 0) { From 0f0e4ef6be2ab6fa601ba9b2bf888d9699bb3454 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 17:47:54 +0100 Subject: [PATCH 212/359] [Updater] Fix double slash in `includeBuild` --- firmware_updater/updater/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/settings.gradle b/firmware_updater/updater/settings.gradle index 7e0c4219..94e79588 100644 --- a/firmware_updater/updater/settings.gradle +++ b/firmware_updater/updater/settings.gradle @@ -2,7 +2,7 @@ rootProject.name = 'SB_updater' // Use this to build with local calimero-core // Change also dependency in build.gradle -//includeBuild('..//..//..//calimero//calimero-core') +//includeBuild('../../../calimero/calimero-core') dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) From babb4dc20003a72d98d252744b9d8ef866cdb8db Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 17:53:26 +0100 Subject: [PATCH 213/359] [Updater] Set cliOptions according to auto detected KNXNetworkLink This enables the Updater to skip the auto-detection in case of a Reconnect. --- .../src/org/selfbus/updater/CliOptions.java | 6 +++--- .../updater/src/org/selfbus/updater/SBKNXLink.java | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index fb3998ce..acfd897a 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -800,7 +800,7 @@ public boolean getNatIsSet() { return natIsSet; } - private void setNatIsSet(boolean natIsSet) { + public void setNatIsSet(boolean natIsSet) { this.natIsSet = natIsSet; logger.debug("{}={}", OPT_LONG_NAT, getNatIsSet()); } @@ -827,7 +827,7 @@ public boolean getTunnelingV2isSet() { return tunnelingV2isSet; } - private void setTunnelingV2isSet(boolean tunnelingV2isSet) { + public void setTunnelingV2isSet(boolean tunnelingV2isSet) { this.tunnelingV2isSet = tunnelingV2isSet; logger.debug("{}={}", OPT_LONG_TUNNEL_V2, getTunnelingV2isSet()); } @@ -836,7 +836,7 @@ public boolean getTunnelingV1isSet() { return tunnelingV1isSet; } - private void setTunnelingV1isSet(boolean tunnelingV1isSet) { + public void setTunnelingV1isSet(boolean tunnelingV1isSet) { this.tunnelingV1isSet = tunnelingV1isSet; logger.debug("{}={}", OPT_LONG_TUNNEL_V1, getTunnelingV2isSet()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index 99b4b330..8c45c287 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -162,19 +162,27 @@ private KNXNetworkLink doOpenLink() throws KNXException, // try unsecure TCP tunneling v2 connection try { - return createTunnelingLinkV2(local, remote, medium); + KNXNetworkLink testLink = createTunnelingLinkV2(local, remote, medium); + cliOptions.setTunnelingV2isSet(true); + return testLink; } catch (final KNXException e) { logger.info("failed with {}", e.toString()); + cliOptions.setTunnelingV2isSet(false); } // try unsecure UDP tunneling v1 connection with nat option set on cli try { - return createTunnelingLinkV1(local, remote, cliOptions.getNatIsSet(), medium); + KNXNetworkLink testLink = createTunnelingLinkV1(local, remote, cliOptions.getNatIsSet(), medium); + cliOptions.setTunnelingV1isSet(true); + return testLink; } catch (final KNXException e) { + cliOptions.setTunnelingV1isSet(false); logger.info("{}failed with {}{}", ansi().fg(YELLOW), e, ansi().reset()); } // last chance try unsecure UDP tunneling v1 connection with INVERTED nat option set on cli - return createTunnelingLinkV1(local, remote, !cliOptions.getNatIsSet(), medium); + KNXNetworkLink testLink = createTunnelingLinkV1(local, remote, !cliOptions.getNatIsSet(), medium); + cliOptions.setNatIsSet(!cliOptions.getNatIsSet()); + return testLink; } } \ No newline at end of file From 263aae55d23b3c9bc54fb4d721c1cfe60296cd9a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 17:56:15 +0100 Subject: [PATCH 214/359] [Updater] Add info for LogMx parser pattern --- firmware_updater/updater/src/resources/logback.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 0fbe63e3..85eaf0ac 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -29,6 +29,10 @@ false %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level [%logger] %method \(%line\) %message%n +
From c10fe9c2109b20b9ae1bad573cac4bd2e88095ec Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 17:58:55 +0100 Subject: [PATCH 215/359] [Updater] Disable `*-warn.html` appender MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With LogMx it´s easy to scan whole (sub)directories for warnings and more. So this appender is kinda redundant. --- .../updater/src/resources/logback.xml | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 85eaf0ac..a86635ac 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -46,25 +46,26 @@
- - ${application.home:-.}/log/updater-${appStartTimestamp}-warn.html - - WARN - - - - %thread%level%logger%msg - %d{HH:mm:ss.SSS}%thread%level%logger%method%line%message - - - - - + + - + From 70367cbee174e4d3d7429c905ac5765b0db55a32 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 18:05:20 +0100 Subject: [PATCH 216/359] [Updater] Move `KNXNetworkLink` creation to `DeviceManagement` --- .../org/selfbus/updater/DeviceManagement.java | 32 +++++++++++++------ .../src/org/selfbus/updater/Updater.java | 20 +++--------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 70ad3634..5a4e6774 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -18,6 +18,7 @@ import tuwien.auto.calimero.mgmt.ManagementProcedures; import tuwien.auto.calimero.mgmt.ManagementProceduresImpl; +import java.net.UnknownHostException; import java.time.Duration; import java.util.Arrays; @@ -58,32 +59,45 @@ public final class DeviceManagement implements AutoCloseable { private Destination progDestination; private KNXNetworkLink link; + private IndividualAddress progDevice; + + private CliOptions cliOptions; + private DeviceManagement () { setProtocolVersion(UDPProtocolVersion.UDP_V1); } - public DeviceManagement(KNXNetworkLink link, IndividualAddress progDevice, Priority priority) - throws KNXLinkClosedException { + public DeviceManagement(CliOptions cliOptions) throws KNXException, UpdaterException, UnknownHostException, + InterruptedException { this(); - this.link = link; - logger.debug("Creating SBManagementClientImpl"); + this.cliOptions = cliOptions; + } + public void open() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { + close(); + this.link = new SBKNXLink(this.cliOptions).openLink(); + this.progDevice = cliOptions.getProgDevicePhysicalAddress(); this.mc = new SBManagementClientImpl(this.link); - this.mc.setPriority(priority); + this.mc.setPriority(cliOptions.getPriority()); this.progDestination = this.mc.createDestination(progDevice, true, false, false); } @Override public void close() { + if (progDestination != null) { + logger.debug("Releasing progDestination {}", progDestination); + progDestination.close(); + } + if (mc != null) { - logger.debug("Releasing mc"); - mc.detach(); - mc.close(); + logger.debug("Releasing mc {}", mc); + mc.close(); // mc.close calls already mc.detach() } if (link != null) { - logger.debug("Releasing link"); + logger.debug("Releasing link {}", link); link.close(); } progDestination = null; + progDevice = null; mc = null; link = null; } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index a42600f6..785cb063 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -11,7 +11,6 @@ import org.selfbus.updater.upd.UDPProtocolVersion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.link.KNXNetworkLink; import java.net.UnknownHostException; @@ -153,7 +152,6 @@ public void run() { * @see java.lang.Runnable#run() */ public void run() { - KNXNetworkLink link = null; try { final String hexFileName = cliOptions.getFileName(); BinImage newFirmware = null; @@ -181,11 +179,10 @@ public void run() { System.out.println(); } - link = new SBKNXLink(cliOptions).openLink(); - - DeviceManagement dm = new DeviceManagement(link, cliOptions.getProgDevicePhysicalAddress(), cliOptions.getPriority()); + DeviceManagement dm = new DeviceManagement(cliOptions); logger.debug("Telegram priority: {}", cliOptions.getPriority()); + dm.open(); //for option --device restart the device in bootloader mode if (cliOptions.getDevicePhysicalAddress() != null) { // phys. knx address of the device in normal operation @@ -233,8 +230,7 @@ public void run() { } // to get here `uid == null` must be true, so it's fine to exit with no-error dm.close(); - link.close(); - System.exit(0); + return; } // store new firmware bin file in cache directory @@ -350,18 +346,11 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 logger.error("Operation did not finish."); } - finally { - if (link != null) { - link.close(); - } - } } public String requestUid() throws KNXException, UpdaterException, UnknownHostException { - KNXNetworkLink link; try { - link = new SBKNXLink(cliOptions).openLink(); - DeviceManagement dm = new DeviceManagement(link, cliOptions.getProgDevicePhysicalAddress(), cliOptions.getPriority()); + DeviceManagement dm = new DeviceManagement(cliOptions); //for option --device restart the device in bootloader mode if (cliOptions.getDevicePhysicalAddress() != null) { // phys. knx address of the device in normal operation @@ -387,7 +376,6 @@ public String requestUid() throws KNXException, UpdaterException, UnknownHostExc dm.restartProgrammingDevice(); } dm.close(); - link.close(); return uid; } catch (final InterruptedException e) { From f9d904eee360d0928aaa619aca2d9def5896a132 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 20:35:14 +0100 Subject: [PATCH 217/359] [Updater] Close `DeviceManagement` gracefully on [CTRL+C] --- .../src/org/selfbus/updater/Updater.java | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 785cb063..ab0ae8a1 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -39,6 +39,7 @@ private Updater() {} // disable default constructor private final static Logger logger = LoggerFactory.getLogger(Updater.class); private CliOptions cliOptions = null; + private static DeviceManagement dm = null; /** * Constructs an instance of the {@link #Updater} class. @@ -99,14 +100,18 @@ public static void main(final String[] args) { } final Updater updater = new Updater(options); - final ShutdownHandler shutDownHandler = new ShutdownHandler().register(); + final ShutdownHandler shutdownHandler = new ShutdownHandler().register(); updater.run(); - shutDownHandler.unregister(); + shutdownHandler.unregister(); } catch (KNXFormatException | ParseException | InterruptedException e) { logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 } finally { + if (dm != null) { + logger.debug("Closing DeviceManagement in main()"); + dm.close(); + } finalizeJansi(); logger.debug("main exit"); } @@ -117,15 +122,33 @@ private static final class ShutdownHandler extends Thread { ShutdownHandler register() { Runtime.getRuntime().addShutdownHook(this); + logger.trace("ShutdownHandler registered"); return this; } void unregister() { - Runtime.getRuntime().removeShutdownHook(this); + try { + Runtime.getRuntime().removeShutdownHook(this); + logger.trace("ShutdownHandler unregistered"); + } + catch (IllegalStateException ignored) { + // Shutdown is currently in progress + } } public void run() { - t.interrupt(); + try { + t.interrupt(); + logger.trace("ShutdownHandler called"); + if (dm != null) { + logger.trace("Closing DeviceManagement"); + dm.close(); + dm = null; + } + } + catch (Exception e) { + logger.error("Error while shutting down", e); + } } } @@ -179,7 +202,7 @@ public void run() { System.out.println(); } - DeviceManagement dm = new DeviceManagement(cliOptions); + dm = new DeviceManagement(cliOptions); logger.debug("Telegram priority: {}", cliOptions.getPriority()); dm.open(); @@ -338,14 +361,22 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres logger.info("Operation finished successfully."); } - catch (final InterruptedException e) { + catch (final InterruptedException | IllegalStateException e) { Thread.currentThread().interrupt(); - logger.info("Operation canceled."); + logger.info("{}Operation canceled.", System.lineSeparator()); + logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 } catch (Throwable e) { - logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 logger.error("Operation did not finish."); } + finally { + if (dm != null) { + logger.debug("Closing DeviceManagement"); + dm.close(); + finalizeJansi(); + } + } } public String requestUid() throws KNXException, UpdaterException, UnknownHostException { From c24e50eb879fc810fed208b331050b367b39ff39 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 20:47:27 +0100 Subject: [PATCH 218/359] [Updater] Delete redunant call of `.detch()` `mgmt.detach()` is already called by `mgmt.close()`. --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 5a4e6774..62d32955 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -453,7 +453,6 @@ public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throw try { ManagementProcedures mgmt = new ManagementProceduresImpl(link); IndividualAddress[] devices = mgmt.readAddress(); - mgmt.detach(); mgmt.close(); if ((devices.length == 0) && (progDeviceAddr == null)) { // no device in prog mode return; From 67d6c3894b36fad6e74392a71e8dcbfe13b8cd59 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 21:05:41 +0100 Subject: [PATCH 219/359] [Updater] Override `sendWait` for destination state logging --- .../updater/SBManagementClientImpl.java | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index 0a537dde..c2c69c10 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -11,7 +11,10 @@ import tuwien.auto.calimero.mgmt.ManagementClientImpl; import tuwien.auto.calimero.mgmt.TransportListener; +import java.time.Duration; + import static org.selfbus.updater.Mcu.MAX_ASDU_LENGTH; +import static tuwien.auto.calimero.mgmt.Destination.State.OpenIdle; /** * Extends the calimero-core class {@link ManagementClientImpl} @@ -186,17 +189,17 @@ public void linkClosed(final CloseEvent e) private static final int USERMSG_MANUFACTURER_6_RESPONSE = 0x2FE; private static final int apciWrite = USERMSG_MANUFACTURER_0_WRITE; private static final int apciResponse = USERMSG_MANUFACTURER_6_RESPONSE; - //private final Logger logger; // uncomment when needed + private final Logger logger; KNXNetworkLink link; public SBManagementClientImpl(KNXNetworkLink link) throws KNXLinkClosedException { super(link); + logger = (Logger) LoggerFactory.getLogger(SBManagementClientImpl.class.getName() + " " + link.getName()); this.link = link; SBLinkListener linkListener = new SBLinkListener(); this.link.addLinkListener(linkListener); - //logger = (Logger) LoggerFactory.getLogger(SBManagementClientImpl.class.getName() + " " + link.getName()); SBTransportListener transportListener = new SBTransportListener(); this.transportLayer().addTransportListener(transportListener); } @@ -225,4 +228,22 @@ public byte[] sendUpdateData(final Destination dst, final int cmd, final byte[] return this.sendWait(dst, getPriority(), send, apciResponse, 2, MAX_ASDU_LENGTH, responseTimeout()); } + + @Override + protected byte[] sendWait(final Destination d, final Priority p, final byte[] apdu, final int responseServiceType, + final int minAsduLen, final int maxAsduLen, final Duration timeout) throws KNXDisconnectException, + KNXTimeoutException, KNXInvalidResponseException, KNXLinkClosedException, InterruptedException { + byte[] received; + try { + received = super.sendWait(d, p, apdu, responseServiceType, minAsduLen, maxAsduLen, timeout); + } + catch (KNXTimeoutException e) { + if (d.getState() != OpenIdle) + { + logger.warn("Critical destination state: {}", d); + } + throw e; + } + return received; + } } From 2b4a6a9b743c1f5d6635ea4e23b2b583ad5d98f9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 21:26:42 +0100 Subject: [PATCH 220/359] [Updater] Move close logging to `DeviceManagement` --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 1 + firmware_updater/updater/src/org/selfbus/updater/Updater.java | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 62d32955..07dc5f83 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -83,6 +83,7 @@ public void open() throws KNXException, UpdaterException, UnknownHostException, @Override public void close() { + logger.debug("Closing {}", this.getClass().getSimpleName()); if (progDestination != null) { logger.debug("Releasing progDestination {}", progDestination); progDestination.close(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index ab0ae8a1..e555ac57 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -109,7 +109,6 @@ public static void main(final String[] args) { } finally { if (dm != null) { - logger.debug("Closing DeviceManagement in main()"); dm.close(); } finalizeJansi(); @@ -141,7 +140,6 @@ public void run() { t.interrupt(); logger.trace("ShutdownHandler called"); if (dm != null) { - logger.trace("Closing DeviceManagement"); dm.close(); dm = null; } @@ -372,7 +370,6 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } finally { if (dm != null) { - logger.debug("Closing DeviceManagement"); dm.close(); finalizeJansi(); } From cd9b147d5c7c9fc5a889a3f72879c5110c012fcd Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 12 Jan 2025 21:27:49 +0100 Subject: [PATCH 221/359] =?UTF-8?q?[Updater]=20Don=C2=B4t=20call=20`finali?= =?UTF-8?q?zeJansi()`=20in=20`run()`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firmware_updater/updater/src/org/selfbus/updater/Updater.java | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index e555ac57..9be24399 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -371,7 +371,6 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres finally { if (dm != null) { dm.close(); - finalizeJansi(); } } } From 38c28ae9a0e2d071927a65ef1ec1e071fc435b53 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 13 Jan 2025 19:15:36 +0100 Subject: [PATCH 222/359] [Updater] Cleanup dead code --- .../updater/SBManagementClientImpl.java | 42 ++----------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index c2c69c10..cbef8e0e 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -33,29 +33,6 @@ private CEMILData getCEMILData(final FrameEvent e, Logger eventLogger) { return linkData; } - /** - * Logs details about a frame event. - *

- * This method checks if the frame retrieved from the {@link FrameEvent} is of type {@link CEMILData}. - * If the frame is valid, it logs the provided message along with the frame data. - *

- * - * @param message A descriptive message to include in the log. - * @param e The {@link FrameEvent} containing the frame to be logged. - * @param eventLogger The logger to use for logging the frame details. - */ - private void logEvent(String message, final FrameEvent e, final Logger eventLogger) { - CEMILData linkData = getCEMILData(e, eventLogger); - if (linkData == null) { - return; - } - - eventLogger.debug("{} : {}", message, linkData); - if (linkData.getSource().equals(link.getKNXMedium().getDeviceAddress())) { - eventLogger.debug("source == device address!"); - } - } - private class SBLinkListener implements NetworkLinkListener { private final Logger linkLogger; @@ -146,22 +123,13 @@ private class SBTransportListener implements TransportListener } @Override - public void broadcast(final FrameEvent e) - { - // logEvent("broadcast", e, transportLogger); - } + public void broadcast(final FrameEvent e) {} @Override - public void dataConnected(final FrameEvent e) - { - // logEvent("dataConnected", e, transportLogger); - } + public void dataConnected(final FrameEvent e) {} @Override - public void dataIndividual(final FrameEvent e) - { - // logEvent("dataIndividual", e, transportLogger); - } + public void dataIndividual(final FrameEvent e) {} @Override public void disconnected(final Destination d) { @@ -169,9 +137,7 @@ public void disconnected(final Destination d) { } @Override - public void group(final FrameEvent e) { - // transportLogger.debug("group {}", e); - } + public void group(final FrameEvent e) {} @Override public void detached(final DetachEvent e) { From 96ba37abeca84fdc0883cf1cc9c237ad2d895bc5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 13 Jan 2025 19:16:00 +0100 Subject: [PATCH 223/359] [Updater] Simplify `if` --- .../selfbus/updater/SBManagementClientImpl.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index cbef8e0e..ec0c4900 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -92,17 +92,9 @@ else if (tpdu == 0x81) { } else { final KNXAddress dst = cemilData.getDestination(); - if (dst instanceof GroupAddress) - // check for broadcast or group - if (dst.getRawAddress() == 0) { - linkLogger.debug("broadcast {} -> {}", cemilData.getSource(), dst); - } - else { - // linkLogger.debug("group"); - } - else { - // individual - // linkLogger.debug("individual"); + // check for broadcast or group + if ((dst instanceof GroupAddress) && (dst.getRawAddress() == 0)) { + linkLogger.debug("broadcast {} -> {}", cemilData.getSource(), dst); } } } From e69acb7464b37b8dd6b1b0a5a534e6108033e72f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 14 Jan 2025 16:44:27 +0100 Subject: [PATCH 224/359] [Updater] Fix negative features --- .../src/org/selfbus/updater/bootloader/BootloaderIdentity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java index 38f6f4e2..e1a969de 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java @@ -40,7 +40,7 @@ private String hexVersionToString(long versionMajor, long versionMinor) { public static BootloaderIdentity fromArray(byte[] parse) { long vMajor = parse[0] & 0xff; long vMinor = parse[1] & 0xff; - long features = Utils.streamToShort(parse, 2); + long features = Utils.streamToShort(parse, 2) & 0xffff; long versionSBLibMajor = parse[4] & 0xff; long versionSBLibMinor = parse[5] & 0xff; long applicationFirstAddress = Utils.streamToLong(parse, 6); From c8549deac8c9d2fddfd398dd4984c45076d266c0 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 14 Jan 2025 19:41:03 +0100 Subject: [PATCH 225/359] [Updater] More detailed `--discover` output --- .../selfbus/updater/DiscoverKnxInterfaces.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java index 8145a07a..0524fcfa 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -64,20 +64,8 @@ public static Set getUsbInterfaces() throws InterruptedException { public static void toText(List> ifaceList) { for (Discoverer.Result r : ifaceList) { SearchResponse sr = r.response(); - String ifName = sr.getDevice().getName(); - String ifHostAddress = sr.getControlEndpoint().endpoint().getAddress().getHostAddress(); - IndividualAddress ifPhysAddress = sr.getDevice().getAddress(); - int ifPort = sr.getControlEndpoint().endpoint().getPort(); - int ifTunnelVersion; - if (sr.v2()) - ifTunnelVersion = 2; - else - ifTunnelVersion = 1; - - String ipInterfaceText = String.format("%s (%s:%d) v%d %s", ifName, ifHostAddress, ifPort, ifTunnelVersion, - ifPhysAddress.toString()); - logger.info("Found IP interface: {}", ipInterfaceText); - logger.info("\t{}", sr.toString()); + logger.info("Found IP interface: {}", sr.getDevice().getName()); + logger.info("\t{}", sr.toString().replace(", ", "\n\t")); } } From e59635c11c28662ed8520004c22c78e8ed66f9d3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 15 Jan 2025 11:51:55 +0100 Subject: [PATCH 226/359] [Updater] Reconstruct `--delay` if it differs from default value --- .../updater/src/org/selfbus/updater/CliOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index acfd897a..9966226c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -644,7 +644,7 @@ public String reconstructCommandLine() { if (!getUid().isBlank()) builder += String.format(" --%s %s", OPT_LONG_UID, getUid()); - if (getDelayMs() > 0) + if (getDelayMs() != Updater.DELAY_MIN) builder += String.format(" --%s %d", OPT_LONG_DELAY, getDelayMs()); if (getTunnelingV2isSet()) From 23744fb1839e6784207f12b24a7079958b2adb2b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 15 Jan 2025 12:01:56 +0100 Subject: [PATCH 227/359] [Updater] Reconnect KNX link on a `KNXException` Instead of trying to send the last telegram again, we try to close the KNXLink gracefully wait 2.5 seconds and connect again. - `KNXTimeoutException` can occur on missing TL4 and L2 ACKs. The KNX link may be closed anyways - `KNXDisconnectException` transport layer (TL4) is closed. - `KNXLinkClosedException` link is already closed --- .../org/selfbus/updater/DeviceManagement.java | 81 +++++++++++++++---- .../src/org/selfbus/updater/SBKNXLink.java | 4 +- .../src/org/selfbus/updater/Updater.java | 5 +- .../src/org/selfbus/updater/gui/GuiMain.java | 2 +- 4 files changed, 72 insertions(+), 20 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 07dc5f83..b560bbcf 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -67,11 +67,18 @@ private DeviceManagement () { setProtocolVersion(UDPProtocolVersion.UDP_V1); } - public DeviceManagement(CliOptions cliOptions) throws KNXException, UpdaterException, UnknownHostException, - InterruptedException { + public DeviceManagement(CliOptions cliOptions) { this(); this.cliOptions = cliOptions; } + + public void reconnect() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { + close(); + //todo 2500ms works quite fine, test with lower delays + Thread.sleep(2500); + open(); + } + public void open() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { close(); this.link = new SBKNXLink(this.cliOptions).openLink(); @@ -412,8 +419,42 @@ public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, return blStatistic; } - public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetry) - throws UpdaterException, InterruptedException, KNXLinkClosedException { + private boolean isLinkAlive() { + if (link == null) { + return false; + } + return link.isOpen(); + } + + private void handleKNXException(final UPDCommand command, final KNXException e, final boolean reconnect) throws + UpdaterException, InterruptedException { + if (LoggingManager.isConsoleActive()) { + System.out.println(); + } + + if (e instanceof KNXAckTimeoutException) { + // This exception is on missing/faulty ack at linklayer level thrown, but we never see it here + // todo check public void send(final CEMI frame, final BlockingMode mode) in ConnectionBase + logger.warn("{}Unexpected: never seen before {}{}", ansi().fg(RED), e.getClass().getSimpleName(),ansi().reset()); + } + + logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), + e.getClass().getSimpleName(), ansi().reset()); + + if (!reconnect) { + return; + } + + try { + reconnect(); + } + catch (KNXException | UnknownHostException e2) { + throw new UpdaterException(String.format("%s failed.", command), e); + } + } + + public ResponseResult sendWithRetry(final UPDCommand command, final byte[] data, int maxRetry) + throws UpdaterException, InterruptedException { ResponseResult result = new ResponseResult(); while (true) { KNXException lastCaughtException; @@ -422,22 +463,26 @@ public ResponseResult sendWithRetry(UPDCommand command, byte[] data, int maxRetr result.copyFromArray(data2); return result; } - catch (KNXTimeoutException e) { + ///\todo check causes of KNXRemoteException, i think they are unrecoverable + catch (KNXDisconnectException | KNXLinkClosedException e) { lastCaughtException = e; - logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), - e.getClass().getSimpleName(), ansi().reset()); - result.incTimeoutCount(); + result.incDropCount(); + handleKNXException(command, e, true); } - catch (KNXDisconnectException e) { ///\todo check causes of KNXRemoteException, if think they are unrecoverable + catch (KNXTimeoutException e) { + // Can happen on a L2 tunnel request ACK timeout or a TL4 ACK timeout lastCaughtException = e; - logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), - e.getClass().getSimpleName(), ansi().reset()); - result.incDropCount(); + result.incTimeoutCount(); + handleKNXException(command, e, true); } catch (KNXInvalidResponseException e) { lastCaughtException = e; - logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), - e.getClass().getSimpleName(), ansi().reset()); + handleKNXException(command, e, true); + } + finally { + if (!isLinkAlive()) { + maxRetry = 0; // exit while + } } if (maxRetry > 0) { @@ -510,4 +555,12 @@ public int getMaxPayload() { public int getMaxUpdCommandRetry() { return MAX_UPD_COMMAND_RETRY; } + + public String getLinkInfo() { + if (this.link == null) { + return "No link available."; + } + + return link.toString(); + } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index 8c45c287..12805999 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -116,9 +116,7 @@ private InetAddress resolveHost(final String host) throws UnknownHostException { */ public KNXNetworkLink openLink() throws KNXException, InterruptedException, UnknownHostException, UpdaterException { - KNXNetworkLink newLink = doOpenLink(); - logger.info("KNX connection: {}", newLink); - return newLink; + return doOpenLink(); } private KNXNetworkLink doOpenLink() throws KNXException, diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 9be24399..668b4753 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -204,6 +204,7 @@ public void run() { logger.debug("Telegram priority: {}", cliOptions.getPriority()); dm.open(); + logger.info("KNX connection: {}", dm.getLinkInfo()); //for option --device restart the device in bootloader mode if (cliOptions.getDevicePhysicalAddress() != null) { // phys. knx address of the device in normal operation @@ -375,7 +376,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } } - public String requestUid() throws KNXException, UpdaterException, UnknownHostException { + public String requestUid() throws KNXException, UpdaterException { try { DeviceManagement dm = new DeviceManagement(cliOptions); @@ -410,7 +411,7 @@ public String requestUid() throws KNXException, UpdaterException, UnknownHostExc Thread.currentThread().interrupt(); return ""; } - catch (UpdaterException | KNXException | UnknownHostException e) { + catch (UpdaterException | KNXException e) { logger.error("{}An error occurred while retrieving the UID. {}{}{}", ansi().fg(RED), System.lineSeparator(), e, ansi().reset()); throw e; diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 560440b4..87529b45 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -192,7 +192,7 @@ private void handleRequestUidAction() { final Updater upd = new Updater(cliOptions); SwingUtilities.invokeLater(this::beforeUidRequest); uid = upd.requestUid(); - } catch (ParseException | UnknownHostException | UpdaterException | KNXException e) { + } catch (ParseException | UpdaterException | KNXException e) { logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 JOptionPane.showMessageDialog(this, String.format(getTranslation("Exception.requestUidAction.Message"), e), From 7336d5308e9e5aeb6afd7583b7908feb4dc0b06d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 15 Jan 2025 12:09:51 +0100 Subject: [PATCH 228/359] [Updater] Add cli option `--reconnect xx` to set length of pause on KNX link reconnect --- firmware_updater/updater/README.md | 6 ++- .../src/org/selfbus/updater/CliOptions.java | 50 +++++++++++++++++++ .../org/selfbus/updater/DeviceManagement.java | 2 +- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 01123dbf..1c6299a5 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -23,8 +23,8 @@ java -jar SB_updater-x.xx-all.jar [-f ] [-m ] | --usb ] [-d ] [-D ] [-o ] [--priority ] [-bs <256|512|1024>] [--user ] [--user-pwd ] [--device-pwd ] [-u ] [-f1] [-H ] [-P ] [-p ] [-t2 - | -t1 | -r] [-n] [-h | -v] [--delay ] [-l ] [--ERASEFLASH] - [--DUMPFLASH ] [-f0] [--statistic] [--discover] + | -t1 | -r] [-n] [-h | -v] [--delay ] [-l ] [--reconnect ] + [--ERASEFLASH] [--DUMPFLASH ] [-f0] [--statistic] [--discover] Selfbus KNX-Firmware update tool options: -f,--fileName Filename of hex file to program @@ -65,6 +65,8 @@ Selfbus KNX-Firmware update tool options: --delay delay telegrams during data transmission to reduce bus load, valid 0-500ms, default 0 -l,--logLevel Logfile logging level [TRACE|DEBUG|INFO] (default TRACE) + --reconnect pause between a KNX connection reconnect, valid + 0-6000ms, default 0 --ERASEFLASH USE WITH CAUTION! Erases the complete flash memory including the physical KNX address and all settings of the device. Only the bootloader is not deleted. diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 9966226c..0e9ac116 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -79,6 +79,7 @@ public class CliOptions { public static final String OPT_LONG_UID = "uid"; public static final String OPT_LONG_DELAY = "delay"; + public static final String OPT_LONG_RECONNECT = "reconnect"; private static final String OPT_SHORT_TUNNEL_V2 = "t2"; public static final String OPT_LONG_TUNNEL_V2 = "tunnelingv2"; @@ -156,6 +157,7 @@ public class CliOptions { private String uid = ""; private boolean flashingFullModeIsSet = false; private int delayMs = 0; + private int reconnectMs = 0; private boolean noFlashIsSet = false; private boolean eraseFullFlashIsSet = false; @@ -171,6 +173,9 @@ public class CliOptions { private boolean discoverIsSet = false; + private static final int RECONNECT_MIN_MS = 0; + private static final int RECONNECT_MAX_MS = 6000; + private CliOptions() { defaultLogLevel = getLogLevel(); @@ -268,6 +273,13 @@ private CliOptions() { .required(false) .type(Number.class) .desc(String.format("delay telegrams during data transmission to reduce bus load, valid 0-500ms, default %d", Updater.DELAY_MIN)).build(); + Option reconnect = Option.builder(null).longOpt(OPT_LONG_RECONNECT) + .argName("ms") + .numberOfArgs(1) + .required(false) + .type(Number.class) + .desc(String.format("pause between a KNX connection reconnect, valid %d-%dms, default %d", RECONNECT_MIN_MS, + RECONNECT_MAX_MS, RECONNECT_MIN_MS)).build(); Option logLevel = Option.builder(OPT_SHORT_LOGLEVEL).longOpt(OPT_LONG_LOGLEVEL) .argName("TRACE|DEBUG|INFO") .numberOfArgs(1) @@ -356,6 +368,7 @@ private CliOptions() { cliOptions.addOption(delay); cliOptions.addOption(logLevel); + cliOptions.addOption(reconnect); cliOptions.addOption(eraseFlash); cliOptions.addOption(dumpFlash); cliOptions.addOption(NO_FLASH); @@ -453,6 +466,11 @@ private void parse(final String[] args) throws ParseException, KNXFormatExceptio else setDelayMs(Updater.DELAY_MIN); + if (cmdLine.hasOption(OPT_LONG_RECONNECT)) + setReconnectMs(cmdLine.getOptionValue(OPT_LONG_RECONNECT)); + else + setReconnectMs(RECONNECT_MIN_MS); + if (cmdLine.hasOption(OPT_LONG_BLOCKSIZE)) setBlockSize(cmdLine.getOptionValue(OPT_LONG_BLOCKSIZE)); else @@ -647,6 +665,9 @@ public String reconstructCommandLine() { if (getDelayMs() != Updater.DELAY_MIN) builder += String.format(" --%s %d", OPT_LONG_DELAY, getDelayMs()); + if (getReconnectMs() != RECONNECT_MIN_MS) + builder += String.format(" --%s %d", OPT_LONG_RECONNECT, getReconnectMs()); + if (getTunnelingV2isSet()) builder += String.format(" --%s", OPT_LONG_TUNNEL_V2); @@ -1152,4 +1173,33 @@ public void setDiscoverIsSet(boolean discoverIsSet) { this.discoverIsSet = discoverIsSet; logger.debug("{}={}", OPT_LONG_DISCOVER, this.discoverIsSet); } + + public int getReconnectMs() { + return reconnectMs; + } + + private void setReconnectMs(int reconnectMs) { + if ((reconnectMs < RECONNECT_MIN_MS) || (reconnectMs > RECONNECT_MAX_MS)) { + logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", + ansi().fg(RED), OPT_LONG_RECONNECT, reconnectMs, RECONNECT_MIN_MS, + RECONNECT_MAX_MS, RECONNECT_MAX_MS, ansi().reset()); + reconnectMs = RECONNECT_MIN_MS; // set to RECONNECT_MIN_MS in case of invalid time + } + this.reconnectMs = reconnectMs; + logger.debug("{}={}", OPT_LONG_RECONNECT, getReconnectMs()); + } + + private void setReconnectMs(String reconnectMs) { + try { + if (reconnectMs == null || reconnectMs.isBlank()) + setReconnectMs(RECONNECT_MIN_MS); + else + setReconnectMs(Integer.parseInt(reconnectMs)); + } + catch (NumberFormatException e) { + setReconnectMs(RECONNECT_MIN_MS); + logger.warn("{}option --{} {} is invalid => set to default {}{}", + ansi().fg(RED), OPT_LONG_RECONNECT, reconnectMs, getReconnectMs(), ansi().reset()); + } + } } \ No newline at end of file diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index b560bbcf..669155ef 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -75,7 +75,7 @@ public DeviceManagement(CliOptions cliOptions) { public void reconnect() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { close(); //todo 2500ms works quite fine, test with lower delays - Thread.sleep(2500); + Thread.sleep(cliOptions.getReconnectMs()); open(); } From 1973d45e7ac311736392be4837e6daceb74c35e4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 15 Jan 2025 12:16:15 +0100 Subject: [PATCH 229/359] [Updater] Fix reconnect logging --- .../updater/src/org/selfbus/updater/CliOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 0e9ac116..9eab1f3b 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -1182,7 +1182,7 @@ private void setReconnectMs(int reconnectMs) { if ((reconnectMs < RECONNECT_MIN_MS) || (reconnectMs > RECONNECT_MAX_MS)) { logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", ansi().fg(RED), OPT_LONG_RECONNECT, reconnectMs, RECONNECT_MIN_MS, - RECONNECT_MAX_MS, RECONNECT_MAX_MS, ansi().reset()); + RECONNECT_MAX_MS, RECONNECT_MIN_MS, ansi().reset()); reconnectMs = RECONNECT_MIN_MS; // set to RECONNECT_MIN_MS in case of invalid time } this.reconnectMs = reconnectMs; From 655bb0a496738eb17c2cf739d0f58a3b2db0ae56 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 15 Jan 2025 12:43:05 +0100 Subject: [PATCH 230/359] [Updater] Cleanup not thrown exceptions and unused imports --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 4 ++-- .../src/org/selfbus/updater/DiscoverKnxInterfaces.java | 1 - firmware_updater/updater/src/org/selfbus/updater/Updater.java | 2 -- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 1 - 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 669155ef..d21c64cf 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -229,7 +229,7 @@ public BootDescriptor requestBootDescriptor() return bootDescriptor; } - public String requestAppVersionString() throws UpdaterException, KNXLinkClosedException, + public String requestAppVersionString() throws UpdaterException, InterruptedException { byte[] result = sendWithRetry(UPDCommand.APP_VERSION_REQUEST, new byte[0], getMaxUpdCommandRetry()).data(); UPDCommand command = UPDCommand.tryFromByteArray(result); @@ -404,7 +404,7 @@ public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) } } - public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, KNXLinkClosedException, + public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, InterruptedException { logger.debug("Requesting Bootloader statistic"); byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java index 0524fcfa..261b58a5 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -2,7 +2,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.IndividualAddress; import tuwien.auto.calimero.KNXException; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 668b4753..70422bb3 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -12,8 +12,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.net.UnknownHostException; - import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; import static org.selfbus.updater.LoggingManager.CONSOLE_APPENDER_NAME; diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 87529b45..fd9e0027 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -26,7 +26,6 @@ import java.io.InputStream; import java.lang.reflect.Method; import java.net.InetSocketAddress; -import java.net.UnknownHostException; import java.util.*; import java.util.List; From a125401815312cda974ba6e77c8f5d2623f3017a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 15 Jan 2025 19:20:31 +0100 Subject: [PATCH 231/359] [Updater] Bump version to 1.23 --- firmware_updater/updater/README.md | 2 +- firmware_updater/updater/build.gradle | 2 +- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 1c6299a5..441a206d 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -1,4 +1,4 @@ -# Selfbus-Updater 1.22 +# Selfbus-Updater 1.23 ## Requirements diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 28eac3b4..14c73cae 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.selfbus' -version = '1.22' ///\todo also change in ../README.md and ToolInfo.java (String version) +version = '1.23' ///\todo also change in ../README.md and ToolInfo.java (String version) description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 1c62b604..4d061d75 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -12,7 +12,7 @@ public final class ToolInfo { private static final long versionMajor = 1; ///\todo change also in ../README.md and build.gradle - private static final long versionMinor = 22; + private static final long versionMinor = 23; private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md private static final long minMinorVersionBootloader = 0; From 799838d11630c5f66645e3fdb16491662d35094b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 16 Jan 2025 10:46:23 +0100 Subject: [PATCH 232/359] [Updater] Gui todos for `--discover` and `--reconnect` --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index fd9e0027..990fa45d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -379,6 +379,8 @@ private CliOptions getCliOptions() throws KNXFormatException, ParseException { if (checkBox*.isVisible() && checkBox*.isSelected()) // todo add gui checkbox --discover argsList.add(argument(OPT_LONG_DISCOVER)); */ + // argsList.add(argument(OPT_LONG_RECONNECT, textFieldReconnect)); // todo add gui textfield --reconnect + // todo add gui button for --discover return new CliOptions(argsList); } From 20802dd6100397e2b15f56ceade5e4c4e21f4395 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 16 Jan 2025 10:50:36 +0100 Subject: [PATCH 233/359] [Updater] Extra newline in console progress info if running in IntelliJ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IntelliJ debug-console doesn´t support ansi cursor movement. Adding an extra newline avoids endless horizontal scrolling. --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 5 +++++ .../updater/src/org/selfbus/updater/LoggingManager.java | 7 +++++++ .../updater/src/org/selfbus/updater/Updater.java | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index d21c64cf..4ed6fc0f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -356,6 +356,11 @@ public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { // console output if (LoggingManager.isConsoleActive()) { System.out.print(logText); + if (LoggingManager.isRunningInIntelliJ()) { + // if running in idea debug-console we need to add a newline, + // because the idea debug-console doesn´t support ansi cursor movements + System.out.println(); + } } // gui JTextPane output logText = ansi().cursorUpLine().toString() + logText; // need this CursorUp in gui diff --git a/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java index f3086eca..d028f489 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.management.ManagementFactory; public final class LoggingManager { private final static Logger logger = LoggerFactory.getLogger(LoggingManager.class); @@ -83,4 +84,10 @@ public static Level getAppenderLogLevel(String appenderName) { public static boolean isConsoleActive() { return (LoggingManager.getAppenderLogLevel(CONSOLE_APPENDER_NAME) == Level.INFO); } + + public static boolean isRunningInIntelliJ() { + // Check for IntelliJ debugging JVM arguments + return ManagementFactory.getRuntimeMXBean().getInputArguments().stream() + .anyMatch(arg -> arg.contains("javaagent") || arg.contains("agentlib")); + } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 70422bb3..22d87e05 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -45,6 +45,12 @@ private Updater() {} // disable default constructor * @param cliOptions the command-line options to be used */ public Updater(CliOptions cliOptions) { + if (LoggingManager.isRunningInIntelliJ()) { + logger.info("IntelliJ detected => adding newline to console progress logging"); + } + else { + logger.debug("IntelliJ not detected => normal console logging active"); + } logger.debug(ToolInfo.getFullInfo()); logger.debug(Settings.getLibraryHeader(false)); this.cliOptions = cliOptions; From a4622d81bee8a26aa101c8d8d0035ad5fe237d39 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 16 Jan 2025 17:10:17 +0100 Subject: [PATCH 234/359] [Updater] Fix dm/link was not opened in`requestUid` --- .../updater/src/org/selfbus/updater/Updater.java | 5 ++++- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 22d87e05..845ae174 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -19,6 +19,8 @@ import org.selfbus.updater.gui.GuiMain; +import java.net.UnknownHostException; + /** * A Tool for updating the firmware of a Selfbus device in a KNX network. *

@@ -380,9 +382,10 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } } - public String requestUid() throws KNXException, UpdaterException { + public String requestUid() throws KNXException, UpdaterException, UnknownHostException { try { DeviceManagement dm = new DeviceManagement(cliOptions); + dm.open(); //for option --device restart the device in bootloader mode if (cliOptions.getDevicePhysicalAddress() != null) { // phys. knx address of the device in normal operation diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 990fa45d..4f9f6c1d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -26,6 +26,7 @@ import java.io.InputStream; import java.lang.reflect.Method; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.*; import java.util.List; @@ -191,7 +192,7 @@ private void handleRequestUidAction() { final Updater upd = new Updater(cliOptions); SwingUtilities.invokeLater(this::beforeUidRequest); uid = upd.requestUid(); - } catch (ParseException | UpdaterException | KNXException e) { + } catch (ParseException | UpdaterException | KNXException | UnknownHostException e) { logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 JOptionPane.showMessageDialog(this, String.format(getTranslation("Exception.requestUidAction.Message"), e), From bf7b80501f8f9fa8c0fcdaeafc1e1b87fe619545 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 16 Jan 2025 17:11:56 +0100 Subject: [PATCH 235/359] =?UTF-8?q?[Updater]=20Don=C2=B4t=20change=20conso?= =?UTF-8?q?le=20log=20level=20while=20debugging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../updater/src/org/selfbus/updater/Updater.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 845ae174..c7d141eb 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -71,8 +71,10 @@ public static void main(final String[] args) { initJansi(); if (args.length == 0) { - logger.info("GUI start => console output set to log level {}", Level.WARN); - LoggingManager.setAppenderLogLevel(CONSOLE_APPENDER_NAME, Level.WARN); + if (!LoggingManager.isRunningInIntelliJ()) { + logger.info("GUI start => console output set to log level {}", Level.WARN); + LoggingManager.setAppenderLogLevel(CONSOLE_APPENDER_NAME, Level.WARN); + } GuiMain.startSwingGui(); finalizeJansi(); return; From 4690a5dae2abbec5899cbfcc18154c0baa952773 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 16 Jan 2025 21:03:56 +0100 Subject: [PATCH 236/359] [Updater] Prettier exception logging --- .../updater/src/org/selfbus/updater/SBKNXLink.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index 12805999..e69f48e0 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -164,7 +164,7 @@ private KNXNetworkLink doOpenLink() throws KNXException, cliOptions.setTunnelingV2isSet(true); return testLink; } catch (final KNXException e) { - logger.info("failed with {}", e.toString()); + logger.info("failed with {}: {}", e.getClass().getSimpleName(), e.getMessage()); cliOptions.setTunnelingV2isSet(false); } @@ -175,7 +175,8 @@ private KNXNetworkLink doOpenLink() throws KNXException, return testLink; } catch (final KNXException e) { cliOptions.setTunnelingV1isSet(false); - logger.info("{}failed with {}{}", ansi().fg(YELLOW), e, ansi().reset()); + logger.info("{}failed with {}: {}{}", ansi().fg(YELLOW), e.getClass().getSimpleName(), e.getMessage(), + ansi().reset()); } // last chance try unsecure UDP tunneling v1 connection with INVERTED nat option set on cli From 88b4b40e5f265bb5ab8b6a10b5aa3781c36cadd8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 5 Feb 2025 13:24:58 +0100 Subject: [PATCH 237/359] [Updater] Prepare logging for calimero-core 3.0 calimero-core 3.0 uses `System.Logger`. `slf4j-jdk-platform-logging` "redirects" it logback. --- firmware_updater/updater/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 14c73cae..c5b61b1b 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -129,6 +129,9 @@ dependencies { // console and file logging implementation 'ch.qos.logback:logback-classic:1.5.16' + // Redirect System.Logger (used by calimero >=3.0) to slf4J (logback) + implementation 'org.slf4j:slf4j-jdk-platform-logging:2.0.16' + // Console ansi color support implementation 'org.jline:jline-terminal-jansi:3.28.0' From f4637344ff61218c42cf7e04821ad4e5c1bb29d8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 9 Feb 2025 16:33:11 +0100 Subject: [PATCH 238/359] [Updater] Log usb discover exception --- .../src/org/selfbus/updater/DiscoverKnxInterfaces.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java index 261b58a5..523d6547 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -48,7 +48,9 @@ public static Set getUsbInterfaces() throws InterruptedException { } catch (KNXException e) { iterator.remove(); - logger.warn("error: reading KNX device descriptor of {} ({})", d, e.getMessage()); + logger.warn("Reading KNX device descriptor of {} failed. ({}: {})", + d, e.getClass().getSimpleName(), e.getMessage()); + logger.debug("", e); } } From dc30ec55bb4177bfa2bcb33f119d74b6f84554c9 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 10 Feb 2025 01:27:13 +0100 Subject: [PATCH 239/359] [Updater] Fix device locked error message --- .../updater/src/org/selfbus/updater/upd/UDPResult.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java index be353ac8..00acd11d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java @@ -52,7 +52,7 @@ public enum UDPResult { /** The programmed application is not startable */ APPLICATION_NOT_STARTABLE((byte)0x59, "Application not startable", true), /** The device is locked */ - DEVICE_LOCKED((byte)0x58, "Device locked. Programming mode on device must be active!", true), + DEVICE_LOCKED((byte)0x58, "Device locked. Device must first be unlocked with UPDCommand::UNLOCK_DEVICE!", true), /** UID sent to unlock the device is invalid */ UID_MISMATCH((byte)0x57, "UID mismatch", true), /** Flash page erase failed */ From d750d4c1a8e1143bfee906525c39d20cee0f81a1 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 10 Feb 2025 01:42:18 +0100 Subject: [PATCH 240/359] =?UTF-8?q?[Updater]=20Don=C2=B4t=20restart=20boot?= =?UTF-8?q?loader=20device=20on=20exception=20in=20full=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restarting the bootloader device on an exception while in full flash mode may result in a non response bootloader device. --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 4ed6fc0f..4f4fc4d4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -320,7 +320,6 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo result.addCounters(tmp); if (UPDProtocol.checkResult(tmp.data(), false) != UDPResult.IAP_SUCCESS) { - restartProgrammingDevice(); throw new UpdaterException("doFlash failed."); } updateProgressInfo(progressInfo, txBuffer.length - updSendDataOffset); From 1ef844c3955ed9900799fe03fb21f232a7731534 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 10 Feb 2025 20:15:31 +0100 Subject: [PATCH 241/359] [Updater] Reduce loglevel of `Critical destination state` --- .../updater/src/org/selfbus/updater/SBManagementClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index ec0c4900..b18cb740 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -198,7 +198,7 @@ protected byte[] sendWait(final Destination d, final Priority p, final byte[] ap catch (KNXTimeoutException e) { if (d.getState() != OpenIdle) { - logger.warn("Critical destination state: {}", d); + logger.debug("Critical destination state: {}", d); } throw e; } From ee0af54b8e2380b4ceaa1b811ae422d0ab93aea4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 01:12:21 +0100 Subject: [PATCH 242/359] [Updater] Change valid `--reconnect` range to 100-12500ms In total the BL may need up to 12 seconds to close its TL4 connection: 1) BL sends T_Data_Individual => 3s timeout 2) BL sends 1. repeated T_Data_Individual => 3s timeout 3) BL sends 2. repeated T_Data_Individual => 3s timeout 4) BL sends 3. repeated T_Data_Individual => 3s timeout 5) BL sends T_Disconnect --- firmware_updater/updater/README.md | 2 +- .../updater/src/org/selfbus/updater/CliOptions.java | 8 ++++---- .../src/org/selfbus/updater/DeviceManagement.java | 11 +++++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 441a206d..c299823c 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -66,7 +66,7 @@ Selfbus KNX-Firmware update tool options: load, valid 0-500ms, default 0 -l,--logLevel Logfile logging level [TRACE|DEBUG|INFO] (default TRACE) --reconnect pause between a KNX connection reconnect, valid - 0-6000ms, default 0 + 100 - 12500ms, default 100 --ERASEFLASH USE WITH CAUTION! Erases the complete flash memory including the physical KNX address and all settings of the device. Only the bootloader is not deleted. diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 9eab1f3b..5664b710 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -157,7 +157,7 @@ public class CliOptions { private String uid = ""; private boolean flashingFullModeIsSet = false; private int delayMs = 0; - private int reconnectMs = 0; + private int reconnectMs = RECONNECT_MIN_MS; private boolean noFlashIsSet = false; private boolean eraseFullFlashIsSet = false; @@ -173,8 +173,8 @@ public class CliOptions { private boolean discoverIsSet = false; - private static final int RECONNECT_MIN_MS = 0; - private static final int RECONNECT_MAX_MS = 6000; + private static final int RECONNECT_MIN_MS = 100; + private static final int RECONNECT_MAX_MS = 12500; private CliOptions() { defaultLogLevel = getLogLevel(); @@ -278,7 +278,7 @@ private CliOptions() { .numberOfArgs(1) .required(false) .type(Number.class) - .desc(String.format("pause between a KNX connection reconnect, valid %d-%dms, default %d", RECONNECT_MIN_MS, + .desc(String.format("pause between a KNX connection reconnect, valid %d - %dms, default %d", RECONNECT_MIN_MS, RECONNECT_MAX_MS, RECONNECT_MIN_MS)).build(); Option logLevel = Option.builder(OPT_SHORT_LOGLEVEL).longOpt(OPT_LONG_LOGLEVEL) .argName("TRACE|DEBUG|INFO") diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 4f4fc4d4..c85818de 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -72,10 +72,13 @@ public DeviceManagement(CliOptions cliOptions) { this.cliOptions = cliOptions; } - public void reconnect() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { + public void reconnect(int waitMs) throws KNXException, UpdaterException, UnknownHostException, InterruptedException { close(); - //todo 2500ms works quite fine, test with lower delays - Thread.sleep(cliOptions.getReconnectMs()); + if (waitMs <= 0) { + waitMs = 100; + } + logger.info("Reconnecting in {} ms", waitMs); + Thread.sleep(waitMs); open(); } @@ -450,7 +453,7 @@ private void handleKNXException(final UPDCommand command, final KNXException e, } try { - reconnect(); + reconnect(cliOptions.getReconnectMs()); } catch (KNXException | UnknownHostException e2) { throw new UpdaterException(String.format("%s failed.", command), e); From 60edebfbd51e201c584479815e47ebd4fa1d0d75 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 01:14:47 +0100 Subject: [PATCH 243/359] [Updater] Prettier logging --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index c85818de..48e95777 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -354,6 +354,7 @@ public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset(), progressInfo, AnsiCursor.on()); + logText += " "; // append one space, just in case an exception message may come up // console output if (LoggingManager.isConsoleActive()) { @@ -435,10 +436,6 @@ private boolean isLinkAlive() { private void handleKNXException(final UPDCommand command, final KNXException e, final boolean reconnect) throws UpdaterException, InterruptedException { - if (LoggingManager.isConsoleActive()) { - System.out.println(); - } - if (e instanceof KNXAckTimeoutException) { // This exception is on missing/faulty ack at linklayer level thrown, but we never see it here // todo check public void send(final CEMI frame, final BlockingMode mode) in ConnectionBase From 5bfb926a63b06d1fde202e4b7620a9d273a4e88f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 01:58:47 +0100 Subject: [PATCH 244/359] [Updater] Return also build date and commit sha in `getToolAndVersion` --- .../src/org/selfbus/updater/ToolInfo.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 4d061d75..7d80fc8a 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -6,6 +6,8 @@ import org.slf4j.LoggerFactory; import tuwien.auto.calimero.Settings; +import java.io.IOException; + /** * Provides information about the application */ @@ -56,11 +58,11 @@ public static String getTool() { public static String getFullInfo() { return getToolAndVersion() + String.format("%s%s%s%s", System.lineSeparator(), desc, - System.lineSeparator(),author); + System.lineSeparator(), author); } public static String getToolAndVersion() { - return String.format("%s %s", getTool(), getVersion()); + return String.format("%s %s (build %s)", getTool(), getVersion(), getManifestInfo()); } public static void showVersion() { @@ -80,4 +82,21 @@ public static String minVersionBootloader() { public static String getToolJarName() { return String.format("SB_updater-%s-all.jar", ToolInfo.getVersion()); } + + private static String getManifestInfo() { + try (final var metaInf = ToolInfo.class.getResourceAsStream("/META-INF/MANIFEST.MF")) { + if (metaInf == null) { + return ""; + } + + final var manifest = new java.util.jar.Manifest(metaInf); + final var attributes = manifest.getMainAttributes(); + return String.format("%s sha %s", + attributes.getValue("Build-Date"), attributes.getValue("Revision")); + } + catch (IOException ignore) { + + } + return ""; + } } From c698f4114425249bb3bf2950600b4f1a10f03434 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 14:13:12 +0100 Subject: [PATCH 245/359] [Updater] Add customizable exception/message filters for console output Following messages will be filtered out in console output (`MessageEvaluator`): - received service acknowledgment status sequence number out of order Following exceptions will be filtered out in console output (`ThrowableEvaluator`): - KNXAckTimeoutException: maximum send attempts, no service acknowledgment received - KNXTimeoutException: no confirmation reply received for DM prop-read.req objtype 8 instance 1 pid 51 start 1 elements 1 --- .../updater/logging/LevelEventEvaluator.java | 28 +++++++++++ .../updater/logging/MessageEvaluator.java | 29 +++++++++++ .../updater/logging/ThrowableEvaluator.java | 50 +++++++++++++++++++ .../updater/src/resources/logback.xml | 19 +++++++ 4 files changed, 126 insertions(+) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/LevelEventEvaluator.java create mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java create mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/LevelEventEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LevelEventEvaluator.java new file mode 100644 index 00000000..b3321a3d --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/LevelEventEvaluator.java @@ -0,0 +1,28 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.boolex.EventEvaluatorBase; + +import static ch.qos.logback.classic.Level.TRACE; + +public class LevelEventEvaluator extends EventEvaluatorBase { + + private Level level = TRACE; + + @Override + public boolean evaluate(ILoggingEvent event) throws NullPointerException { + Level eventLevel = event.getLevel(); + return !eventLevel.isGreaterOrEqual(getLevel()); + } + + @SuppressWarnings("unused") + public Level getLevel() { + return level; + } + + @SuppressWarnings("unused") + public void setLevel(Level level) { + this.level = level; + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java new file mode 100644 index 00000000..52ae0d30 --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java @@ -0,0 +1,29 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.spi.ILoggingEvent; + +import java.util.List; +; + +public class MessageEvaluator extends LevelEventEvaluator { + + // Customizable List of log messages to filter out + private static final List FILTERED_MESSAGES = List.of( + "received service acknowledgment status sequence number out of order" + ); + + @Override + public boolean evaluate(ILoggingEvent event) throws NullPointerException { + if (super.evaluate(event)) { + return true; + } + + String message = event.getFormattedMessage(); + for (String filteredMessage : FILTERED_MESSAGES) { + if (message.contains(filteredMessage)) { + return false; // Filter this event out + } + } + return true; + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java new file mode 100644 index 00000000..6fc94fda --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java @@ -0,0 +1,50 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.ThrowableProxy; +import tuwien.auto.calimero.KNXAckTimeoutException; +import tuwien.auto.calimero.KNXTimeoutException; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class ThrowableEvaluator extends LevelEventEvaluator { + + // Customizable Map of exception classes and corresponding messages to filter out + Map, List> exceptionFilterMap = Map.of( + KNXAckTimeoutException.class, List.of( + "maximum send attempts, no service acknowledgment received" + ), + KNXTimeoutException.class, List.of( + "no confirmation reply received for DM prop-read.req objtype 8 instance 1 pid 51 start 1 elements 1" + ) + ); + + @Override + public boolean evaluate(ILoggingEvent event) throws NullPointerException { + if (super.evaluate(event)) { + return true; // Ignore log level + } + + ThrowableProxy throwableProxy = (ThrowableProxy) event.getThrowableProxy(); + if (throwableProxy == null) { + return true; // no stacktrace present + } + + Throwable throwable = throwableProxy.getThrowable(); + // Check if the current exception is present in the filter map + for (Map.Entry, List> exceptionFilter : exceptionFilterMap.entrySet()) { + if (exceptionFilter.getKey().isInstance(throwable)) { + // Check if the exception message matches one of the filtered messages + for (String filteredMessage : exceptionFilter.getValue()) { + if (Objects.equals(throwable.getMessage(), filteredMessage)) { + return false; // Filter this event out + } + } + } + } + return true; + } + +} diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index a86635ac..6c95e93a 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -4,6 +4,9 @@ + + + + + + WARN + + DENY + NEUTRAL + + + + + WARN + + DENY + NEUTRAL + %message%n From c5038939d330644f1c9bfd8a1a41b540b60bea1e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 14:38:08 +0100 Subject: [PATCH 246/359] [Updater] Move logging stuff to `.logging` --- .../org/selfbus/updater/DeviceManagement.java | 2 + .../src/org/selfbus/updater/Updater.java | 3 +- .../src/org/selfbus/updater/gui/GuiMain.java | 1 + .../{ => logging}/ListTextAppenders.java | 132 ++++++------- .../updater/{ => logging}/LoggingManager.java | 186 +++++++++--------- .../updater/src/resources/logback.xml | 2 +- 6 files changed, 165 insertions(+), 161 deletions(-) rename firmware_updater/updater/src/org/selfbus/updater/{ => logging}/ListTextAppenders.java (95%) rename firmware_updater/updater/src/org/selfbus/updater/{ => logging}/LoggingManager.java (96%) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index 48e95777..be1aa18c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -4,6 +4,8 @@ import org.selfbus.updater.bootloader.BootDescriptor; import org.selfbus.updater.bootloader.BootloaderIdentity; import org.selfbus.updater.bootloader.BootloaderStatistic; +import org.selfbus.updater.logging.ListTextAppenders; +import org.selfbus.updater.logging.LoggingManager; import org.selfbus.updater.upd.UDPProtocolVersion; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index c7d141eb..687bc164 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -4,6 +4,7 @@ import org.apache.commons.cli.ParseException; import org.fusesource.jansi.AnsiConsole; import org.selfbus.updater.bootloader.BootloaderStatistic; +import org.selfbus.updater.logging.LoggingManager; import tuwien.auto.calimero.*; import org.selfbus.updater.bootloader.BootDescriptor; import org.selfbus.updater.bootloader.BootloaderIdentity; @@ -14,7 +15,7 @@ import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; -import static org.selfbus.updater.LoggingManager.CONSOLE_APPENDER_NAME; +import static org.selfbus.updater.logging.LoggingManager.CONSOLE_APPENDER_NAME; import static org.selfbus.updater.Utils.shortenPath; import org.selfbus.updater.gui.GuiMain; diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 4f9f6c1d..51857c0f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -5,6 +5,7 @@ import com.intellij.uiDesigner.core.Spacer; import org.apache.commons.cli.ParseException; import org.selfbus.updater.*; +import org.selfbus.updater.logging.ListTextAppenders; import tuwien.auto.calimero.*; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; diff --git a/firmware_updater/updater/src/org/selfbus/updater/ListTextAppenders.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java similarity index 95% rename from firmware_updater/updater/src/org/selfbus/updater/ListTextAppenders.java rename to firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java index 6900a81c..2bdc85d1 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ListTextAppenders.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java @@ -1,66 +1,66 @@ -package org.selfbus.updater; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.Appender; -import org.selfbus.updater.gui.TextAppender; -import org.slf4j.LoggerFactory; - -import java.util.Iterator; - -public final class ListTextAppenders { - @SuppressWarnings("unused") - private ListTextAppenders() {} // avoids instance creation - - private static TextAppender jTextPaneAppender = null; - private static Logger logger = null; - - public static TextAppender searchAppender() { - // Get the LoggerContext from the LoggerFactory - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - - // Iterate over all loggers in the context - for (Logger iterLogger : loggerContext.getLoggerList()) { - // Get all appenders for the iterLogger - Iterator> appenderIterator = iterLogger.iteratorForAppenders(); - - while (appenderIterator.hasNext()) { - Appender appender = appenderIterator.next(); - if (appender instanceof TextAppender) { - logger = iterLogger; - jTextPaneAppender = (TextAppender) appender; - return jTextPaneAppender; - } - } - } - jTextPaneAppender = null; - return jTextPaneAppender; - } - - public static TextAppender getJTextPaneAppender() { - if (jTextPaneAppender != null) { - return jTextPaneAppender; - } - else { - return searchAppender(); - } - } - - public static void appendEvent(Level level, String logMessage) { - if (getJTextPaneAppender() == null) { - return; - } - - if (logger == null) { - return; - } - - ILoggingEvent loggingEvent = new LoggingEvent(logger.getName(), logger, - level, logMessage, null, null); - getJTextPaneAppender().doAppend(loggingEvent); - } -} - +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import org.selfbus.updater.gui.TextAppender; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; + +public final class ListTextAppenders { + @SuppressWarnings("unused") + private ListTextAppenders() {} // avoids instance creation + + private static TextAppender jTextPaneAppender = null; + private static Logger logger = null; + + public static TextAppender searchAppender() { + // Get the LoggerContext from the LoggerFactory + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + + // Iterate over all loggers in the context + for (Logger iterLogger : loggerContext.getLoggerList()) { + // Get all appenders for the iterLogger + Iterator> appenderIterator = iterLogger.iteratorForAppenders(); + + while (appenderIterator.hasNext()) { + Appender appender = appenderIterator.next(); + if (appender instanceof TextAppender) { + logger = iterLogger; + jTextPaneAppender = (TextAppender) appender; + return jTextPaneAppender; + } + } + } + jTextPaneAppender = null; + return jTextPaneAppender; + } + + public static TextAppender getJTextPaneAppender() { + if (jTextPaneAppender != null) { + return jTextPaneAppender; + } + else { + return searchAppender(); + } + } + + public static void appendEvent(Level level, String logMessage) { + if (getJTextPaneAppender() == null) { + return; + } + + if (logger == null) { + return; + } + + ILoggingEvent loggingEvent = new LoggingEvent(logger.getName(), logger, + level, logMessage, null, null); + getJTextPaneAppender().doAppend(loggingEvent); + } +} + diff --git a/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java similarity index 96% rename from firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java rename to firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java index d028f489..0d5c2caa 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java @@ -1,93 +1,93 @@ -package org.selfbus.updater; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.filter.ThresholdFilter; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.spi.FilterReply; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.management.ManagementFactory; - -public final class LoggingManager { - private final static Logger logger = LoggerFactory.getLogger(LoggingManager.class); - - - // In case CONSOLE_APPENDER_NAME is changed, - // change also CONSOLE appender name in src/resources/logback.xml - public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; - - public static void disableAppender(String appenderName) { - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); - - if (appender == null) { - logger.warn("disableAppender: appender {} not found.", appenderName); - return; - } - - logger.trace("Stopping and disabling {} appender.", appenderName); - appender.stop(); - loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(appenderName); - logger.trace("{} appender has been disabled.", appenderName); - } - - public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); - - if (appender == null) { - logger.warn("setAppenderLogLevel: appender {} not found.", appenderName); - return; - } - - appender.clearAllFilters(); - - ThresholdFilter thresholdFilter = new ThresholdFilter(); - thresholdFilter.setLevel(newLogLevel.toString()); - thresholdFilter.setContext(loggerContext); - thresholdFilter.start(); - - appender.addFilter(thresholdFilter); - logger.trace("{} appender log level set to: {}", appenderName, newLogLevel); - } - - public static Level getAppenderLogLevel(String appenderName) { - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName.toUpperCase()); - - if (appender == null) { - return null; - } - - final Level[] testLevels = {Level.INFO, Level.DEBUG, Level.WARN, Level.ERROR, Level.TRACE}; - - for (Filter filter : appender.getCopyOfAttachedFiltersList()) { - if (filter instanceof ThresholdFilter thresholdFilter) { - // Test with different levels to infer the threshold - for (Level testLevel : testLevels) { - LoggingEvent testEvent = new LoggingEvent(); - testEvent.setLevel(testLevel); - if (thresholdFilter.decide(testEvent) != FilterReply.DENY) { - return testLevel; - } - } - } - } - return null; - } - - public static boolean isConsoleActive() { - return (LoggingManager.getAppenderLogLevel(CONSOLE_APPENDER_NAME) == Level.INFO); - } - - public static boolean isRunningInIntelliJ() { - // Check for IntelliJ debugging JVM arguments - return ManagementFactory.getRuntimeMXBean().getInputArguments().stream() - .anyMatch(arg -> arg.contains("javaagent") || arg.contains("agentlib")); - } -} +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.filter.ThresholdFilter; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.management.ManagementFactory; + +public final class LoggingManager { + private final static Logger logger = LoggerFactory.getLogger(LoggingManager.class); + + + // In case CONSOLE_APPENDER_NAME is changed, + // change also CONSOLE appender name in src/resources/logback.xml + public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; + + public static void disableAppender(String appenderName) { + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); + + if (appender == null) { + logger.warn("disableAppender: appender {} not found.", appenderName); + return; + } + + logger.trace("Stopping and disabling {} appender.", appenderName); + appender.stop(); + loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(appenderName); + logger.trace("{} appender has been disabled.", appenderName); + } + + public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); + + if (appender == null) { + logger.warn("setAppenderLogLevel: appender {} not found.", appenderName); + return; + } + + appender.clearAllFilters(); + + ThresholdFilter thresholdFilter = new ThresholdFilter(); + thresholdFilter.setLevel(newLogLevel.toString()); + thresholdFilter.setContext(loggerContext); + thresholdFilter.start(); + + appender.addFilter(thresholdFilter); + logger.trace("{} appender log level set to: {}", appenderName, newLogLevel); + } + + public static Level getAppenderLogLevel(String appenderName) { + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName.toUpperCase()); + + if (appender == null) { + return null; + } + + final Level[] testLevels = {Level.INFO, Level.DEBUG, Level.WARN, Level.ERROR, Level.TRACE}; + + for (Filter filter : appender.getCopyOfAttachedFiltersList()) { + if (filter instanceof ThresholdFilter thresholdFilter) { + // Test with different levels to infer the threshold + for (Level testLevel : testLevels) { + LoggingEvent testEvent = new LoggingEvent(); + testEvent.setLevel(testLevel); + if (thresholdFilter.decide(testEvent) != FilterReply.DENY) { + return testLevel; + } + } + } + } + return null; + } + + public static boolean isConsoleActive() { + return (LoggingManager.getAppenderLogLevel(CONSOLE_APPENDER_NAME) == Level.INFO); + } + + public static boolean isRunningInIntelliJ() { + // Check for IntelliJ debugging JVM arguments + return ManagementFactory.getRuntimeMXBean().getInputArguments().stream() + .anyMatch(arg -> arg.contains("javaagent") || arg.contains("agentlib")); + } +} diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 6c95e93a..96e2d942 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -9,7 +9,7 @@ true From 01ddfef33ceb6f6211c0b2991e838850ca5c248c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 14:48:22 +0100 Subject: [PATCH 247/359] [Updater] Apply exception/message filter also to `list` appender --- .../updater/src/resources/logback.xml | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 96e2d942..df74de26 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -16,7 +16,7 @@ INFO - + WARN @@ -24,7 +24,7 @@ DENY NEUTRAL - + WARN @@ -41,6 +41,22 @@ INFO + + + + WARN + + DENY + NEUTRAL + + + + + WARN + + DENY + NEUTRAL + From bf685c80a6a58787d62dae3db418cf91ed49f542 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 14:59:37 +0100 Subject: [PATCH 248/359] [Updater] Docu log filters --- .../updater/logging/MessageEvaluator.java | 27 +++++++++++- .../updater/logging/ThrowableEvaluator.java | 41 ++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java index 52ae0d30..7e316572 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java @@ -3,15 +3,38 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import java.util.List; -; +/** + * The {@code MessageEvaluator} class is a custom implementation of {@code LevelEventEvaluator}. + * It is used to evaluate logging events and filter out specific log messages based on a + * predefined list of messages. + *

+ * This functionality can be useful for excluding log messages that are deemed + * irrelevant or repetitive during runtime. + */ public class MessageEvaluator extends LevelEventEvaluator { - // Customizable List of log messages to filter out + /** + * A list of log messages that should be filtered out. + * If an event's formatted message contains any of the strings in this list, + * the event will be excluded from logging. + */ private static final List FILTERED_MESSAGES = List.of( "received service acknowledgment status sequence number out of order" ); + /** + * Evaluates the provided logging event. + *

+ * This method first delegates evaluation to the superclass method. If the superclass + * evaluation passes, the event is logged. Otherwise, it checks whether the event's + * message contains any of the filtered messages specified in {@code FILTERED_MESSAGES}. + * If a match is found, the event will not be logged. + * + * @param event the {@code ILoggingEvent} to evaluate + * @return {@code true} if the event passes all filters and should be logged, {@code false} otherwise + * @throws NullPointerException if the event or its formatted message is null + */ @Override public boolean evaluate(ILoggingEvent event) throws NullPointerException { if (super.evaluate(event)) { diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java index 6fc94fda..e5b47369 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java @@ -9,9 +9,39 @@ import java.util.Map; import java.util.Objects; +/** + * The {@code ThrowableEvaluator} class extends {@link LevelEventEvaluator} + * to provide a custom evaluation of logging events that filters out specific exceptions + * with matching messages. + * + *

This evaluator is designed to exclude specific logging events from being processed, + * based on their associated throwable (exception) type and message content. The + * {@code exceptionFilterMap} maintains a mapping of throwable types and corresponding + * messages that should be filtered. + * + *

Example usage: + *

    + *
  • If the throwable is of type {@code KNXAckTimeoutException} and its message matches + * {@code "maximum send attempts, no service acknowledgment received"}, the event will + * be filtered out (ignored).
  • + *
  • If the throwable is of type {@code KNXTimeoutException} and its message matches + * {@code "no confirmation reply received for DM ..."}, the event will also be filtered out.
  • + *
+ * + *

If no throwable is associated with the event or the throwable does not meet the + * filtering criteria, the event is not filtered. + * + *

This can be useful in logging systems where specific repetitive or expected exceptions + * (e.g., timeout exceptions) need to be ignored to reduce log noise. + */ public class ThrowableEvaluator extends LevelEventEvaluator { - // Customizable Map of exception classes and corresponding messages to filter out + /** + * A customizable map that defines the filter configuration for throwable events. + *

Each entry in the map associates a specific exception class (key) with + * a list of specific error messages (value). If an exception of a matching type + * and message is encountered during evaluation, the event may be filtered out. + */ Map, List> exceptionFilterMap = Map.of( KNXAckTimeoutException.class, List.of( "maximum send attempts, no service acknowledgment received" @@ -21,6 +51,15 @@ public class ThrowableEvaluator extends LevelEventEvaluator { ) ); + /** + * Evaluates a logging event to determine whether it should be processed or filtered. + * + * @param event the {@link ILoggingEvent} instance to be evaluated. + * @return {@code true} if the event should be processed; {@code false} if the event + * should be filtered out. + * @throws NullPointerException if a null input is encountered during evaluation. + * This is unlikely since events usually undergo prior validation. + */ @Override public boolean evaluate(ILoggingEvent event) throws NullPointerException { if (super.evaluate(event)) { From a93a5a4b7593fafb36eb27688915ce5e0324c0d5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 15:00:34 +0100 Subject: [PATCH 249/359] =?UTF-8?q?[Updater]=20Info=20`getManifestInfo`=20?= =?UTF-8?q?doesn=C2=B4t=20work=20while=20debugging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 7d80fc8a..82a467c2 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -84,6 +84,8 @@ public static String getToolJarName() { } private static String getManifestInfo() { + // Only works correctly if built with gradle. + // This will fail during debugging, e.g. in IDEA, due to a different content of MANIFEST.MF try (final var metaInf = ToolInfo.class.getResourceAsStream("/META-INF/MANIFEST.MF")) { if (metaInf == null) { return ""; From 2bf71ba3e50470c7b08c67f3047872c2097c2a26 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 15:09:25 +0100 Subject: [PATCH 250/359] [Updater] Fix some found project inspection problems --- .../updater/src/org/selfbus/updater/DeviceManagement.java | 2 +- .../updater/src/org/selfbus/updater/logging/LoggingManager.java | 1 + .../src/org/selfbus/updater/logging/ThrowableEvaluator.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index be1aa18c..fedb49fe 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -303,7 +303,7 @@ public void dumpFlashRange(long startAddress, long endAddress) } public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo progressInfo) - throws UpdaterException, KNXLinkClosedException, InterruptedException, KNXTimeoutException { + throws UpdaterException, InterruptedException { int nIndex = 0; ResponseResult result = new ResponseResult(); while (nIndex < data.length) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java index 0d5c2caa..ba4e9701 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java @@ -21,6 +21,7 @@ public final class LoggingManager { // change also CONSOLE appender name in src/resources/logback.xml public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; + @SuppressWarnings("unused") public static void disableAppender(String appenderName) { LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java index e5b47369..db8d1dda 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java @@ -42,7 +42,7 @@ public class ThrowableEvaluator extends LevelEventEvaluator { * a list of specific error messages (value). If an exception of a matching type * and message is encountered during evaluation, the event may be filtered out. */ - Map, List> exceptionFilterMap = Map.of( + final Map, List> exceptionFilterMap = Map.of( KNXAckTimeoutException.class, List.of( "maximum send attempts, no service acknowledgment received" ), From 2d918cc1a44c9c4165a49ac9ff911da1ad8772a0 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 18:00:28 +0100 Subject: [PATCH 251/359] [Updater] Filter eibd hop count decrement info message --- .../src/org/selfbus/updater/logging/MessageEvaluator.java | 5 ++++- firmware_updater/updater/src/resources/logback.xml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java index 7e316572..2180499f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java @@ -20,7 +20,10 @@ public class MessageEvaluator extends LevelEventEvaluator { * the event will be excluded from logging. */ private static final List FILTERED_MESSAGES = List.of( - "received service acknowledgment status sequence number out of order" + "received service acknowledgment status sequence number out of order", + // from calimero-core KNXnetIPTunnel.java: + // we could get a .con with its hop count already decremented by 1 (eibd does that) + "received L_Data.con with hop count decremented by 1 (sent" ); /** diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index df74de26..29929d9c 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -52,7 +52,7 @@ - WARN + INFO DENY NEUTRAL From 1a86ed36a7c6c39d597240240a0eb6b7bff5c77c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 18:01:53 +0100 Subject: [PATCH 252/359] [Updater] Docu reasons for filtering message/exception --- .../src/org/selfbus/updater/logging/MessageEvaluator.java | 1 + .../src/org/selfbus/updater/logging/ThrowableEvaluator.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java index 2180499f..baefaadd 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java @@ -20,6 +20,7 @@ public class MessageEvaluator extends LevelEventEvaluator { * the event will be excluded from logging. */ private static final List FILTERED_MESSAGES = List.of( + // Loxone Miniserver Gen1 returns always status 0x04 on KNX tunnel requests with sequence number 255 "received service acknowledgment status sequence number out of order", // from calimero-core KNXnetIPTunnel.java: // we could get a .con with its hop count already decremented by 1 (eibd does that) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java index db8d1dda..99ea9452 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java @@ -44,10 +44,14 @@ public class ThrowableEvaluator extends LevelEventEvaluator { */ final Map, List> exceptionFilterMap = Map.of( KNXAckTimeoutException.class, List.of( + // Loxone Miniserver Gen1 returns always status 0x04 on KNX tunnel requests with sequence number 255 "maximum send attempts, no service acknowledgment received" ), KNXTimeoutException.class, List.of( "no confirmation reply received for DM prop-read.req objtype 8 instance 1 pid 51 start 1 elements 1" + // Loxone Miniserver Gen1 does not respond to + // tuwien.auto.calimero.link.KNXNetworkLinkIP.configureWithServerSettings(..) which sends + // M_PropRead.req OT=8 (cEMI Server), PID=51 (PID_MEDIUM_TYPE) ) ); From 2dc34d340e8a439861e7413f3a9b3af5f67df51f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 18:03:43 +0100 Subject: [PATCH 253/359] [Updater] Filter console log of failed M_PropRead.req PID_MAX_APDULENGTH --- .../src/org/selfbus/updater/logging/ThrowableEvaluator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java index 99ea9452..e103926e 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java @@ -48,10 +48,12 @@ public class ThrowableEvaluator extends LevelEventEvaluator { "maximum send attempts, no service acknowledgment received" ), KNXTimeoutException.class, List.of( - "no confirmation reply received for DM prop-read.req objtype 8 instance 1 pid 51 start 1 elements 1" // Loxone Miniserver Gen1 does not respond to // tuwien.auto.calimero.link.KNXNetworkLinkIP.configureWithServerSettings(..) which sends // M_PropRead.req OT=8 (cEMI Server), PID=51 (PID_MEDIUM_TYPE) + "no confirmation reply received for DM prop-read.req objtype 8 instance 1 pid 51 start 1 elements 1", + // and M_PropRead.req OT=0 (Device), PID=56 (PID_MAX_APDULENGTH) + "no confirmation reply received for DM prop-read.req objtype 0 instance 1 pid 56 start 1 elements 1" ) ); From ce3079538cec1c214c1b603079f568a49847e31f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 20:32:36 +0100 Subject: [PATCH 254/359] [Updater] New option `--ip-tunnel-reconnect` for some IP-Interfaces Allows to reconnect the KNX IP tunnel on specified sequence number. May help with some IP-Interfaces which are not fully Spec. compliant. E.g. for Loxone Miniserver Gen 1. set it to 245. Getting the current tunnel connection sequence number is quite hacky reflection stuff. This is why it is handled separately in the new subclass `DeviceManagementReflect` --- firmware_updater/updater/README.md | 15 +++- .../src/org/selfbus/updater/CliOptions.java | 61 +++++++++++++- .../org/selfbus/updater/DeviceManagement.java | 6 +- .../src/org/selfbus/updater/Updater.java | 3 +- .../devicemgnt/DeviceManagementFactory.java | 18 +++++ .../devicemgnt/DeviceManagementReflect.java | 29 +++++++ .../devicemgnt/ReflectKNXnetIPTunnel.java | 81 +++++++++++++++++++ 7 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementFactory.java create mode 100644 firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementReflect.java create mode 100644 firmware_updater/updater/src/org/selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index c299823c..b4108213 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -24,7 +24,8 @@ java -jar SB_updater-x.xx-all.jar [-f ] [-m ] ] [-bs <256|512|1024>] [--user ] [--user-pwd ] [--device-pwd ] [-u ] [-f1] [-H ] [-P ] [-p ] [-t2 | -t1 | -r] [-n] [-h | -v] [--delay ] [-l ] [--reconnect ] - [--ERASEFLASH] [--DUMPFLASH ] [-f0] [--statistic] [--discover] + [--ip-tunnel-reconnect <#sequence>] [--ERASEFLASH] [--DUMPFLASH ] [-f0] + [--statistic] [--discover] Selfbus KNX-Firmware update tool options: -f,--fileName Filename of hex file to program @@ -38,7 +39,10 @@ Selfbus KNX-Firmware update tool options: none) -D,--progDevice KNX device address in bootloader mode (default 15.15.192) - -o,--own own physical KNX address (default 0.0.0) + -o,--own own physical KNX tunnel address (default 0.0.0). + Required for some IP interfaces that also use their own + address as the tunnel address, e.g. Loxone Miniserver + Gen 1. --priority KNX telegram priority (default LOW) -bs,--blocksize <256|512|1024> Block size to program (default 1024 bytes) --user KNX IP Secure tunneling user identifier (1..127) @@ -65,8 +69,11 @@ Selfbus KNX-Firmware update tool options: --delay delay telegrams during data transmission to reduce bus load, valid 0-500ms, default 0 -l,--logLevel Logfile logging level [TRACE|DEBUG|INFO] (default TRACE) - --reconnect pause between a KNX connection reconnect, valid - 100 - 12500ms, default 100 + --reconnect pause between a KNX connection reconnect, valid 100 - + 12500ms, default 100 + --ip-tunnel-reconnect <#sequence> Reconnect KNX IP tunnel on sequence number, valid 100 - + 247, default -1. May help with some IP-Interfaces e.g. + for Loxone Miniserver Gen 1. set to 245 --ERASEFLASH USE WITH CAUTION! Erases the complete flash memory including the physical KNX address and all settings of the device. Only the bootloader is not deleted. diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 5664b710..7f65c8f6 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -80,6 +80,7 @@ public class CliOptions { public static final String OPT_LONG_DELAY = "delay"; public static final String OPT_LONG_RECONNECT = "reconnect"; + public static final String OPT_LONG_RECONNECT_SEQ_NUMBER = "ip-tunnel-reconnect"; private static final String OPT_SHORT_TUNNEL_V2 = "t2"; public static final String OPT_LONG_TUNNEL_V2 = "tunnelingv2"; @@ -158,6 +159,7 @@ public class CliOptions { private boolean flashingFullModeIsSet = false; private int delayMs = 0; private int reconnectMs = RECONNECT_MIN_MS; + private int reconnectSeqNumber = RECONNECT_INVALID_SEQ_NUMBER; private boolean noFlashIsSet = false; private boolean eraseFullFlashIsSet = false; @@ -176,6 +178,10 @@ public class CliOptions { private static final int RECONNECT_MIN_MS = 100; private static final int RECONNECT_MAX_MS = 12500; + private static final int RECONNECT_MIN_SEQ_NUMBER = 100; + private static final int RECONNECT_MAX_SEQ_NUMBER = 255 - (4*2); // reserve up to 4 repeats on transport layer with up 2 telegrams + private static final int RECONNECT_INVALID_SEQ_NUMBER = -1; + private CliOptions() { defaultLogLevel = getLogLevel(); @@ -261,7 +267,8 @@ private CliOptions() { .numberOfArgs(1) .required(false) .type(IndividualAddress.class) - .desc(String.format("own physical KNX address (default %s)", getOwnPhysicalAddress().toString())).build(); + .desc(String.format("own physical KNX tunnel address (default %s). Required for some IP interfaces that also use their own address as the tunnel address, e.g. Loxone Miniserver Gen 1.", + getOwnPhysicalAddress().toString())).build(); Option uid = Option.builder(OPT_SHORT_UID).longOpt(OPT_LONG_UID) .argName("uid") .numberOfArgs(1) @@ -280,6 +287,13 @@ private CliOptions() { .type(Number.class) .desc(String.format("pause between a KNX connection reconnect, valid %d - %dms, default %d", RECONNECT_MIN_MS, RECONNECT_MAX_MS, RECONNECT_MIN_MS)).build(); + Option reconnectSeqNumber = Option.builder(null).longOpt(OPT_LONG_RECONNECT_SEQ_NUMBER) + .argName("#sequence") + .numberOfArgs(1) + .required(false) + .type(Number.class) + .desc(String.format("Reconnect KNX IP tunnel on sequence number, valid %d - %d, default %d. May help with some IP-Interfaces e.g. for Loxone Miniserver Gen 1. set to 245", + RECONNECT_MIN_SEQ_NUMBER, RECONNECT_MAX_SEQ_NUMBER, getReconnectSeqNumber())).build(); Option logLevel = Option.builder(OPT_SHORT_LOGLEVEL).longOpt(OPT_LONG_LOGLEVEL) .argName("TRACE|DEBUG|INFO") .numberOfArgs(1) @@ -369,6 +383,7 @@ private CliOptions() { cliOptions.addOption(delay); cliOptions.addOption(logLevel); cliOptions.addOption(reconnect); + cliOptions.addOption(reconnectSeqNumber); cliOptions.addOption(eraseFlash); cliOptions.addOption(dumpFlash); cliOptions.addOption(NO_FLASH); @@ -471,6 +486,11 @@ private void parse(final String[] args) throws ParseException, KNXFormatExceptio else setReconnectMs(RECONNECT_MIN_MS); + if (cmdLine.hasOption(OPT_LONG_RECONNECT_SEQ_NUMBER)) + setReconnectSeqNumber(cmdLine.getOptionValue(OPT_LONG_RECONNECT_SEQ_NUMBER)); + else + setReconnectSeqNumber(RECONNECT_INVALID_SEQ_NUMBER); + if (cmdLine.hasOption(OPT_LONG_BLOCKSIZE)) setBlockSize(cmdLine.getOptionValue(OPT_LONG_BLOCKSIZE)); else @@ -1202,4 +1222,43 @@ private void setReconnectMs(String reconnectMs) { ansi().fg(RED), OPT_LONG_RECONNECT, reconnectMs, getReconnectMs(), ansi().reset()); } } + + public int getReconnectSeqNumber() { + return reconnectSeqNumber; + } + + private void setReconnectSeqNumber(int reconnectSeqNumber) { + if (((reconnectSeqNumber < RECONNECT_MIN_SEQ_NUMBER) || (reconnectSeqNumber > RECONNECT_MAX_SEQ_NUMBER)) && + (reconnectSeqNumber != RECONNECT_INVALID_SEQ_NUMBER)) { + logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", + ansi().fg(RED), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, RECONNECT_MIN_SEQ_NUMBER, + RECONNECT_MAX_SEQ_NUMBER, RECONNECT_INVALID_SEQ_NUMBER, ansi().reset()); + reconnectSeqNumber = RECONNECT_INVALID_SEQ_NUMBER; // set to invalid + } + this.reconnectSeqNumber = reconnectSeqNumber; + logger.debug("{}={}", OPT_LONG_RECONNECT_SEQ_NUMBER, getReconnectSeqNumber()); + } + + private void setReconnectSeqNumber(String reconnectSeqNumber) { + try { + if (reconnectSeqNumber == null || reconnectSeqNumber.isBlank()) + setReconnectSeqNumber(RECONNECT_INVALID_SEQ_NUMBER); + else + setReconnectSeqNumber(Integer.parseInt(reconnectSeqNumber)); + } + catch (NumberFormatException e) { + setReconnectSeqNumber(RECONNECT_INVALID_SEQ_NUMBER); + logger.warn("{}option --{} {} is invalid => set to default {}{}", + ansi().fg(RED), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, getReconnectMs(), ansi().reset()); + } + } + + public boolean isValidReconnectSeqNumber() { + if (getReconnectSeqNumber() == RECONNECT_INVALID_SEQ_NUMBER) { + return false; + } + + return ((getReconnectSeqNumber() >= RECONNECT_MIN_SEQ_NUMBER) && + (getReconnectSeqNumber() <= RECONNECT_MAX_SEQ_NUMBER)); + } } \ No newline at end of file diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java index fedb49fe..82e285b9 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java @@ -32,7 +32,7 @@ /** * Provides methods to send firmware update telegrams to the bootloader (MCU) */ -public final class DeviceManagement implements AutoCloseable { +public class DeviceManagement implements AutoCloseable { /** * EraseCode for the APCI_MASTER_RESET_PDU (valid from 1..7) */ @@ -59,11 +59,11 @@ public final class DeviceManagement implements AutoCloseable { */ private SBManagementClientImpl mc; private Destination progDestination; - private KNXNetworkLink link; + protected KNXNetworkLink link; private IndividualAddress progDevice; - private CliOptions cliOptions; + protected CliOptions cliOptions; private DeviceManagement () { setProtocolVersion(UDPProtocolVersion.UDP_V1); diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 687bc164..841aca80 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -4,6 +4,7 @@ import org.apache.commons.cli.ParseException; import org.fusesource.jansi.AnsiConsole; import org.selfbus.updater.bootloader.BootloaderStatistic; +import org.selfbus.updater.devicemgnt.DeviceManagementFactory; import org.selfbus.updater.logging.LoggingManager; import tuwien.auto.calimero.*; import org.selfbus.updater.bootloader.BootDescriptor; @@ -209,7 +210,7 @@ public void run() { System.out.println(); } - dm = new DeviceManagement(cliOptions); + dm = DeviceManagementFactory.getDeviceManagement(cliOptions); logger.debug("Telegram priority: {}", cliOptions.getPriority()); dm.open(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementFactory.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementFactory.java new file mode 100644 index 00000000..a919feda --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementFactory.java @@ -0,0 +1,18 @@ +package org.selfbus.updater.devicemgnt; + +import org.selfbus.updater.CliOptions; +import org.selfbus.updater.DeviceManagement; + +final public class DeviceManagementFactory { + @SuppressWarnings("unused") + private DeviceManagementFactory() {} + + public static DeviceManagement getDeviceManagement(CliOptions cliOptions) { + if (cliOptions.isValidReconnectSeqNumber()) { + return new DeviceManagementReflect(cliOptions); + } + else { + return new DeviceManagement(cliOptions); + } + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementReflect.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementReflect.java new file mode 100644 index 00000000..7d25a663 --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementReflect.java @@ -0,0 +1,29 @@ +package org.selfbus.updater.devicemgnt; + +import org.selfbus.updater.*; +import org.selfbus.updater.upd.UPDCommand; +import tuwien.auto.calimero.KNXException; + +import java.net.UnknownHostException; + +public class DeviceManagementReflect extends DeviceManagement { + public DeviceManagementReflect(CliOptions cliOptions) { + super(cliOptions); + } + + @Override + public ResponseResult sendWithRetry(final UPDCommand command, final byte[] data, int maxRetry) + throws UpdaterException, InterruptedException { + ReflectKNXnetIPTunnel reflect = new ReflectKNXnetIPTunnel(link); + final int seqNmb = reflect.getSequenceNumberSent(); + + if ((cliOptions.isValidReconnectSeqNumber()) && (seqNmb >= cliOptions.getReconnectSeqNumber())) { + try { + this.reconnect(cliOptions.getReconnectMs()); + } + catch (KNXException | UnknownHostException ignore) { + } + } + return super.sendWithRetry(command, data, maxRetry); + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java new file mode 100644 index 00000000..6a34c610 --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java @@ -0,0 +1,81 @@ +package org.selfbus.updater.devicemgnt; + +import com.google.common.collect.Lists; +import tuwien.auto.calimero.knxnetip.KNXnetIPTunnel; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.util.List; + +public class ReflectKNXnetIPTunnel { + + private KNXnetIPTunnel tunnel = null; + Field fieldSeqNumb = null; + private final static int SEQUENCE_NUMBER_INVALID = -1; + int sequenceNumberSent = SEQUENCE_NUMBER_INVALID; + + @SuppressWarnings("unused") + private ReflectKNXnetIPTunnel() { + } + + public ReflectKNXnetIPTunnel(Object obj) { + Field fieldConn = getField(obj, "conn"); + if (fieldConn == null) { + return; + } + + fieldConn.setAccessible(true); + try { + Object connValue = fieldConn.get(obj); + if (!(connValue instanceof KNXnetIPTunnel)) { + return; + } + tunnel = (KNXnetIPTunnel) connValue; + fieldSeqNumb = getField(tunnel, "seqSend"); + if (fieldSeqNumb == null) { + return; + } + fieldSeqNumb.setAccessible(true); + } + catch (IllegalAccessException ignored) { + } + } + + public int getSequenceNumberSent() { + if (fieldSeqNumb == null) { + return SEQUENCE_NUMBER_INVALID; + } + + try { + sequenceNumberSent = fieldSeqNumb.getInt(tunnel); + } + catch (IllegalAccessException ignored) { + return SEQUENCE_NUMBER_INVALID; + } + return sequenceNumberSent; + } + + private Field getField(Object obj, String fieldName) { + // Iterate over all declared fields + for (Field field : getFieldsUpTo(obj.getClass(), Object.class)) { + if (field.getName().equals(fieldName)) { + return field; + } + } + return null; + } + + private static Iterable getFieldsUpTo(@Nonnull Class startClass, + @Nullable Class exclusiveParent) { + List currentClassFields = Lists.newArrayList(startClass.getDeclaredFields()); + Class parentClass = startClass.getSuperclass(); + + if (parentClass != null && (!(parentClass.equals(exclusiveParent)))) { + List parentClassFields = + (List) getFieldsUpTo(parentClass, exclusiveParent); + currentClassFields.addAll(parentClassFields); + } + return currentClassFields; + } +} From 0d9e49e5cf7ee47abe9ad80b9d732dc10f37079e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 20:43:38 +0100 Subject: [PATCH 255/359] [Updater] Move `DeviceManagement` to `devicemgnt` --- .../org/selfbus/updater/FlashDiffMode.java | 1 + .../org/selfbus/updater/FlashFullMode.java | 1 + .../org/selfbus/updater/ResponseResult.java | 4 +- .../src/org/selfbus/updater/Updater.java | 1 + .../{ => devicemgnt}/DeviceManagement.java | 1145 +++++++++-------- .../devicemgnt/DeviceManagementFactory.java | 1 - 6 files changed, 578 insertions(+), 575 deletions(-) rename firmware_updater/updater/src/org/selfbus/updater/{ => devicemgnt}/DeviceManagement.java (97%) diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index 84e05564..556114b7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -2,6 +2,7 @@ import net.harawata.appdirs.AppDirsFactory; import org.selfbus.updater.bootloader.BootDescriptor; +import org.selfbus.updater.devicemgnt.DeviceManagement; import org.selfbus.updater.mode.differential.FlashDiff; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index 7a52d8a2..76126919 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -1,6 +1,7 @@ package org.selfbus.updater; import org.selfbus.updater.bootloader.BootloaderStatistic; +import org.selfbus.updater.devicemgnt.DeviceManagement; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; import org.selfbus.updater.upd.UPDProtocol; diff --git a/firmware_updater/updater/src/org/selfbus/updater/ResponseResult.java b/firmware_updater/updater/src/org/selfbus/updater/ResponseResult.java index 7f313789..0603f233 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ResponseResult.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ResponseResult.java @@ -8,14 +8,14 @@ public class ResponseResult { private long dropCount; private long written; - ResponseResult() { + public ResponseResult() { this.timeoutCount = 0; this.dropCount = 0; this.data = null; this.written = 0; } - byte[] data() { + public byte[] data() { return data; } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 841aca80..feb6e692 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -4,6 +4,7 @@ import org.apache.commons.cli.ParseException; import org.fusesource.jansi.AnsiConsole; import org.selfbus.updater.bootloader.BootloaderStatistic; +import org.selfbus.updater.devicemgnt.DeviceManagement; import org.selfbus.updater.devicemgnt.DeviceManagementFactory; import org.selfbus.updater.logging.LoggingManager; import tuwien.auto.calimero.*; diff --git a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java similarity index 97% rename from firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java rename to firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 82e285b9..566e9160 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -1,572 +1,573 @@ -package org.selfbus.updater; - -import ch.qos.logback.classic.Level; -import org.selfbus.updater.bootloader.BootDescriptor; -import org.selfbus.updater.bootloader.BootloaderIdentity; -import org.selfbus.updater.bootloader.BootloaderStatistic; -import org.selfbus.updater.logging.ListTextAppenders; -import org.selfbus.updater.logging.LoggingManager; -import org.selfbus.updater.upd.UDPProtocolVersion; -import org.selfbus.updater.upd.UDPResult; -import org.selfbus.updater.upd.UPDCommand; -import org.selfbus.updater.upd.UPDProtocol; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import tuwien.auto.calimero.*; -import tuwien.auto.calimero.link.KNXLinkClosedException; -import tuwien.auto.calimero.link.KNXNetworkLink; -import tuwien.auto.calimero.mgmt.Destination; -import tuwien.auto.calimero.mgmt.KNXDisconnectException; -import tuwien.auto.calimero.mgmt.ManagementProcedures; -import tuwien.auto.calimero.mgmt.ManagementProceduresImpl; - -import java.net.UnknownHostException; -import java.time.Duration; -import java.util.Arrays; - -import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; -import static org.selfbus.updater.Mcu.MAX_FLASH_ERASE_TIMEOUT; -import static org.selfbus.updater.upd.UPDProtocol.DATA_POSITION; - -/** - * Provides methods to send firmware update telegrams to the bootloader (MCU) - */ -public class DeviceManagement implements AutoCloseable { - /** - * EraseCode for the APCI_MASTER_RESET_PDU (valid from 1..7) - */ - private static final int RESTART_ERASE_CODE = 7; - /** - * Channelnumber for the APCI_MASTER_RESET_PDU - */ - private static final int RESTART_CHANNEL = 255; - - /** - * Default maximum retries a UPD command is sent to the client - */ - private static final int MAX_UPD_COMMAND_RETRY = 3; - - private UDPProtocolVersion protocolVersion; - - private int blockSize; - private int maxPayload; - private int updSendDataOffset; - - private final static Logger logger = LoggerFactory.getLogger(DeviceManagement.class); - /** - * Calimero device management client - */ - private SBManagementClientImpl mc; - private Destination progDestination; - protected KNXNetworkLink link; - - private IndividualAddress progDevice; - - protected CliOptions cliOptions; - - private DeviceManagement () { - setProtocolVersion(UDPProtocolVersion.UDP_V1); - } - - public DeviceManagement(CliOptions cliOptions) { - this(); - this.cliOptions = cliOptions; - } - - public void reconnect(int waitMs) throws KNXException, UpdaterException, UnknownHostException, InterruptedException { - close(); - if (waitMs <= 0) { - waitMs = 100; - } - logger.info("Reconnecting in {} ms", waitMs); - Thread.sleep(waitMs); - open(); - } - - public void open() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { - close(); - this.link = new SBKNXLink(this.cliOptions).openLink(); - this.progDevice = cliOptions.getProgDevicePhysicalAddress(); - this.mc = new SBManagementClientImpl(this.link); - this.mc.setPriority(cliOptions.getPriority()); - this.progDestination = this.mc.createDestination(progDevice, true, false, false); - } - - @Override - public void close() { - logger.debug("Closing {}", this.getClass().getSimpleName()); - if (progDestination != null) { - logger.debug("Releasing progDestination {}", progDestination); - progDestination.close(); - } - - if (mc != null) { - logger.debug("Releasing mc {}", mc); - mc.close(); // mc.close calls already mc.detach() - } - if (link != null) { - logger.debug("Releasing link {}", link); - link.close(); - } - progDestination = null; - progDevice = null; - mc = null; - link = null; - } - - public void restartProgrammingDevice() - throws KNXTimeoutException, KNXLinkClosedException, InterruptedException { - logger.info("Restarting device {}", progDestination); - mc.restart(progDestination); - } - - private void waitRestartTime(int restartTimeSeconds) throws InterruptedException { - while (restartTimeSeconds > 0) { - Thread.sleep(1000); - if (LoggingManager.isConsoleActive()) { - System.out.printf("%s.%s", ansi().fgBright(GREEN), ansi().reset()); - } - restartTimeSeconds--; - } - if (LoggingManager.isConsoleActive()) { - System.out.println(); - } - } - - /** - * Restarts the device running in normal into the Bootloader mode - * - * @param device the IndividualAddress of the device to restart - */ - public void restartDeviceToBootloader(IndividualAddress device) throws InterruptedException { - int restartProcessTime = Mcu.DEFAULT_RESTART_TIME_SECONDS; - try (Destination dest = this.mc.createDestination(device, true, false, false)) { - logger.info("Restarting device {} into bootloader", device); - restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); - logger.info("Device {} reported {}{}{} second(s) for restarting", - device, ansi().fgBright(GREEN), restartProcessTime, ansi().reset()); - waitRestartTime(restartProcessTime); - } catch (final KNXException e) { - logger.info("{}Restart state of device {} unknown. {}{}", - ansi().fgBright(RED), device, e.getMessage(), ansi().reset()); - logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 - logger.info("Waiting {}{}{} seconds for device {} to restart", - ansi().fgBright(GREEN), restartProcessTime, ansi().reset(), device); - waitRestartTime(restartProcessTime); - } - } - - public String requestUIDFromDevice() - throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { - logger.info("Requesting UID from {}", progDestination.getAddress()); - byte[] result = sendWithRetry(UPDCommand.REQUEST_UID, new byte[0], getMaxUpdCommandRetry()).data(); - UPDCommand command = UPDCommand.tryFromByteArray(result); - if (command != UPDCommand.RESPONSE_UID) { - restartProgrammingDevice(); - throw new UpdaterException("Requesting UID failed!"); - } - - byte[] uid; - if ((result.length >= UPDProtocol.UID_LENGTH_USED) && (result.length <= UPDProtocol.UID_LENGTH_MAX)){ - uid = Arrays.copyOfRange(result, DATA_POSITION, UPDProtocol.UID_LENGTH_USED + DATA_POSITION); - logger.info(" got: {} length {}", UPDProtocol.byteArrayToHex(uid), uid.length); - return UPDProtocol.byteArrayToHex(uid); - } else { - uid = Arrays.copyOfRange(result, DATA_POSITION, result.length - DATA_POSITION); - String errorMsg = String.format("Request UID failed %s result.length=%d, UID_LENGTH_USED=%d, UID_LENGTH_MAX=%d", - UPDProtocol.byteArrayToHex(uid), uid.length, UPDProtocol.UID_LENGTH_USED, UPDProtocol.UID_LENGTH_MAX); - logger.error(errorMsg); - restartProgrammingDevice(); - throw new UpdaterException(errorMsg); - } - } - - public BootloaderIdentity requestBootloaderIdentity() - throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { - logger.debug("Requesting Bootloader Identity"); - - byte[] telegram = new byte[2]; - telegram[0] = (byte) ToolInfo.versionMajor(); - telegram[1] = (byte) ToolInfo.versionMinor(); - - byte[] result = sendWithRetry(UPDCommand.REQUEST_BL_IDENTITY, telegram, getMaxUpdCommandRetry()).data(); - UPDCommand command = UPDCommand.tryFromByteArray(result); - if (command != UPDCommand.RESPONSE_BL_IDENTITY) - { - if (command == UPDCommand.RESPONSE_BL_VERSION_MISMATCH) { - long minMajorVersion = result[DATA_POSITION] & 0xff; - long minMinorVersion = result[DATA_POSITION + 1] & 0xff; - logger.error("{}Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.{}", - ansi().fg(RED), ToolInfo.getVersion(), minMajorVersion, minMinorVersion, ansi().reset()); - } - restartProgrammingDevice(); - throw new UpdaterException("Requesting Bootloader Identity failed!"); - } - - BootloaderIdentity bl = BootloaderIdentity.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info("Device Bootloader: {}{}{}", ansi().fgBright(YELLOW), bl, ansi().reset()); - - boolean versionsMatch = (bl.versionMajor() > ToolInfo.minMajorVersionBootloader()) || - ((bl.versionMajor() == ToolInfo.minMajorVersionBootloader()) && (bl.versionMinor() >= ToolInfo.minMinorVersionBootloader())); - - if (!versionsMatch) - { - logger.error("{}Bootloader version {} is not compatible, please update Bootloader to version {} or higher{}", - ansi().fg(RED), bl.getVersion(), ToolInfo.minVersionBootloader(), ansi().reset()); - throw new UpdaterException("Bootloader version not compatible!"); - } - return bl; - } - - public BootDescriptor requestBootDescriptor() - throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { - logger.debug("Requesting Boot Descriptor"); - byte[] result = sendWithRetry(UPDCommand.REQUEST_BOOT_DESC, new byte[0], getMaxUpdCommandRetry()).data(); - UPDCommand command = UPDCommand.tryFromByteArray(result); - if (command != UPDCommand.RESPONSE_BOOT_DESC) { - restartProgrammingDevice(); - throw new UpdaterException("Boot descriptor request failed!"); - } - - BootDescriptor bootDescriptor = BootDescriptor.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info("Current firmware: {}", bootDescriptor); - return bootDescriptor; - } - - public String requestAppVersionString() throws UpdaterException, - InterruptedException { - byte[] result = sendWithRetry(UPDCommand.APP_VERSION_REQUEST, new byte[0], getMaxUpdCommandRetry()).data(); - UPDCommand command = UPDCommand.tryFromByteArray(result); - if (command != UPDCommand.APP_VERSION_RESPONSE) { - return ""; - } - - return new String(result,DATA_POSITION,result.length - DATA_POSITION); // Convert 12 bytes to string starting from result[DATA_POSITION]; - } - - public void unlockDeviceWithUID(String uid) - throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { - logger.info("Unlocking device {} with UID {}", progDestination.getAddress(), uid); - byte[] result = sendWithRetry(UPDCommand.UNLOCK_DEVICE, UPDProtocol.uidToByteArray(uid), getMaxUpdCommandRetry()).data(); - if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { - restartProgrammingDevice(); - throw new UpdaterException(String.format("Unlocking device %s failed.", progDestination.getAddress())); - } - } - - public void eraseAddressRange(long startAddress, long totalLength) - throws KNXLinkClosedException, InterruptedException, UpdaterException, KNXTimeoutException { - long endAddress = startAddress + totalLength - 1; - byte[] telegram = new byte[8]; - Utils.longToStream(telegram, 0 , startAddress); - Utils.longToStream(telegram, 4 , endAddress); - logger.info("Erasing firmware address range: {}", String.format("0x%04X-0x%04X", startAddress, endAddress)); - Duration oldResponseTimeout = mc.responseTimeout(); - Duration newResponseTimeout = MAX_FLASH_ERASE_TIMEOUT.multipliedBy(2); - if (oldResponseTimeout.compareTo(newResponseTimeout) < 0) { - mc.responseTimeout(newResponseTimeout); // temporarily increase responseTimeout - logger.trace("mc.ResponseTimeout temporarily increased to {}", mc.responseTimeout()); - } - - byte[] result = sendWithRetry(UPDCommand.ERASE_ADDRESS_RANGE, telegram, getMaxUpdCommandRetry()).data(); - - mc.responseTimeout(oldResponseTimeout); // restore responseTimeout - logger.trace("mc.ResponseTimeout restored to {}", mc.responseTimeout()); - - if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { - restartProgrammingDevice(); - throw new UpdaterException("Erasing firmware address range failed."); - } - } - - public void eraseFlash() - throws KNXLinkClosedException, InterruptedException, UpdaterException, KNXTimeoutException { - byte[] result = sendWithRetry(UPDCommand.ERASE_COMPLETE_FLASH, new byte[0], getMaxUpdCommandRetry()).data(); - if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { - restartProgrammingDevice(); - throw new UpdaterException("Deleting the entire flash failed."); - } - } - - public void dumpFlashRange(long startAddress, long endAddress) - throws KNXLinkClosedException, InterruptedException, UpdaterException, KNXTimeoutException { - byte[] telegram = new byte[8]; - Utils.longToStream(telegram, 0 , startAddress); - Utils.longToStream(telegram, 4 , endAddress); - // sendWithRetry will always time out, because the mcu is busy dumping the flash - byte[] result = sendWithRetry(UPDCommand.DUMP_FLASH, telegram, 0).data(); - if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { - restartProgrammingDevice(); - throw new UpdaterException("Flash dumping failed."); - } - } - - public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo progressInfo) - throws UpdaterException, InterruptedException { - int nIndex = 0; - ResponseResult result = new ResponseResult(); - while (nIndex < data.length) - { - byte[] txBuffer; - if ((data.length + updSendDataOffset - nIndex) >= getMaxPayload()) { - txBuffer = new byte[getMaxPayload()]; - } - else { - txBuffer = new byte[data.length + updSendDataOffset - nIndex]; - } - - if (getProtocolVersion() == UDPProtocolVersion.UDP_V0) { - txBuffer[0] = (byte) nIndex; // First byte contains mcu's ramBuffer start position to copy to - } - System.arraycopy(data, nIndex, txBuffer, updSendDataOffset, txBuffer.length - updSendDataOffset); - - ResponseResult tmp = sendWithRetry(UPDCommand.SEND_DATA, txBuffer, maxRetry); - result.addCounters(tmp); - - if (UPDProtocol.checkResult(tmp.data(), false) != UDPResult.IAP_SUCCESS) { - throw new UpdaterException("doFlash failed."); - } - updateProgressInfo(progressInfo, txBuffer.length - updSendDataOffset); - nIndex += txBuffer.length - updSendDataOffset; - - if (delay > 0) { - Thread.sleep(delay); //Reduce bus load during data upload, without 2:04, 50ms 2:33, 60ms 2:41, 70ms 2:54, 80ms 3:04 - } - } - result.addWritten(nIndex); - return result; - } - - public void startProgressInfo(ProgressInfo progressInfo) { - logger.info(progressInfo.getHeader()); - // We need one newLine for the gui - ListTextAppenders.appendEvent(Level.INFO, System.lineSeparator()); - } - - public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { - if (progressInfo == null) { - return; - } - - progressInfo.update(bytesDone); - - String logText = String.format("%s%s%s%s", - AnsiCursor.off(), - ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset(), - progressInfo, - AnsiCursor.on()); - logText += " "; // append one space, just in case an exception message may come up - - // console output - if (LoggingManager.isConsoleActive()) { - System.out.print(logText); - if (LoggingManager.isRunningInIntelliJ()) { - // if running in idea debug-console we need to add a newline, - // because the idea debug-console doesn´t support ansi cursor movements - System.out.println(); - } - } - // gui JTextPane output - logText = ansi().cursorUpLine().toString() + logText; // need this CursorUp in gui - ListTextAppenders.appendEvent(Level.INFO, logText); - } - - public void finalProgressInfo(ProgressInfo progressInfo) { - if (progressInfo == null) { - return; - } - - //todo this is stupid - // We need two CursorUp in the gui, because it appends to every logMessage System.lineSeparator() - ListTextAppenders.appendEvent(Level.INFO, ansi().cursorUpLine(1).toString()); - - // Now normal logging in console and gui - logger.info("{} {}", ansi().cursorToColumn(1).toString(), progressInfo); - } - - public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) - throws UpdaterException, KNXLinkClosedException, InterruptedException, KNXTimeoutException { - - byte[] streamBootDescriptor = bootDescriptor.toStream(); - // send new boot descriptor - ResponseResult flashResult = doFlash(streamBootDescriptor, getMaxUpdCommandRetry(), delay, null); - if (flashResult.written() != streamBootDescriptor.length) { - throw new UpdaterException(String.format("Sending Boot descriptor (length %d) failed. Wrote %d", streamBootDescriptor.length, flashResult.written())); - } - if (delay > 0) { - Thread.sleep(delay); - } - int crc32Value = Utils.crc32Value(streamBootDescriptor); - byte[] programBootDescriptor = new byte[8]; - Utils.longToStream(programBootDescriptor, 0, streamBootDescriptor.length); - Utils.longToStream(programBootDescriptor, 4, crc32Value); - logger.debug("Updating boot descriptor with crc32 {}", - String.format("0x%08X, length %d", crc32Value, streamBootDescriptor.length)); - ResponseResult programResult = sendWithRetry(UPDCommand.UPDATE_BOOT_DESC, programBootDescriptor, getMaxUpdCommandRetry()); - if (UPDProtocol.checkResult(programResult.data()) != UDPResult.IAP_SUCCESS) { - restartProgrammingDevice(); - throw new UpdaterException("Updating boot descriptor failed."); - } - programResult.addCounters(flashResult); - if (delay > 0) { - Thread.sleep(delay); - } - } - - public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, - InterruptedException { - logger.debug("Requesting Bootloader statistic"); - byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); - UPDCommand command = UPDCommand.tryFromByteArray(result); - if (command != UPDCommand.RESPONSE_STATISTIC) { - logger.warn("Requesting Bootloader statistic {}failed!{}", ansi().fg(RED), ansi().reset()); - return null; - } - BootloaderStatistic blStatistic = BootloaderStatistic.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.debug("#Disconnect: {} #repeated T_ACK: {}", blStatistic.getDisconnectCountColored(), - blStatistic.getRepeatedT_ACKcount()); - return blStatistic; - } - - private boolean isLinkAlive() { - if (link == null) { - return false; - } - return link.isOpen(); - } - - private void handleKNXException(final UPDCommand command, final KNXException e, final boolean reconnect) throws - UpdaterException, InterruptedException { - if (e instanceof KNXAckTimeoutException) { - // This exception is on missing/faulty ack at linklayer level thrown, but we never see it here - // todo check public void send(final CEMI frame, final BlockingMode mode) in ConnectionBase - logger.warn("{}Unexpected: never seen before {}{}", ansi().fg(RED), e.getClass().getSimpleName(),ansi().reset()); - } - - logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), - e.getClass().getSimpleName(), ansi().reset()); - - if (!reconnect) { - return; - } - - try { - reconnect(cliOptions.getReconnectMs()); - } - catch (KNXException | UnknownHostException e2) { - throw new UpdaterException(String.format("%s failed.", command), e); - } - } - - public ResponseResult sendWithRetry(final UPDCommand command, final byte[] data, int maxRetry) - throws UpdaterException, InterruptedException { - ResponseResult result = new ResponseResult(); - while (true) { - KNXException lastCaughtException; - try { - byte[] data2 = mc.sendUpdateData(progDestination, command.toByte(), data); - result.copyFromArray(data2); - return result; - } - ///\todo check causes of KNXRemoteException, i think they are unrecoverable - catch (KNXDisconnectException | KNXLinkClosedException e) { - lastCaughtException = e; - result.incDropCount(); - handleKNXException(command, e, true); - } - catch (KNXTimeoutException e) { - // Can happen on a L2 tunnel request ACK timeout or a TL4 ACK timeout - lastCaughtException = e; - result.incTimeoutCount(); - handleKNXException(command, e, true); - } - catch (KNXInvalidResponseException e) { - lastCaughtException = e; - handleKNXException(command, e, true); - } - finally { - if (!isLinkAlive()) { - maxRetry = 0; // exit while - } - } - - if (maxRetry > 0) { - maxRetry--; - } - else { - throw new UpdaterException(String.format("%s failed.", command), lastCaughtException); - } - } - } - - public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throws UpdaterException, - InterruptedException { - try { - ManagementProcedures mgmt = new ManagementProceduresImpl(link); - IndividualAddress[] devices = mgmt.readAddress(); - mgmt.close(); - if ((devices.length == 0) && (progDeviceAddr == null)) { // no device in prog mode - return; - } - else if ((devices.length == 1) && (progDeviceAddr != null) && (progDeviceAddr.equals(devices[0]))) { // correct device in prog mode - return; - } - logger.warn("{}{} Device(s) in bootloader/programming mode: {}{}", - ansi().fgBright(RED), devices.length, Arrays.stream(devices).toArray(), ansi().reset()); - if (devices.length == 0) { - throw new UpdaterException("No device in programming mode."); - } - else { - throw new UpdaterException(String.format("%d wrong device(s) %s are already in programming mode.", devices.length, Arrays.toString(devices))); - } - } catch (KNXException e ) { - throw new UpdaterException(String.format("checkDevicesInProgrammingMode failed. %s", e.getMessage()), e); - } - } - - public UDPProtocolVersion getProtocolVersion() { - return protocolVersion; - } - - public void setProtocolVersion(UDPProtocolVersion protocolVersion) { - this.protocolVersion = protocolVersion; - this.maxPayload = Mcu.MAX_PAYLOAD; - if (this.protocolVersion == UDPProtocolVersion.UDP_V1) { - this.blockSize = Mcu.UPD_PROGRAM_SIZE; - this.updSendDataOffset = 0; - } - else { - this.blockSize = Mcu.FLASH_PAGE_SIZE; - this.updSendDataOffset = 1; - } - } - - public int getBlockSize() { - return blockSize; - } - - public boolean setBlockSize(int blockSize) { - if (getProtocolVersion() == UDPProtocolVersion.UDP_V0) { - return false; - } - this.blockSize = blockSize; - return true; - } - - public int getMaxPayload() { - return maxPayload; - } - - public int getMaxUpdCommandRetry() { - return MAX_UPD_COMMAND_RETRY; - } - - public String getLinkInfo() { - if (this.link == null) { - return "No link available."; - } - - return link.toString(); - } -} +package org.selfbus.updater.devicemgnt; + +import ch.qos.logback.classic.Level; +import org.selfbus.updater.*; +import org.selfbus.updater.bootloader.BootDescriptor; +import org.selfbus.updater.bootloader.BootloaderIdentity; +import org.selfbus.updater.bootloader.BootloaderStatistic; +import org.selfbus.updater.logging.ListTextAppenders; +import org.selfbus.updater.logging.LoggingManager; +import org.selfbus.updater.upd.UDPProtocolVersion; +import org.selfbus.updater.upd.UDPResult; +import org.selfbus.updater.upd.UPDCommand; +import org.selfbus.updater.upd.UPDProtocol; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tuwien.auto.calimero.*; +import tuwien.auto.calimero.link.KNXLinkClosedException; +import tuwien.auto.calimero.link.KNXNetworkLink; +import tuwien.auto.calimero.mgmt.Destination; +import tuwien.auto.calimero.mgmt.KNXDisconnectException; +import tuwien.auto.calimero.mgmt.ManagementProcedures; +import tuwien.auto.calimero.mgmt.ManagementProceduresImpl; + +import java.net.UnknownHostException; +import java.time.Duration; +import java.util.Arrays; + +import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.Mcu.MAX_FLASH_ERASE_TIMEOUT; +import static org.selfbus.updater.upd.UPDProtocol.DATA_POSITION; + +/** + * Provides methods to send firmware update telegrams to the bootloader (MCU) + */ +public class DeviceManagement implements AutoCloseable { + /** + * EraseCode for the APCI_MASTER_RESET_PDU (valid from 1..7) + */ + private static final int RESTART_ERASE_CODE = 7; + /** + * Channelnumber for the APCI_MASTER_RESET_PDU + */ + private static final int RESTART_CHANNEL = 255; + + /** + * Default maximum retries a UPD command is sent to the client + */ + private static final int MAX_UPD_COMMAND_RETRY = 3; + + private UDPProtocolVersion protocolVersion; + + private int blockSize; + private int maxPayload; + private int updSendDataOffset; + + private final static Logger logger = LoggerFactory.getLogger(DeviceManagement.class); + /** + * Calimero device management client + */ + private SBManagementClientImpl mc; + private Destination progDestination; + protected KNXNetworkLink link; + + private IndividualAddress progDevice; + + protected CliOptions cliOptions; + + private DeviceManagement () { + setProtocolVersion(UDPProtocolVersion.UDP_V1); + } + + public DeviceManagement(CliOptions cliOptions) { + this(); + this.cliOptions = cliOptions; + } + + public void reconnect(int waitMs) throws KNXException, UpdaterException, UnknownHostException, InterruptedException { + close(); + if (waitMs <= 0) { + waitMs = 100; + } + logger.info("Reconnecting in {} ms", waitMs); + Thread.sleep(waitMs); + open(); + } + + public void open() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { + close(); + this.link = new SBKNXLink(this.cliOptions).openLink(); + this.progDevice = cliOptions.getProgDevicePhysicalAddress(); + this.mc = new SBManagementClientImpl(this.link); + this.mc.setPriority(cliOptions.getPriority()); + this.progDestination = this.mc.createDestination(progDevice, true, false, false); + } + + @Override + public void close() { + logger.debug("Closing {}", this.getClass().getSimpleName()); + if (progDestination != null) { + logger.debug("Releasing progDestination {}", progDestination); + progDestination.close(); + } + + if (mc != null) { + logger.debug("Releasing mc {}", mc); + mc.close(); // mc.close calls already mc.detach() + } + if (link != null) { + logger.debug("Releasing link {}", link); + link.close(); + } + progDestination = null; + progDevice = null; + mc = null; + link = null; + } + + public void restartProgrammingDevice() + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException { + logger.info("Restarting device {}", progDestination); + mc.restart(progDestination); + } + + private void waitRestartTime(int restartTimeSeconds) throws InterruptedException { + while (restartTimeSeconds > 0) { + Thread.sleep(1000); + if (LoggingManager.isConsoleActive()) { + System.out.printf("%s.%s", ansi().fgBright(GREEN), ansi().reset()); + } + restartTimeSeconds--; + } + if (LoggingManager.isConsoleActive()) { + System.out.println(); + } + } + + /** + * Restarts the device running in normal into the Bootloader mode + * + * @param device the IndividualAddress of the device to restart + */ + public void restartDeviceToBootloader(IndividualAddress device) throws InterruptedException { + int restartProcessTime = Mcu.DEFAULT_RESTART_TIME_SECONDS; + try (Destination dest = this.mc.createDestination(device, true, false, false)) { + logger.info("Restarting device {} into bootloader", device); + restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); + logger.info("Device {} reported {}{}{} second(s) for restarting", + device, ansi().fgBright(GREEN), restartProcessTime, ansi().reset()); + waitRestartTime(restartProcessTime); + } catch (final KNXException e) { + logger.info("{}Restart state of device {} unknown. {}{}", + ansi().fgBright(RED), device, e.getMessage(), ansi().reset()); + logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + logger.info("Waiting {}{}{} seconds for device {} to restart", + ansi().fgBright(GREEN), restartProcessTime, ansi().reset(), device); + waitRestartTime(restartProcessTime); + } + } + + public String requestUIDFromDevice() + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { + logger.info("Requesting UID from {}", progDestination.getAddress()); + byte[] result = sendWithRetry(UPDCommand.REQUEST_UID, new byte[0], getMaxUpdCommandRetry()).data(); + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.RESPONSE_UID) { + restartProgrammingDevice(); + throw new UpdaterException("Requesting UID failed!"); + } + + byte[] uid; + if ((result.length >= UPDProtocol.UID_LENGTH_USED) && (result.length <= UPDProtocol.UID_LENGTH_MAX)){ + uid = Arrays.copyOfRange(result, DATA_POSITION, UPDProtocol.UID_LENGTH_USED + DATA_POSITION); + logger.info(" got: {} length {}", UPDProtocol.byteArrayToHex(uid), uid.length); + return UPDProtocol.byteArrayToHex(uid); + } else { + uid = Arrays.copyOfRange(result, DATA_POSITION, result.length - DATA_POSITION); + String errorMsg = String.format("Request UID failed %s result.length=%d, UID_LENGTH_USED=%d, UID_LENGTH_MAX=%d", + UPDProtocol.byteArrayToHex(uid), uid.length, UPDProtocol.UID_LENGTH_USED, UPDProtocol.UID_LENGTH_MAX); + logger.error(errorMsg); + restartProgrammingDevice(); + throw new UpdaterException(errorMsg); + } + } + + public BootloaderIdentity requestBootloaderIdentity() + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { + logger.debug("Requesting Bootloader Identity"); + + byte[] telegram = new byte[2]; + telegram[0] = (byte) ToolInfo.versionMajor(); + telegram[1] = (byte) ToolInfo.versionMinor(); + + byte[] result = sendWithRetry(UPDCommand.REQUEST_BL_IDENTITY, telegram, getMaxUpdCommandRetry()).data(); + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.RESPONSE_BL_IDENTITY) + { + if (command == UPDCommand.RESPONSE_BL_VERSION_MISMATCH) { + long minMajorVersion = result[DATA_POSITION] & 0xff; + long minMinorVersion = result[DATA_POSITION + 1] & 0xff; + logger.error("{}Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.{}", + ansi().fg(RED), ToolInfo.getVersion(), minMajorVersion, minMinorVersion, ansi().reset()); + } + restartProgrammingDevice(); + throw new UpdaterException("Requesting Bootloader Identity failed!"); + } + + BootloaderIdentity bl = BootloaderIdentity.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); + logger.info("Device Bootloader: {}{}{}", ansi().fgBright(YELLOW), bl, ansi().reset()); + + boolean versionsMatch = (bl.versionMajor() > ToolInfo.minMajorVersionBootloader()) || + ((bl.versionMajor() == ToolInfo.minMajorVersionBootloader()) && (bl.versionMinor() >= ToolInfo.minMinorVersionBootloader())); + + if (!versionsMatch) + { + logger.error("{}Bootloader version {} is not compatible, please update Bootloader to version {} or higher{}", + ansi().fg(RED), bl.getVersion(), ToolInfo.minVersionBootloader(), ansi().reset()); + throw new UpdaterException("Bootloader version not compatible!"); + } + return bl; + } + + public BootDescriptor requestBootDescriptor() + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { + logger.debug("Requesting Boot Descriptor"); + byte[] result = sendWithRetry(UPDCommand.REQUEST_BOOT_DESC, new byte[0], getMaxUpdCommandRetry()).data(); + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.RESPONSE_BOOT_DESC) { + restartProgrammingDevice(); + throw new UpdaterException("Boot descriptor request failed!"); + } + + BootDescriptor bootDescriptor = BootDescriptor.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); + logger.info("Current firmware: {}", bootDescriptor); + return bootDescriptor; + } + + public String requestAppVersionString() throws UpdaterException, + InterruptedException { + byte[] result = sendWithRetry(UPDCommand.APP_VERSION_REQUEST, new byte[0], getMaxUpdCommandRetry()).data(); + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.APP_VERSION_RESPONSE) { + return ""; + } + + return new String(result,DATA_POSITION,result.length - DATA_POSITION); // Convert 12 bytes to string starting from result[DATA_POSITION]; + } + + public void unlockDeviceWithUID(String uid) + throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { + logger.info("Unlocking device {} with UID {}", progDestination.getAddress(), uid); + byte[] result = sendWithRetry(UPDCommand.UNLOCK_DEVICE, UPDProtocol.uidToByteArray(uid), getMaxUpdCommandRetry()).data(); + if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { + restartProgrammingDevice(); + throw new UpdaterException(String.format("Unlocking device %s failed.", progDestination.getAddress())); + } + } + + public void eraseAddressRange(long startAddress, long totalLength) + throws KNXLinkClosedException, InterruptedException, UpdaterException, KNXTimeoutException { + long endAddress = startAddress + totalLength - 1; + byte[] telegram = new byte[8]; + Utils.longToStream(telegram, 0 , startAddress); + Utils.longToStream(telegram, 4 , endAddress); + logger.info("Erasing firmware address range: {}", String.format("0x%04X-0x%04X", startAddress, endAddress)); + Duration oldResponseTimeout = mc.responseTimeout(); + Duration newResponseTimeout = MAX_FLASH_ERASE_TIMEOUT.multipliedBy(2); + if (oldResponseTimeout.compareTo(newResponseTimeout) < 0) { + mc.responseTimeout(newResponseTimeout); // temporarily increase responseTimeout + logger.trace("mc.ResponseTimeout temporarily increased to {}", mc.responseTimeout()); + } + + byte[] result = sendWithRetry(UPDCommand.ERASE_ADDRESS_RANGE, telegram, getMaxUpdCommandRetry()).data(); + + mc.responseTimeout(oldResponseTimeout); // restore responseTimeout + logger.trace("mc.ResponseTimeout restored to {}", mc.responseTimeout()); + + if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { + restartProgrammingDevice(); + throw new UpdaterException("Erasing firmware address range failed."); + } + } + + public void eraseFlash() + throws KNXLinkClosedException, InterruptedException, UpdaterException, KNXTimeoutException { + byte[] result = sendWithRetry(UPDCommand.ERASE_COMPLETE_FLASH, new byte[0], getMaxUpdCommandRetry()).data(); + if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { + restartProgrammingDevice(); + throw new UpdaterException("Deleting the entire flash failed."); + } + } + + public void dumpFlashRange(long startAddress, long endAddress) + throws KNXLinkClosedException, InterruptedException, UpdaterException, KNXTimeoutException { + byte[] telegram = new byte[8]; + Utils.longToStream(telegram, 0 , startAddress); + Utils.longToStream(telegram, 4 , endAddress); + // sendWithRetry will always time out, because the mcu is busy dumping the flash + byte[] result = sendWithRetry(UPDCommand.DUMP_FLASH, telegram, 0).data(); + if (UPDProtocol.checkResult(result) != UDPResult.IAP_SUCCESS) { + restartProgrammingDevice(); + throw new UpdaterException("Flash dumping failed."); + } + } + + public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo progressInfo) + throws UpdaterException, InterruptedException { + int nIndex = 0; + ResponseResult result = new ResponseResult(); + while (nIndex < data.length) + { + byte[] txBuffer; + if ((data.length + updSendDataOffset - nIndex) >= getMaxPayload()) { + txBuffer = new byte[getMaxPayload()]; + } + else { + txBuffer = new byte[data.length + updSendDataOffset - nIndex]; + } + + if (getProtocolVersion() == UDPProtocolVersion.UDP_V0) { + txBuffer[0] = (byte) nIndex; // First byte contains mcu's ramBuffer start position to copy to + } + System.arraycopy(data, nIndex, txBuffer, updSendDataOffset, txBuffer.length - updSendDataOffset); + + ResponseResult tmp = sendWithRetry(UPDCommand.SEND_DATA, txBuffer, maxRetry); + result.addCounters(tmp); + + if (UPDProtocol.checkResult(tmp.data(), false) != UDPResult.IAP_SUCCESS) { + throw new UpdaterException("doFlash failed."); + } + updateProgressInfo(progressInfo, txBuffer.length - updSendDataOffset); + nIndex += txBuffer.length - updSendDataOffset; + + if (delay > 0) { + Thread.sleep(delay); //Reduce bus load during data upload, without 2:04, 50ms 2:33, 60ms 2:41, 70ms 2:54, 80ms 3:04 + } + } + result.addWritten(nIndex); + return result; + } + + public void startProgressInfo(ProgressInfo progressInfo) { + logger.info(progressInfo.getHeader()); + // We need one newLine for the gui + ListTextAppenders.appendEvent(Level.INFO, System.lineSeparator()); + } + + public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { + if (progressInfo == null) { + return; + } + + progressInfo.update(bytesDone); + + String logText = String.format("%s%s%s%s", + AnsiCursor.off(), + ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset(), + progressInfo, + AnsiCursor.on()); + logText += " "; // append one space, just in case an exception message may come up + + // console output + if (LoggingManager.isConsoleActive()) { + System.out.print(logText); + if (LoggingManager.isRunningInIntelliJ()) { + // if running in idea debug-console we need to add a newline, + // because the idea debug-console doesn´t support ansi cursor movements + System.out.println(); + } + } + // gui JTextPane output + logText = ansi().cursorUpLine().toString() + logText; // need this CursorUp in gui + ListTextAppenders.appendEvent(Level.INFO, logText); + } + + public void finalProgressInfo(ProgressInfo progressInfo) { + if (progressInfo == null) { + return; + } + + //todo this is stupid + // We need two CursorUp in the gui, because it appends to every logMessage System.lineSeparator() + ListTextAppenders.appendEvent(Level.INFO, ansi().cursorUpLine(1).toString()); + + // Now normal logging in console and gui + logger.info("{} {}", ansi().cursorToColumn(1).toString(), progressInfo); + } + + public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) + throws UpdaterException, KNXLinkClosedException, InterruptedException, KNXTimeoutException { + + byte[] streamBootDescriptor = bootDescriptor.toStream(); + // send new boot descriptor + ResponseResult flashResult = doFlash(streamBootDescriptor, getMaxUpdCommandRetry(), delay, null); + if (flashResult.written() != streamBootDescriptor.length) { + throw new UpdaterException(String.format("Sending Boot descriptor (length %d) failed. Wrote %d", streamBootDescriptor.length, flashResult.written())); + } + if (delay > 0) { + Thread.sleep(delay); + } + int crc32Value = Utils.crc32Value(streamBootDescriptor); + byte[] programBootDescriptor = new byte[8]; + Utils.longToStream(programBootDescriptor, 0, streamBootDescriptor.length); + Utils.longToStream(programBootDescriptor, 4, crc32Value); + logger.debug("Updating boot descriptor with crc32 {}", + String.format("0x%08X, length %d", crc32Value, streamBootDescriptor.length)); + ResponseResult programResult = sendWithRetry(UPDCommand.UPDATE_BOOT_DESC, programBootDescriptor, getMaxUpdCommandRetry()); + if (UPDProtocol.checkResult(programResult.data()) != UDPResult.IAP_SUCCESS) { + restartProgrammingDevice(); + throw new UpdaterException("Updating boot descriptor failed."); + } + programResult.addCounters(flashResult); + if (delay > 0) { + Thread.sleep(delay); + } + } + + public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, + InterruptedException { + logger.debug("Requesting Bootloader statistic"); + byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); + UPDCommand command = UPDCommand.tryFromByteArray(result); + if (command != UPDCommand.RESPONSE_STATISTIC) { + logger.warn("Requesting Bootloader statistic {}failed!{}", ansi().fg(RED), ansi().reset()); + return null; + } + BootloaderStatistic blStatistic = BootloaderStatistic.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); + logger.debug("#Disconnect: {} #repeated T_ACK: {}", blStatistic.getDisconnectCountColored(), + blStatistic.getRepeatedT_ACKcount()); + return blStatistic; + } + + private boolean isLinkAlive() { + if (link == null) { + return false; + } + return link.isOpen(); + } + + private void handleKNXException(final UPDCommand command, final KNXException e, final boolean reconnect) throws + UpdaterException, InterruptedException { + if (e instanceof KNXAckTimeoutException) { + // This exception is on missing/faulty ack at linklayer level thrown, but we never see it here + // todo check public void send(final CEMI frame, final BlockingMode mode) in ConnectionBase + logger.warn("{}Unexpected: never seen before {}{}", ansi().fg(RED), e.getClass().getSimpleName(),ansi().reset()); + } + + logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), + e.getClass().getSimpleName(), ansi().reset()); + + if (!reconnect) { + return; + } + + try { + reconnect(cliOptions.getReconnectMs()); + } + catch (KNXException | UnknownHostException e2) { + throw new UpdaterException(String.format("%s failed.", command), e); + } + } + + public ResponseResult sendWithRetry(final UPDCommand command, final byte[] data, int maxRetry) + throws UpdaterException, InterruptedException { + ResponseResult result = new ResponseResult(); + while (true) { + KNXException lastCaughtException; + try { + byte[] data2 = mc.sendUpdateData(progDestination, command.toByte(), data); + result.copyFromArray(data2); + return result; + } + ///\todo check causes of KNXRemoteException, i think they are unrecoverable + catch (KNXDisconnectException | KNXLinkClosedException e) { + lastCaughtException = e; + result.incDropCount(); + handleKNXException(command, e, true); + } + catch (KNXTimeoutException e) { + // Can happen on a L2 tunnel request ACK timeout or a TL4 ACK timeout + lastCaughtException = e; + result.incTimeoutCount(); + handleKNXException(command, e, true); + } + catch (KNXInvalidResponseException e) { + lastCaughtException = e; + handleKNXException(command, e, true); + } + finally { + if (!isLinkAlive()) { + maxRetry = 0; // exit while + } + } + + if (maxRetry > 0) { + maxRetry--; + } + else { + throw new UpdaterException(String.format("%s failed.", command), lastCaughtException); + } + } + } + + public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throws UpdaterException, + InterruptedException { + try { + ManagementProcedures mgmt = new ManagementProceduresImpl(link); + IndividualAddress[] devices = mgmt.readAddress(); + mgmt.close(); + if ((devices.length == 0) && (progDeviceAddr == null)) { // no device in prog mode + return; + } + else if ((devices.length == 1) && (progDeviceAddr != null) && (progDeviceAddr.equals(devices[0]))) { // correct device in prog mode + return; + } + logger.warn("{}{} Device(s) in bootloader/programming mode: {}{}", + ansi().fgBright(RED), devices.length, Arrays.stream(devices).toArray(), ansi().reset()); + if (devices.length == 0) { + throw new UpdaterException("No device in programming mode."); + } + else { + throw new UpdaterException(String.format("%d wrong device(s) %s are already in programming mode.", devices.length, Arrays.toString(devices))); + } + } catch (KNXException e ) { + throw new UpdaterException(String.format("checkDevicesInProgrammingMode failed. %s", e.getMessage()), e); + } + } + + public UDPProtocolVersion getProtocolVersion() { + return protocolVersion; + } + + public void setProtocolVersion(UDPProtocolVersion protocolVersion) { + this.protocolVersion = protocolVersion; + this.maxPayload = Mcu.MAX_PAYLOAD; + if (this.protocolVersion == UDPProtocolVersion.UDP_V1) { + this.blockSize = Mcu.UPD_PROGRAM_SIZE; + this.updSendDataOffset = 0; + } + else { + this.blockSize = Mcu.FLASH_PAGE_SIZE; + this.updSendDataOffset = 1; + } + } + + public int getBlockSize() { + return blockSize; + } + + public boolean setBlockSize(int blockSize) { + if (getProtocolVersion() == UDPProtocolVersion.UDP_V0) { + return false; + } + this.blockSize = blockSize; + return true; + } + + public int getMaxPayload() { + return maxPayload; + } + + public int getMaxUpdCommandRetry() { + return MAX_UPD_COMMAND_RETRY; + } + + public String getLinkInfo() { + if (this.link == null) { + return "No link available."; + } + + return link.toString(); + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementFactory.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementFactory.java index a919feda..7538b0c5 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementFactory.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagementFactory.java @@ -1,7 +1,6 @@ package org.selfbus.updater.devicemgnt; import org.selfbus.updater.CliOptions; -import org.selfbus.updater.DeviceManagement; final public class DeviceManagementFactory { @SuppressWarnings("unused") From 52c125637b64dd6654723a161b49e2fdf3843b59 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 21:00:32 +0100 Subject: [PATCH 256/359] [Updater] Delete version in README.md --- firmware_updater/updater/README.md | 2 +- firmware_updater/updater/build.gradle | 2 +- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index b4108213..fab10c00 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -1,4 +1,4 @@ -# Selfbus-Updater 1.23 +# Selfbus-Updater ## Requirements diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index c5b61b1b..c560ddd5 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.selfbus' -version = '1.23' ///\todo also change in ../README.md and ToolInfo.java (String version) +version = '1.23' ///\todo also change in ToolInfo.java (String version) description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 82a467c2..82455df2 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -13,7 +13,7 @@ */ public final class ToolInfo { - private static final long versionMajor = 1; ///\todo change also in ../README.md and build.gradle + private static final long versionMajor = 1; ///\todo change also in build.gradle private static final long versionMinor = 23; private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md From 0918096491299f9911cb40921d1687f82699406f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 21:16:12 +0100 Subject: [PATCH 257/359] [Updater] Add Loxone Miniserver Gen 1 example to README.md --- firmware_updater/updater/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index fab10c00..f38113b0 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -101,6 +101,13 @@ Recommended for new firmware versions with known UID: ``` java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1.hex" --uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 ``` +Important for Loxone Miniserver Gen 1: +``` +java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1_flashstart_*.hex" --uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 --full --reconnect 500 --ip-tunnel-reconnect 247 --own x.y.z +``` +**For Loxone Miniserver Gen 1 `--ip-tunnel-reconnect` and `--own x.y.z` are mandatory.** +**`--own x.y.z` must match the Loxone Miniserver´s own physical KNX address** + ## Development ### IDEs: From 1b1cd3312a7fdbdd5011cb7a7d464cd6c269ca09 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 21:17:32 +0100 Subject: [PATCH 258/359] [Updater] Add `_flashstart_` in example `--fileName` --- firmware_updater/updater/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index f38113b0..b0e7f81d 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -95,11 +95,11 @@ java -jar SB_updater-x.xx-all.jar ``` Recommended for new firmware versions if UID is unknown: ``` -java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1.hex" +java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1_flashstart_*.hex" ``` Recommended for new firmware versions with known UID: ``` -java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1.hex" --uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 +java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1_flashstart_*.hex" --uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 ``` Important for Loxone Miniserver Gen 1: ``` From d9572d9a0846cc315fe7745940df5f4e9329ffee Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 21:59:23 +0100 Subject: [PATCH 259/359] [Updater] Use local `calimero-core/release/2.6` till 2.6-rc2 --- firmware_updater/updater/build.gradle | 6 ++++-- firmware_updater/updater/settings.gradle | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index c560ddd5..44898048 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -106,10 +106,12 @@ tasks.named('check') { dependencies { // Use this implementation for debugging with local calimero-core // Set location of local calimero-core in settings.gradle - //implementation 'com.github.calimero:calimero-core' + implementation 'com.github.calimero:calimero-core' + //todo use maven repo again when it contains commit b4114ab (probably with release candidate 2?) + // https://github.com/calimero-project/calimero-core/commit/b4114abe79bc9448e0e0c5b02c480229f4de6953 // calimero knx bus access library - implementation 'com.github.calimero:calimero-core:2.6-rc1' + //implementation 'com.github.calimero:calimero-core:2.6-rc1' // calimero serial tx/rx lib for ft1.2 and tpuart support implementation 'com.github.calimero:calimero-rxtx:2.6-rc1' diff --git a/firmware_updater/updater/settings.gradle b/firmware_updater/updater/settings.gradle index 94e79588..8497d26f 100644 --- a/firmware_updater/updater/settings.gradle +++ b/firmware_updater/updater/settings.gradle @@ -1,8 +1,11 @@ rootProject.name = 'SB_updater' +//todo use maven repo again when it contains commit b4114ab (probably with release candidate 2?) +// https://github.com/calimero-project/calimero-core/commit/b4114abe79bc9448e0e0c5b02c480229f4de6953 + // Use this to build with local calimero-core // Change also dependency in build.gradle -//includeBuild('../../../calimero/calimero-core') +includeBuild('../../../calimero/calimero-core') dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) From c4def1da890aa304d0622c0decb64aa9c02dc869 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 11 Feb 2025 22:00:11 +0100 Subject: [PATCH 260/359] [Updater] Bump version to 1.24 --- firmware_updater/updater/build.gradle | 2 +- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 44898048..1da2b18e 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.selfbus' -version = '1.23' ///\todo also change in ToolInfo.java (String version) +version = '1.24' ///\todo Change also in ToolInfo.java (versionMajor, versionMinor) description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 82455df2..70e9b812 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -14,7 +14,7 @@ public final class ToolInfo { private static final long versionMajor = 1; ///\todo change also in build.gradle - private static final long versionMinor = 23; + private static final long versionMinor = 24; private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md private static final long minMinorVersionBootloader = 0; From 4e7152e55d857266929699c0e47a736392b813d8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 12 Feb 2025 23:16:30 +0100 Subject: [PATCH 261/359] [Updater] Fix `buffer` not repeated if error occurred in last while run If `BYTECOUNT_RECEIVED_TOO_LOW` or `BYTECOUNT_RECEIVED_TOO_HIGH` occurred the `while` would exit anyways, because `fis.available() > 0` is `false` after last while run. Replaced redundant `repeat` with setting and checking of `buffer` with `null`. --- .../src/org/selfbus/updater/FlashFullMode.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index 76126919..0c1e9556 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -38,7 +38,6 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa dm.eraseAddressRange(newFirmware.startAddress(), totalLength); // erase affected flash range } - byte[] buffer = new byte[dm.getBlockSize()]; long progAddress = newFirmware.startAddress(); String logMessage = String.format("Start sending application data (%d bytes)", totalLength); @@ -47,10 +46,7 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa } logger.info(logMessage); - int nRead = 0; - boolean repeat = false; SpinningCursor.reset(); - ProgressInfo progressInfo; BootloaderStatistic bootloaderStatistic; if (logStatistics) { @@ -63,12 +59,16 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa } dm.startProgressInfo(progressInfo); - while (fis.available() > 0) { - if (!repeat) { + int nRead = 0; + byte[] buffer = null; + while ((fis.available() > 0) || (buffer != null)) { + if (buffer == null) { + // Start or buffer was successfully transferred + buffer = new byte[dm.getBlockSize()]; nRead = fis.read(buffer); // Read up to size of buffer } else { - repeat = false; + // Failed to transfer buffer, try again System.out.println(); } @@ -96,7 +96,6 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa UDPResult result = UPDProtocol.checkResult(resultProgramData.data()); if ((result == UDPResult.BYTECOUNT_RECEIVED_TOO_LOW) || (result == UDPResult.BYTECOUNT_RECEIVED_TOO_HIGH)) { - repeat = true; // do not count failed transfer progressInfo.update(-txBuffer.length); } @@ -105,6 +104,7 @@ else if (result == UDPResult.IAP_COMPARE_ERROR) { ansi().fg(RED), CliOptions.OPT_LONG_BLOCKSIZE, ansi().reset())); } else if (result == UDPResult.IAP_SUCCESS) { + buffer = null; progAddress += txBuffer.length; if (bootloaderStatistic != null) { bootloaderStatistic = dm.requestBootLoaderStatistic(); From ad1333cdecb0ad7361bce3afcd06bc59289bb8a5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 12 Feb 2025 23:44:35 +0100 Subject: [PATCH 262/359] [Updater] Use `switch` instead of `if else if else...` --- .../org/selfbus/updater/FlashFullMode.java | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index 0c1e9556..a709af52 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -93,28 +93,31 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa logger.debug("{} with crc32 {}", debugInfo, String.format("0x%08X", crc32)); resultProgramData = dm.sendWithRetry(UPDCommand.PROGRAM, progPars, dm.getMaxUpdCommandRetry()); + resultTotal.addCounters(resultProgramData); // keep track of statistic data UDPResult result = UPDProtocol.checkResult(resultProgramData.data()); - if ((result == UDPResult.BYTECOUNT_RECEIVED_TOO_LOW) || (result == UDPResult.BYTECOUNT_RECEIVED_TOO_HIGH)) { - // do not count failed transfer - progressInfo.update(-txBuffer.length); - } - else if (result == UDPResult.IAP_COMPARE_ERROR) { - throw new UpdaterException(String.format("ProgramData update failed. %sTry again with option '--%s 256'%s", - ansi().fg(RED), CliOptions.OPT_LONG_BLOCKSIZE, ansi().reset())); - } - else if (result == UDPResult.IAP_SUCCESS) { - buffer = null; - progAddress += txBuffer.length; - if (bootloaderStatistic != null) { - bootloaderStatistic = dm.requestBootLoaderStatistic(); - } + switch (result) { + case IAP_SUCCESS: + buffer = null; + progAddress += txBuffer.length; + if (bootloaderStatistic != null) { + bootloaderStatistic = dm.requestBootLoaderStatistic(); + } + break; + + case BYTECOUNT_RECEIVED_TOO_LOW: + case BYTECOUNT_RECEIVED_TOO_HIGH: + // do not count failed transfer and send buffer again in next while run + progressInfo.update(-txBuffer.length); + break; + + case IAP_COMPARE_ERROR: + throw new UpdaterException(String.format("ProgramData update failed. %sTry again with option '--%s 256'%s", + ansi().fg(RED), CliOptions.OPT_LONG_BLOCKSIZE, ansi().reset())); + default: + dm.restartProgrammingDevice(); + throw new UpdaterException("ProgramData update failed."); } - else { - dm.restartProgrammingDevice(); - throw new UpdaterException("ProgramData update failed."); - } - resultTotal.addCounters(resultProgramData); // keep track of statistic data } fis.close(); dm.finalProgressInfo(progressInfo); From 512f1ecf459070dea5cac91b5c84f1ec56063d83 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 12 Feb 2025 23:49:37 +0100 Subject: [PATCH 263/359] [Updater] Fix check also log message not only exception and its message --- .../updater/logging/ThrowableEvaluator.java | 73 ++++++++++++++----- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java index e103926e..6bb0cc56 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java @@ -37,24 +37,58 @@ public class ThrowableEvaluator extends LevelEventEvaluator { /** - * A customizable map that defines the filter configuration for throwable events. - *

Each entry in the map associates a specific exception class (key) with - * a list of specific error messages (value). If an exception of a matching type - * and message is encountered during evaluation, the event may be filtered out. + * The {@code ConditionalFilter} record represents a pair of conditions used to filter + * throwable logging events based on specific log messages and associated exception messages. + * + * @param logMessage The log message as a filtering condition. + * @param exceptionMessage The message of the exception that serves as a filtering condition. + */ + public record ConditionalFilter(String logMessage, String exceptionMessage) {} + + /** + * A customizable map that defines configurable filter criteria for throwable events. + *

+ * Each entry in the map specifies a throwable class (the key) and a corresponding list + * of {@link ConditionalFilter}s (the value). The key represents the type of exception, + * while the value contains specific filtering conditions. A filtering condition is + * defined by a combination of a log message and an exception message. Only events + * containing a throwable that matches the exception type and both of its associated + * filter conditions will be excluded. + *

+ * + * @see ConditionalFilter */ - final Map, List> exceptionFilterMap = Map.of( - KNXAckTimeoutException.class, List.of( - // Loxone Miniserver Gen1 returns always status 0x04 on KNX tunnel requests with sequence number 255 - "maximum send attempts, no service acknowledgment received" + public static final Map, List> EXCEPTION_FILTER_MAP = Map.of( + KNXAckTimeoutException.class, List.of( + // Loxone Miniserver Gen1 returns always status 0x04 on KNX tunnel requests with sequence number 255 + new ConditionalFilter( + "close connection - maximum send attempts", + "maximum send attempts, no service acknowledgment received" + ) + ), + KNXTimeoutException.class, List.of( + // Loxone Miniserver Gen1 does not respond to + // tuwien.auto.calimero.link.KNXNetworkLinkIP.configureWithServerSettings(..) which sends + // M_PropRead.req OT=8 (cEMI Server), PID=51 (PID_MEDIUM_TYPE) + new ConditionalFilter( + "response timeout waiting for confirmation", + "no confirmation reply received for DM prop-read.req objtype 8 instance 1 pid 51 start 1 elements 1" + ), + new ConditionalFilter( + "skip link configuration (use defaults)", + "no confirmation reply received for DM prop-read.req objtype 8 instance 1 pid 51 start 1 elements 1" + ), + + // and M_PropRead.req OT=0 (Device), PID=56 (PID_MAX_APDULENGTH) + new ConditionalFilter( + "response timeout waiting for confirmation", + "no confirmation reply received for DM prop-read.req objtype 0 instance 1 pid 56 start 1 elements 1" ), - KNXTimeoutException.class, List.of( - // Loxone Miniserver Gen1 does not respond to - // tuwien.auto.calimero.link.KNXNetworkLinkIP.configureWithServerSettings(..) which sends - // M_PropRead.req OT=8 (cEMI Server), PID=51 (PID_MEDIUM_TYPE) - "no confirmation reply received for DM prop-read.req objtype 8 instance 1 pid 51 start 1 elements 1", - // and M_PropRead.req OT=0 (Device), PID=56 (PID_MAX_APDULENGTH) - "no confirmation reply received for DM prop-read.req objtype 0 instance 1 pid 56 start 1 elements 1" + new ConditionalFilter( + "skip link configuration (use defaults)", + "no confirmation reply received for DM prop-read.req objtype 0 instance 1 pid 56 start 1 elements 1" ) + ) ); /** @@ -77,13 +111,16 @@ public boolean evaluate(ILoggingEvent event) throws NullPointerException { return true; // no stacktrace present } + String logMessage = event.getFormattedMessage(); Throwable throwable = throwableProxy.getThrowable(); // Check if the current exception is present in the filter map - for (Map.Entry, List> exceptionFilter : exceptionFilterMap.entrySet()) { + for (Map.Entry, List> exceptionFilter : EXCEPTION_FILTER_MAP.entrySet()) { if (exceptionFilter.getKey().isInstance(throwable)) { // Check if the exception message matches one of the filtered messages - for (String filteredMessage : exceptionFilter.getValue()) { - if (Objects.equals(throwable.getMessage(), filteredMessage)) { + for (ConditionalFilter f : exceptionFilter.getValue()) { + // Check if logMessage and exception message match the filter + if ((Objects.equals(logMessage, f.logMessage())) && + (Objects.equals(throwable.getMessage(), f.exceptionMessage()))) { return false; // Filter this event out } } From 8818831062971ddd4f75cb9a13c4a0cc9caac2f6 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 12 Feb 2025 23:51:33 +0100 Subject: [PATCH 264/359] [Updater] Add evaluator tests --- .../updater/logging/MessageEvaluator.java | 2 +- .../logging/LevelEventEvaluatorTest.java | 47 ++++++++ .../updater/logging/MessageEvaluatorTest.java | 60 ++++++++++ .../logging/ThrowableEvaluatorTest.java | 104 ++++++++++++++++++ 4 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java create mode 100644 firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java create mode 100644 firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java index baefaadd..92f2910a 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java @@ -19,7 +19,7 @@ public class MessageEvaluator extends LevelEventEvaluator { * If an event's formatted message contains any of the strings in this list, * the event will be excluded from logging. */ - private static final List FILTERED_MESSAGES = List.of( + public static final List FILTERED_MESSAGES = List.of( // Loxone Miniserver Gen1 returns always status 0x04 on KNX tunnel requests with sequence number 255 "received service acknowledgment status sequence number out of order", // from calimero-core KNXnetIPTunnel.java: diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java new file mode 100644 index 00000000..1afe29bd --- /dev/null +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java @@ -0,0 +1,47 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +import static ch.qos.logback.classic.Level.*; +import static org.junit.jupiter.api.Assertions.*; + +class LevelEventEvaluatorTest { + + /** + * Tests the evaluate method in the LevelEventEvaluator to ensure it correctly evaluates + * logging events against its level filter. + */ + private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; + private final static Logger logger = (Logger) LoggerFactory.getLogger(LevelEventEvaluatorTest.class); + LevelEventEvaluator evaluator = null; + String fqcn = logger.getClass().getName(); + + @BeforeEach + public void setUp() { + evaluator = new LevelEventEvaluator(); + evaluator.setLevel(TRACE); + } + + private boolean callEvaluate(Level level) { + ILoggingEvent event = new LoggingEvent(fqcn, logger, level, "a", null, null); + return evaluator.evaluate(event); + } + + @Test + void testLevel() { + for (Level evaluatorLevel : allLevels) { + evaluator.setLevel(evaluatorLevel); + for (Level level : allLevels) { + // assertFalse(callEvaluate(level)); //todo probably bug? Whole logic is inverted? + // True should be the correct one? + // assertTrue(callEvaluate(level)); + } + } + } +} \ No newline at end of file diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java new file mode 100644 index 00000000..0319fa88 --- /dev/null +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java @@ -0,0 +1,60 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +import static ch.qos.logback.classic.Level.*; +import static org.junit.jupiter.api.Assertions.*; + +class MessageEvaluatorTest { + + /** + * Tests the evaluate method in the MessageEvaluator to ensure it correctly evaluates + * logging events against its filter for log messages. + */ + private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; + private final static Logger logger = (Logger) LoggerFactory.getLogger(MessageEvaluatorTest.class); + MessageEvaluator evaluator = null; + String fqcn = logger.getClass().getName(); + + @BeforeEach + public void setUp() { + evaluator = new MessageEvaluator(); + evaluator.setLevel(TRACE); + } + + private boolean callEvaluate(Level level, String message) { + ILoggingEvent event = new LoggingEvent(fqcn, logger, level, message, null, null); + return evaluator.evaluate(event); + } + + @Test + void testLevel() { + for (Level evaluatorLevel : allLevels) { + evaluator.setLevel(evaluatorLevel); + for (Level level : allLevels) { + assertTrue(callEvaluate(level, "")); + } + } + } + + @Test + void testFilteredMessagesList() { + evaluator.setLevel(INFO); + for (Level level : new Level[]{ERROR, WARN, INFO}) { + // Check some "random" log message is NOT filtered out + assertTrue(callEvaluate(level, "some log message " + level.toString())); + for (String filteredMessage : MessageEvaluator.FILTERED_MESSAGES) { + // Check matching log message is filtered out + assertFalse(callEvaluate(level, filteredMessage)); + // Check containing log message is filtered out + assertFalse(callEvaluate(level, filteredMessage + "foo")); // test + } + } + } +} \ No newline at end of file diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java new file mode 100644 index 00000000..6220c9c3 --- /dev/null +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java @@ -0,0 +1,104 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.lang.reflect.Constructor; +import java.util.Map; + +import static ch.qos.logback.classic.Level.*; +import static org.junit.jupiter.api.Assertions.*; + +public class ThrowableEvaluatorTest { + + /** + * Tests the evaluate method in the ThrowableEvaluator to ensure it correctly evaluates + * logging events against its filter for exceptions and corresponding messages. + */ + private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; + private final static Logger logger = (Logger) LoggerFactory.getLogger(ThrowableEvaluatorTest.class); + ThrowableEvaluator evaluator = null; + String fqcn = logger.getClass().getName(); + + @BeforeEach + public void setUp() { + evaluator = new ThrowableEvaluator(); + evaluator.setLevel(TRACE); + } + + private boolean callEvaluate(Level level, String message, Throwable throwable) { + ILoggingEvent event = new LoggingEvent(fqcn, logger, level, message, throwable, null); + return evaluator.evaluate(event); + } + + @Test + void testLevel() { + for (Level evaluatorLevel : allLevels) { + evaluator.setLevel(evaluatorLevel); + for (Level level : allLevels) { + assertTrue(callEvaluate(level, "", null)); + } + } + } + + @Test + void testThrowableIsNull() { + for (Level evaluatorLevel : allLevels) { + evaluator.setLevel(evaluatorLevel); + for (Level level : allLevels) { + assertTrue(callEvaluate(level, "testThrowableIsNull", null)); + } + } + } + + private Throwable createExceptionInstance(Class exceptionClass, String message) throws Exception { + // Use reflection to find a compatible constructor (one that accepts a String) + Constructor constructor = exceptionClass.getConstructor(String.class); + return constructor.newInstance(message); + } + + @Test + void testExceptionFilterMap() throws Exception { + evaluator.setLevel(INFO); + // Iterate over all exceptions + for (Map.Entry, List> entry : + ThrowableEvaluator.EXCEPTION_FILTER_MAP.entrySet()) { + Class exceptionClass = entry.getKey(); + + // Iterate over all logmessage / exception message + for (ThrowableEvaluator.ConditionalFilter filter : entry.getValue()) { + String logMessage = filter.logMessage(); + String exceptionMessage = filter.exceptionMessage(); + + // Check without exception is not filtered + for (Level level : allLevels) { + assertTrue(callEvaluate(level, logMessage, null)); + } + + Throwable exception = createExceptionInstance(exceptionClass, exceptionMessage); + for (Level level : new Level[]{ERROR, WARN, INFO}) { + // Check matching to EXCEPTION_FILTER_MAP is filtered out + assertFalse(callEvaluate(level, logMessage, exception)); + + // Check not matching logmessage to EXCEPTION_FILTER_MAP is NOT filtered + assertTrue(callEvaluate(level, logMessage + "foo", exception)); + } + + exception = createExceptionInstance(exceptionClass, exceptionMessage + "foo"); + for (Level level : new Level[]{ERROR, WARN, INFO}) { + // Check not matching to EXCEPTION_FILTER_MAP is NOT filtered + assertTrue(callEvaluate(level, logMessage, exception)); + + // Check not matching logmessage to EXCEPTION_FILTER_MAP is NOT filtered + assertTrue(callEvaluate(level, logMessage + "foo", exception)); + } + } + } + } +} \ No newline at end of file From 52d91035e9f823f1ee6190ca1fa3d2f9b42933f3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 13 Feb 2025 05:27:02 +0100 Subject: [PATCH 265/359] [Updater] Suppress swing/awt logs These trace/debug logs came up with commit 88b4b40 --- firmware_updater/updater/src/resources/logback.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 29929d9c..0726c362 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -103,4 +103,9 @@ + + + + + From 4ba565f912114afc32dfffd7bba4625a4622091d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 13 Feb 2025 14:08:24 +0100 Subject: [PATCH 266/359] [Updater] Less empty new lines in console --- firmware_updater/updater/src/org/selfbus/updater/Updater.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index feb6e692..9510a506 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -204,11 +204,9 @@ public void run() { } } else { - System.out.println(); logger.info("{}No firmware file (*.hex) specified! Specify with --{}{}", ansi().bg(RED).fg(BLACK), CliOptions.OPT_LONG_FILENAME, ansi().reset()); logger.info("{}Reading only device information{}", ansi().fgBright(YELLOW), ansi().reset()); - System.out.println(); } dm = DeviceManagementFactory.getDeviceManagement(cliOptions); From 67fcf22d8c5513a31c97f18cdbee5d8b8de831e3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 01:02:25 +0100 Subject: [PATCH 267/359] =?UTF-8?q?[Updater]=20Don=C2=B4t=20print=20full?= =?UTF-8?q?=20stacktrace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 51857c0f..16007a06 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -305,7 +305,7 @@ private boolean loadAllParameters(String fileName) { setComponentNames(); guiSettings.readComponentsSettings(fileName); } catch (IOException e) { - logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + logger.error("{}:{}", e.getClass().getSimpleName(), e.getMessage()); JOptionPane.showMessageDialog(this, String.format(getTranslation("IOException.loadingSettings.Message"), fileName, e.getMessage()), this.getTranslation("Warning"), JOptionPane.WARNING_MESSAGE); From 742065b0cc2ec46b84e60a5a4e70196c596f91fc Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 01:08:23 +0100 Subject: [PATCH 268/359] [Updater] New method `SpinningCursor.setBlank()` --- .../org/selfbus/updater/SpinningCursor.java | 10 +++++++--- .../selfbus/updater/SpinningCursorTest.java | 20 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SpinningCursor.java b/firmware_updater/updater/src/org/selfbus/updater/SpinningCursor.java index 41702c3f..507ff5b3 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SpinningCursor.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SpinningCursor.java @@ -4,11 +4,11 @@ public final class SpinningCursor { @SuppressWarnings("unused") private SpinningCursor() {} // avoids instance creation - private static final char[] cursorPool = {'|', '/', '-', '\\'}; - private static int index = 0; + private static final char[] cursorPool = {' ', '|', '/', '-', '\\'}; // Index 0 is the blank cursor + private static int index = 1; public static void reset() { - index = 0; + index = 1; } public static char getNext() { if (index > (cursorPool.length - 1)) { @@ -19,4 +19,8 @@ public static char getNext() { return nextCursor; } + + public static void setBlank() { + index = 0; + } } diff --git a/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java b/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java index 8cba5f2c..276667f8 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java @@ -1,21 +1,25 @@ package org.selfbus.updater; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class SpinningCursorTest { + @BeforeEach + void setUp() { + SpinningCursor.reset(); + } + @Test public void testInitialCursorState() { // Ensure the initial state is the first character in the cursor array - SpinningCursor.reset(); assertEquals('|', SpinningCursor.getNext()); } @Test public void testCursorRotation() { - SpinningCursor.reset(); // Simulate rotating through the cursor characters assertEquals('|', SpinningCursor.getNext()); assertEquals('/', SpinningCursor.getNext()); @@ -27,7 +31,6 @@ public void testCursorRotation() { @Test public void testCursorReset() { - SpinningCursor.reset(); SpinningCursor.getNext(); // Move to the first character SpinningCursor.reset(); // Reset to initial state assertEquals('|', SpinningCursor.getNext()); // Should still be at the beginning @@ -35,12 +38,21 @@ public void testCursorReset() { @Test public void testCursorIndexWrapping() { - SpinningCursor.reset(); // Rotate through all characters and check wrapping behavior for (int i = 0; i < 100; i++) { SpinningCursor.getNext(); } assertEquals('|', SpinningCursor.getNext()); } + + @Test + public void testCursorSetBlank() { + SpinningCursor.getNext(); + SpinningCursor.setBlank(); + assertEquals(' ', SpinningCursor.getNext()); + assertEquals('|', SpinningCursor.getNext()); + SpinningCursor.reset(); + assertEquals('|', SpinningCursor.getNext()); + } } From 4f764f9c3032e5f9176555d341fb439a89866c3f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 01:09:40 +0100 Subject: [PATCH 269/359] [Updater] Add todo and log for `appender.clearAllFilters()` --- .../src/org/selfbus/updater/logging/LoggingManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java index ba4e9701..2bb496a4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java @@ -42,11 +42,13 @@ public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); if (appender == null) { - logger.warn("setAppenderLogLevel: appender {} not found.", appenderName); + logger.warn("appender {} not found.", appenderName); return; } + //todo this might be quite problematic, we loose all other possible defined filters here appender.clearAllFilters(); + logger.info("Deleted all filters of {}", appender.getName()); ThresholdFilter thresholdFilter = new ThresholdFilter(); thresholdFilter.setLevel(newLogLevel.toString()); From a264ef9493cb787352a3a8844f0e5eda4af925fb Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 01:24:09 +0100 Subject: [PATCH 270/359] [Updater] Add new Encoder, MarkerFilter and Markers --- .../updater/logging/ConsoleEncoder.java | 53 +++++++++++++++++++ .../updater/logging/ExcludeMarkerFilter.java | 38 +++++++++++++ .../org/selfbus/updater/logging/Markers.java | 22 ++++++++ 3 files changed, 113 insertions(+) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/ConsoleEncoder.java create mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/ExcludeMarkerFilter.java create mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/Markers.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ConsoleEncoder.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ConsoleEncoder.java new file mode 100644 index 00000000..045869d4 --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ConsoleEncoder.java @@ -0,0 +1,53 @@ +package org.selfbus.updater.logging; + +import java.util.Arrays; + +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; + +import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_NO_NEWLINE; + +public class ConsoleEncoder extends PatternLayoutEncoder { + @Override + public void start() { + super.start(); + if (!LoggingManager.isRunningInIntelliJ()) { + addInfo("IntelliJ not detected => Marker processing enabled."); + } + } + + @Override + public byte[] encode(ILoggingEvent event) { + final byte[] superByteArray = super.encode(event); + // No marker + if (event.getMarkerList() == null) { + return superByteArray; + } + + // Not the marker we are looking for + if (!(event.getMarkerList().contains(CONSOLE_GUI_NO_NEWLINE))) { + return superByteArray; + } + + final byte[] lineSeparator = System.lineSeparator().getBytes(); + // shorter than the line separator + if (superByteArray.length < lineSeparator.length) { + return superByteArray; + } + + byte[] compare = Arrays.copyOfRange(superByteArray, superByteArray.length - lineSeparator.length, superByteArray.length); + if (!Arrays.equals(compare, lineSeparator)) { + return superByteArray; + } + + final byte[] trimmed = Arrays.copyOfRange(superByteArray, 0, superByteArray.length - lineSeparator.length); + if (LoggingManager.isRunningInIntelliJ()) { + // IntelliJs debug console doesn´t support ansi cursor movements + // so we return the original one from the base pattern decoder + return superByteArray; + } + else { + return trimmed; + } + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ExcludeMarkerFilter.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ExcludeMarkerFilter.java new file mode 100644 index 00000000..16c1b525 --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ExcludeMarkerFilter.java @@ -0,0 +1,38 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +@SuppressWarnings("unused") +public class ExcludeMarkerFilter extends Filter { + + String marker; + + @Override + public FilterReply decide(ILoggingEvent event) { + Marker markerToMatch = MarkerFactory.getMarker(marker); + if (event.getMarkerList() == null) { + return FilterReply.NEUTRAL; + } + + if (event.getMarkerList().contains(markerToMatch)) { + return FilterReply.DENY; + } + else { + return FilterReply.NEUTRAL; + } + } + + @SuppressWarnings("unused") + public String getMarker() { + return marker; + } + + @SuppressWarnings("unused") + public void setMarker(String marker) { + this.marker = marker; + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/Markers.java b/firmware_updater/updater/src/org/selfbus/updater/logging/Markers.java new file mode 100644 index 00000000..2666dd7d --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/Markers.java @@ -0,0 +1,22 @@ +package org.selfbus.updater.logging; + +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +/** + * The {@code Markers} class provides a set of predefined logging markers + * that can be used to categorize and filter log messages in the application. + */ +public class Markers { + + /** + * Marker used to categorize log messages intended exclusively for the console and GUI. + */ + public static final Marker CONSOLE_GUI_ONLY = MarkerFactory.getMarker("CONSOLE_GUI_ONLY"); + + /** + * Marker used to categorize log messages intended exclusively for the console and GUI, + * specifically to handle cases where newline characters should be omitted. + */ + public static final Marker CONSOLE_GUI_NO_NEWLINE = MarkerFactory.getMarker("CONSOLE_GUI_NO_NEWLINE"); +} From 19c62fa0c42a05c1f164b8eb951c4cd305fa81b2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 01:29:06 +0100 Subject: [PATCH 271/359] [Updater] Unify gui and console progress logging --- .../org/selfbus/updater/FlashDiffMode.java | 2 +- .../org/selfbus/updater/FlashFullMode.java | 6 +-- .../src/org/selfbus/updater/Updater.java | 6 --- .../updater/devicemgnt/DeviceManagement.java | 54 ++++++------------- .../org/selfbus/updater/gui/TextAppender.java | 16 ++++-- .../updater/logging/LoggingManager.java | 4 -- .../updater/src/resources/logback.xml | 22 +++++++- 7 files changed, 49 insertions(+), 61 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index 556114b7..dac4643c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -137,7 +137,7 @@ public static ResponseResult doDifferentialFlash(DeviceManagement dm, long start throw new UpdaterException("DiffMode PROGRAM_DECOMPRESSED_DATA failed."); } }); - dm.finalProgressInfo(progressInfo); + dm.logAndPrintProgressInfo(progressInfo); result.get().setWritten(differ.getTotalBytesTransferred()); return result.get(); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index a709af52..be58d14b 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -67,10 +67,6 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa buffer = new byte[dm.getBlockSize()]; nRead = fis.read(buffer); // Read up to size of buffer } - else { - // Failed to transfer buffer, try again - System.out.println(); - } byte[] txBuffer = new byte[nRead]; System.arraycopy(buffer, 0, txBuffer, 0, nRead); @@ -120,7 +116,7 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa } } fis.close(); - dm.finalProgressInfo(progressInfo); + dm.logAndPrintProgressInfo(progressInfo); resultTotal.setWritten(progressInfo.getBytesDone()); return resultTotal; } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 9510a506..5aaebf57 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -50,12 +50,6 @@ private Updater() {} // disable default constructor * @param cliOptions the command-line options to be used */ public Updater(CliOptions cliOptions) { - if (LoggingManager.isRunningInIntelliJ()) { - logger.info("IntelliJ detected => adding newline to console progress logging"); - } - else { - logger.debug("IntelliJ not detected => normal console logging active"); - } logger.debug(ToolInfo.getFullInfo()); logger.debug(Settings.getLibraryHeader(false)); this.cliOptions = cliOptions; diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 566e9160..b13d323d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -1,12 +1,9 @@ package org.selfbus.updater.devicemgnt; -import ch.qos.logback.classic.Level; import org.selfbus.updater.*; import org.selfbus.updater.bootloader.BootDescriptor; import org.selfbus.updater.bootloader.BootloaderIdentity; import org.selfbus.updater.bootloader.BootloaderStatistic; -import org.selfbus.updater.logging.ListTextAppenders; -import org.selfbus.updater.logging.LoggingManager; import org.selfbus.updater.upd.UDPProtocolVersion; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; @@ -28,6 +25,8 @@ import static org.fusesource.jansi.Ansi.*; import static org.fusesource.jansi.Ansi.Color.*; import static org.selfbus.updater.Mcu.MAX_FLASH_ERASE_TIMEOUT; +import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_ONLY; +import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_NO_NEWLINE; import static org.selfbus.updater.upd.UPDProtocol.DATA_POSITION; /** @@ -125,14 +124,10 @@ public void restartProgrammingDevice() private void waitRestartTime(int restartTimeSeconds) throws InterruptedException { while (restartTimeSeconds > 0) { Thread.sleep(1000); - if (LoggingManager.isConsoleActive()) { - System.out.printf("%s.%s", ansi().fgBright(GREEN), ansi().reset()); - } + logger.info(CONSOLE_GUI_NO_NEWLINE, String.format("%s.%s", ansi().fgBright(GREEN), ansi().reset())); restartTimeSeconds--; } - if (LoggingManager.isConsoleActive()) { - System.out.println(); - } + logger.info(CONSOLE_GUI_ONLY, ""); // Just a new line } /** @@ -340,9 +335,7 @@ public ResponseResult doFlash(byte[] data, int maxRetry, int delay, ProgressInfo } public void startProgressInfo(ProgressInfo progressInfo) { - logger.info(progressInfo.getHeader()); - // We need one newLine for the gui - ListTextAppenders.appendEvent(Level.INFO, System.lineSeparator()); + logger.info(CONSOLE_GUI_ONLY, progressInfo.getHeader()); } public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { @@ -351,39 +344,24 @@ public void updateProgressInfo(ProgressInfo progressInfo, long bytesDone) { } progressInfo.update(bytesDone); + printProgressInfo(progressInfo); + } - String logText = String.format("%s%s%s%s", + private void printProgressInfo(ProgressInfo progressInfo) { + // append one space, just in case an exception message may come up + String logText = String.format("%s%s%s%s ", AnsiCursor.off(), ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset(), progressInfo, AnsiCursor.on()); - logText += " "; // append one space, just in case an exception message may come up - - // console output - if (LoggingManager.isConsoleActive()) { - System.out.print(logText); - if (LoggingManager.isRunningInIntelliJ()) { - // if running in idea debug-console we need to add a newline, - // because the idea debug-console doesn´t support ansi cursor movements - System.out.println(); - } - } - // gui JTextPane output - logText = ansi().cursorUpLine().toString() + logText; // need this CursorUp in gui - ListTextAppenders.appendEvent(Level.INFO, logText); + logger.info(CONSOLE_GUI_NO_NEWLINE, logText); } - public void finalProgressInfo(ProgressInfo progressInfo) { - if (progressInfo == null) { - return; - } - - //todo this is stupid - // We need two CursorUp in the gui, because it appends to every logMessage System.lineSeparator() - ListTextAppenders.appendEvent(Level.INFO, ansi().cursorUpLine(1).toString()); - - // Now normal logging in console and gui - logger.info("{} {}", ansi().cursorToColumn(1).toString(), progressInfo); + public void logAndPrintProgressInfo(ProgressInfo progressInfo) { + logger.debug("{}{}{}", progressInfo.getHeader(), System.lineSeparator(), progressInfo); + SpinningCursor.setBlank(); + printProgressInfo(progressInfo); + logger.info(CONSOLE_GUI_ONLY, ""); // Just a new line } public void programBootDescriptor(BootDescriptor bootDescriptor, int delay) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java b/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java index 521fc164..fdccbfca 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java @@ -8,6 +8,8 @@ import javax.swing.*; import javax.swing.text.BadLocationException; +import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_NO_NEWLINE; + public class TextAppender extends AppenderBase { private JTextPane textPane; @@ -39,11 +41,15 @@ protected void append(ILoggingEvent event) { appendStackTrace(throwableProxy, eventMessage); } - String logMessage = eventMessage.toString(); - - // Ensure the message ends with a line separator - if (!logMessage.endsWith(System.lineSeparator())) { - logMessage += System.lineSeparator(); + String logMessage; + if ((event.getMarkerList() != null) && (event.getMarkerList().contains(CONSOLE_GUI_NO_NEWLINE))) { + logMessage = eventMessage.toString(); + } + else { + // Ensure same behavior like ConsoleAppender + // It gets a new line added to every logging message + // by it´s pattern %message%n (notice the n = new line) in logback.xml + logMessage = eventMessage.append(System.lineSeparator()).toString(); } String finalLogMessage = logMessage; diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java index 2bb496a4..9efe9162 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java @@ -84,10 +84,6 @@ public static Level getAppenderLogLevel(String appenderName) { return null; } - public static boolean isConsoleActive() { - return (LoggingManager.getAppenderLogLevel(CONSOLE_APPENDER_NAME) == Level.INFO); - } - public static boolean isRunningInIntelliJ() { // Check for IntelliJ debugging JVM arguments return ManagementFactory.getRuntimeMXBean().getInputArguments().stream() diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 0726c362..02dfad10 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -32,7 +32,7 @@ DENY NEUTRAL - + %message%n @@ -69,6 +69,12 @@ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level [%logger] %method (%line) %message%n --> + + CONSOLE_GUI_ONLY + + + CONSOLE_GUI_NO_NEWLINE + @@ -79,6 +85,12 @@ %d{HH:mm:ss.SSS}%thread%level%logger%method%line%message + + CONSOLE_GUI_ONLY + + + CONSOLE_GUI_NO_NEWLINE + - + From a6d90b5c6aea3bb6f8bc429848ca2293e86a010b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 01:35:08 +0100 Subject: [PATCH 272/359] [Updater] Move progress stuff to `.progress` --- .../org/selfbus/updater/FlashDiffMode.java | 2 + .../org/selfbus/updater/FlashFullMode.java | 3 + .../src/org/selfbus/updater/Updater.java | 1 + .../updater/devicemgnt/DeviceManagement.java | 3 + .../updater/gui/ConColorsToStyledDoc.java | 2 +- .../updater/{ => progress}/AnsiCursor.java | 36 +-- .../updater/{ => progress}/ProgressInfo.java | 230 +++++++++--------- .../{ => progress}/ProgressInfoAdvanced.java | 4 +- .../{ => progress}/SpinningCursor.java | 52 ++-- .../selfbus/updater/SpinningCursorTest.java | 1 + 10 files changed, 172 insertions(+), 162 deletions(-) rename firmware_updater/updater/src/org/selfbus/updater/{ => progress}/AnsiCursor.java (87%) rename firmware_updater/updater/src/org/selfbus/updater/{ => progress}/ProgressInfo.java (95%) rename firmware_updater/updater/src/org/selfbus/updater/{ => progress}/ProgressInfoAdvanced.java (84%) rename firmware_updater/updater/src/org/selfbus/updater/{ => progress}/SpinningCursor.java (90%) diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index dac4643c..d56e8c6d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -4,6 +4,8 @@ import org.selfbus.updater.bootloader.BootDescriptor; import org.selfbus.updater.devicemgnt.DeviceManagement; import org.selfbus.updater.mode.differential.FlashDiff; +import org.selfbus.updater.progress.ProgressInfo; +import org.selfbus.updater.progress.SpinningCursor; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; import org.selfbus.updater.upd.UPDProtocol; diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index be58d14b..e7ca4831 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -2,6 +2,9 @@ import org.selfbus.updater.bootloader.BootloaderStatistic; import org.selfbus.updater.devicemgnt.DeviceManagement; +import org.selfbus.updater.progress.ProgressInfo; +import org.selfbus.updater.progress.ProgressInfoAdvanced; +import org.selfbus.updater.progress.SpinningCursor; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; import org.selfbus.updater.upd.UPDProtocol; diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 5aaebf57..36761e36 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -7,6 +7,7 @@ import org.selfbus.updater.devicemgnt.DeviceManagement; import org.selfbus.updater.devicemgnt.DeviceManagementFactory; import org.selfbus.updater.logging.LoggingManager; +import org.selfbus.updater.progress.AnsiCursor; import tuwien.auto.calimero.*; import org.selfbus.updater.bootloader.BootDescriptor; import org.selfbus.updater.bootloader.BootloaderIdentity; diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index b13d323d..9ab7f5ef 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -4,6 +4,9 @@ import org.selfbus.updater.bootloader.BootDescriptor; import org.selfbus.updater.bootloader.BootloaderIdentity; import org.selfbus.updater.bootloader.BootloaderStatistic; +import org.selfbus.updater.progress.AnsiCursor; +import org.selfbus.updater.progress.ProgressInfo; +import org.selfbus.updater.progress.SpinningCursor; import org.selfbus.updater.upd.UDPProtocolVersion; import org.selfbus.updater.upd.UDPResult; import org.selfbus.updater.upd.UPDCommand; diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index e4082695..c184ca6c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -1,6 +1,6 @@ package org.selfbus.updater.gui; -import org.selfbus.updater.AnsiCursor; +import org.selfbus.updater.progress.AnsiCursor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java b/firmware_updater/updater/src/org/selfbus/updater/progress/AnsiCursor.java similarity index 87% rename from firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java rename to firmware_updater/updater/src/org/selfbus/updater/progress/AnsiCursor.java index 505d917d..5694e0f3 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/AnsiCursor.java +++ b/firmware_updater/updater/src/org/selfbus/updater/progress/AnsiCursor.java @@ -1,18 +1,18 @@ -package org.selfbus.updater; - -public final class AnsiCursor { - @SuppressWarnings("unused") - private AnsiCursor() {} // avoids instance creation - - @SuppressWarnings("SameReturnValue") - public static String on() { - // cursor on - return "\033[?25h"; - } - - @SuppressWarnings("SameReturnValue") - public static String off() { - // cursor off - return "\033[?25l"; - } -} +package org.selfbus.updater.progress; + +public final class AnsiCursor { + @SuppressWarnings("unused") + private AnsiCursor() {} // avoids instance creation + + @SuppressWarnings("SameReturnValue") + public static String on() { + // cursor on + return "\033[?25h"; + } + + @SuppressWarnings("SameReturnValue") + public static String off() { + // cursor off + return "\033[?25l"; + } +} diff --git a/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java b/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java similarity index 95% rename from firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java rename to firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java index 0170db04..ac5aba69 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ProgressInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java @@ -1,115 +1,115 @@ -package org.selfbus.updater; - -import org.fusesource.jansi.Ansi; - -import static org.fusesource.jansi.Ansi.Color.GREEN; -import static org.fusesource.jansi.Ansi.Color.YELLOW; -import static org.fusesource.jansi.Ansi.ansi; - -public class ProgressInfo { - private long startTimeMs; - private long lastUpdateTimeMs; - private float minBytesPerSecond; - private float maxBytesPerSecond; - private float currentBytesPerSecond; - private long totalBytes; - private long bytesDone; - private float percentageDone; - - @SuppressWarnings("unused") - private ProgressInfo() {} - - public ProgressInfo(long totalByteCount) { - minBytesPerSecond = -1.0f; - maxBytesPerSecond = -1.0f; - currentBytesPerSecond = -1.0f; - totalBytes = totalByteCount; - bytesDone = 0; - startTimeMs = System.currentTimeMillis(); - lastUpdateTimeMs = startTimeMs; - } - - public void update(long byteCount) { - // logging of connection speed - long duration = System.currentTimeMillis() - lastUpdateTimeMs; - currentBytesPerSecond = (float) byteCount / (duration / 1000.0f); - lastUpdateTimeMs = System.currentTimeMillis(); - - if (minBytesPerSecond < 0) { - minBytesPerSecond = currentBytesPerSecond; - } - - if (maxBytesPerSecond < 0) { - maxBytesPerSecond = currentBytesPerSecond; - } - - minBytesPerSecond = Math.min(minBytesPerSecond, currentBytesPerSecond); - maxBytesPerSecond = Math.max(maxBytesPerSecond, currentBytesPerSecond); - bytesDone += byteCount; - percentageDone = 100.0f * (bytesDone) / getTotalBytes(); - } - - private String getSpeed(boolean averageSpeed) { - float bytesPerSecond; - if (averageSpeed) { - long duration = getLastUpdateTimeMs() - getStartTimeMs(); - bytesPerSecond = (float) getBytesDone() / (duration / 1000.0f); - } - else { - bytesPerSecond = getCurrentBytesPerSecond(); - } - Ansi.Color color; - if (bytesPerSecond >= 50.0) { - color = GREEN; - } - else { - color = YELLOW; - } - return String.format("%s%5.1f%s", ansi().fgBright(color), bytesPerSecond, ansi().reset()); - } - - public String getHeader() { - return " Done Speed Avg Min Max Time"; - } - - public String toString() { - return String.format("%5.1f%% %s %s %5.1f %5.1f %tM:%= 50.0) { + color = GREEN; + } + else { + color = YELLOW; + } + return String.format("%s%5.1f%s", ansi().fgBright(color), bytesPerSecond, ansi().reset()); + } + + public String getHeader() { + return " Done Speed Avg Min Max Time"; + } + + public String toString() { + return String.format("%5.1f%% %s %s %5.1f %5.1f %tM:% (cursorPool.length - 1)) { - reset(); - } - char nextCursor = cursorPool[index]; - index++; - - return nextCursor; - } - - public static void setBlank() { - index = 0; - } -} +package org.selfbus.updater.progress; + +public final class SpinningCursor { + @SuppressWarnings("unused") + private SpinningCursor() {} // avoids instance creation + + private static final char[] cursorPool = {' ', '|', '/', '-', '\\'}; // Index 0 is the blank cursor + private static int index = 1; + + public static void reset() { + index = 1; + } + public static char getNext() { + if (index > (cursorPool.length - 1)) { + reset(); + } + char nextCursor = cursorPool[index]; + index++; + + return nextCursor; + } + + public static void setBlank() { + index = 0; + } +} diff --git a/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java b/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java index 276667f8..e493d624 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/SpinningCursorTest.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.selfbus.updater.progress.SpinningCursor; import static org.junit.jupiter.api.Assertions.*; From 5e6f6ec679e28a4c075e0e8cac766e0bb0ea1480 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 01:52:48 +0100 Subject: [PATCH 273/359] [Updater] Update gui todo missing cli options --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 16007a06..a3721322 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -376,13 +376,9 @@ private CliOptions getCliOptions() throws KNXFormatException, ParseException { argsList.add(argument(OPT_LONG_LOGSTATISTIC)); */ // argsList.add(argument(OPT_LONG_BLOCKSIZE, comboBox*)); // todo add gui combobox --blocksize 256, 512, 1024 - -/* - if (checkBox*.isVisible() && checkBox*.isSelected()) // todo add gui checkbox --discover - argsList.add(argument(OPT_LONG_DISCOVER)); - */ - // argsList.add(argument(OPT_LONG_RECONNECT, textFieldReconnect)); // todo add gui textfield --reconnect - // todo add gui button for --discover + // argsList.add(argument(OPT_LONG_RECONNECT, textFieldReconnect)); // todo add gui textfield --reconnect 100-12500 + // todo add gui button for OPT_LONG_DISCOVER --discover + // argsList.add(argument(OPT_LONG_RECONNECT_SEQ_NUMBER, textFieldSeqNmbReconn)); todo add textfield --ip-tunnel-reconnect 100-247 return new CliOptions(argsList); } From 507b6c4b47232a2a53584a8b20a3ac2086018975 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 11:56:27 +0100 Subject: [PATCH 274/359] [Updater] Make `fqcn` final --- .../org/selfbus/updater/logging/LevelEventEvaluatorTest.java | 2 +- .../test/org/selfbus/updater/logging/MessageEvaluatorTest.java | 2 +- .../org/selfbus/updater/logging/ThrowableEvaluatorTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java index 1afe29bd..e931e872 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java @@ -20,7 +20,7 @@ class LevelEventEvaluatorTest { private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; private final static Logger logger = (Logger) LoggerFactory.getLogger(LevelEventEvaluatorTest.class); LevelEventEvaluator evaluator = null; - String fqcn = logger.getClass().getName(); + final String fqcn = logger.getClass().getName(); @BeforeEach public void setUp() { diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java index 0319fa88..2a2d794c 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java @@ -20,7 +20,7 @@ class MessageEvaluatorTest { private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; private final static Logger logger = (Logger) LoggerFactory.getLogger(MessageEvaluatorTest.class); MessageEvaluator evaluator = null; - String fqcn = logger.getClass().getName(); + final String fqcn = logger.getClass().getName(); @BeforeEach public void setUp() { diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java index 6220c9c3..c552f6f2 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java @@ -24,7 +24,7 @@ public class ThrowableEvaluatorTest { private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; private final static Logger logger = (Logger) LoggerFactory.getLogger(ThrowableEvaluatorTest.class); ThrowableEvaluator evaluator = null; - String fqcn = logger.getClass().getName(); + final String fqcn = logger.getClass().getName(); @BeforeEach public void setUp() { From 733fe1276540638e890f5fd16d0242bf41454535 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 11:57:01 +0100 Subject: [PATCH 275/359] [Updater] Fix project inspector warnings --- .../src/org/selfbus/updater/logging/ConsoleEncoder.java | 3 ++- .../org/selfbus/updater/logging/ThrowableEvaluatorTest.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ConsoleEncoder.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ConsoleEncoder.java index 045869d4..771695ce 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ConsoleEncoder.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ConsoleEncoder.java @@ -7,6 +7,7 @@ import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_NO_NEWLINE; +@SuppressWarnings("unused") public class ConsoleEncoder extends PatternLayoutEncoder { @Override public void start() { @@ -42,7 +43,7 @@ public byte[] encode(ILoggingEvent event) { final byte[] trimmed = Arrays.copyOfRange(superByteArray, 0, superByteArray.length - lineSeparator.length); if (LoggingManager.isRunningInIntelliJ()) { - // IntelliJs debug console doesn´t support ansi cursor movements + // IntelliJs debug console does not support ansi cursor movements // so we return the original one from the base pattern decoder return superByteArray; } diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java index c552f6f2..ac944690 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java @@ -71,7 +71,7 @@ void testExceptionFilterMap() throws Exception { ThrowableEvaluator.EXCEPTION_FILTER_MAP.entrySet()) { Class exceptionClass = entry.getKey(); - // Iterate over all logmessage / exception message + // Iterate over all log messages / exception messages for (ThrowableEvaluator.ConditionalFilter filter : entry.getValue()) { String logMessage = filter.logMessage(); String exceptionMessage = filter.exceptionMessage(); @@ -86,7 +86,7 @@ void testExceptionFilterMap() throws Exception { // Check matching to EXCEPTION_FILTER_MAP is filtered out assertFalse(callEvaluate(level, logMessage, exception)); - // Check not matching logmessage to EXCEPTION_FILTER_MAP is NOT filtered + // Check not matching log messages to EXCEPTION_FILTER_MAP is NOT filtered assertTrue(callEvaluate(level, logMessage + "foo", exception)); } @@ -95,7 +95,7 @@ void testExceptionFilterMap() throws Exception { // Check not matching to EXCEPTION_FILTER_MAP is NOT filtered assertTrue(callEvaluate(level, logMessage, exception)); - // Check not matching logmessage to EXCEPTION_FILTER_MAP is NOT filtered + // Check not matching log messages to EXCEPTION_FILTER_MAP is NOT filtered assertTrue(callEvaluate(level, logMessage + "foo", exception)); } } From dc72cb77ff8d3311024b477190cf741096de229e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 13:24:40 +0100 Subject: [PATCH 276/359] [Updater] Simplify console/gui log filtering Filters in `logback.xml` are called in their declared order. So `LevelEventEvaluator`s work is already done by the `ThresholdFilter`. Extending `Filter` instead of `EventEvaluatorBase` simplifies the filters and their configuration in `logback.xml` --- .../updater/logging/LevelEventEvaluator.java | 28 ----- ...ssageEvaluator.java => MessageFilter.java} | 30 +++-- ...bleEvaluator.java => ThrowableFilter.java} | 23 ++-- .../updater/src/resources/logback.xml | 36 +----- .../logging/LevelEventEvaluatorTest.java | 47 -------- .../updater/logging/MessageEvaluatorTest.java | 60 ---------- .../updater/logging/MessageFilterTest.java | 46 ++++++++ .../logging/ThrowableEvaluatorTest.java | 104 ------------------ .../updater/logging/ThrowableFilterTest.java | 81 ++++++++++++++ 9 files changed, 157 insertions(+), 298 deletions(-) delete mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/LevelEventEvaluator.java rename firmware_updater/updater/src/org/selfbus/updater/logging/{MessageEvaluator.java => MessageFilter.java} (55%) rename firmware_updater/updater/src/org/selfbus/updater/logging/{ThrowableEvaluator.java => ThrowableFilter.java} (87%) delete mode 100644 firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java delete mode 100644 firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java create mode 100644 firmware_updater/updater/test/org/selfbus/updater/logging/MessageFilterTest.java delete mode 100644 firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java create mode 100644 firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableFilterTest.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/LevelEventEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LevelEventEvaluator.java deleted file mode 100644 index b3321a3d..00000000 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/LevelEventEvaluator.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.selfbus.updater.logging; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.boolex.EventEvaluatorBase; - -import static ch.qos.logback.classic.Level.TRACE; - -public class LevelEventEvaluator extends EventEvaluatorBase { - - private Level level = TRACE; - - @Override - public boolean evaluate(ILoggingEvent event) throws NullPointerException { - Level eventLevel = event.getLevel(); - return !eventLevel.isGreaterOrEqual(getLevel()); - } - - @SuppressWarnings("unused") - public Level getLevel() { - return level; - } - - @SuppressWarnings("unused") - public void setLevel(Level level) { - this.level = level; - } -} diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java similarity index 55% rename from firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java rename to firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java index 92f2910a..a6e4d5d2 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java @@ -1,18 +1,20 @@ package org.selfbus.updater.logging; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; import java.util.List; /** - * The {@code MessageEvaluator} class is a custom implementation of {@code LevelEventEvaluator}. + * The {@code MessageFilter} class is a custom implementation of {@code Filter}. * It is used to evaluate logging events and filter out specific log messages based on a * predefined list of messages. *

* This functionality can be useful for excluding log messages that are deemed * irrelevant or repetitive during runtime. */ -public class MessageEvaluator extends LevelEventEvaluator { +public class MessageFilter extends Filter { /** * A list of log messages that should be filtered out. @@ -28,29 +30,25 @@ public class MessageEvaluator extends LevelEventEvaluator { ); /** - * Evaluates the provided logging event. - *

- * This method first delegates evaluation to the superclass method. If the superclass - * evaluation passes, the event is logged. Otherwise, it checks whether the event's - * message contains any of the filtered messages specified in {@code FILTERED_MESSAGES}. - * If a match is found, the event will not be logged. + * Evaluates a logging event and determines whether the event should + * be accepted or handled neutrally based on its log message content. * - * @param event the {@code ILoggingEvent} to evaluate - * @return {@code true} if the event passes all filters and should be logged, {@code false} otherwise - * @throws NullPointerException if the event or its formatted message is null + * @param event the logging event to be evaluated. + * @return {@code FilterReply.DENY} if the event's message contains any of the predefined + * filtered messages, otherwise {@code FilterReply.NEUTRAL}. */ @Override - public boolean evaluate(ILoggingEvent event) throws NullPointerException { - if (super.evaluate(event)) { - return true; + public FilterReply decide(ILoggingEvent event) { + if (!isStarted()) { + return FilterReply.NEUTRAL; } String message = event.getFormattedMessage(); for (String filteredMessage : FILTERED_MESSAGES) { if (message.contains(filteredMessage)) { - return false; // Filter this event out + return FilterReply.DENY; } } - return true; + return FilterReply.NEUTRAL; } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableFilter.java similarity index 87% rename from firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java rename to firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableFilter.java index 6bb0cc56..b2ce3973 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableEvaluator.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ThrowableFilter.java @@ -2,6 +2,8 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ThrowableProxy; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; import tuwien.auto.calimero.KNXAckTimeoutException; import tuwien.auto.calimero.KNXTimeoutException; @@ -10,7 +12,7 @@ import java.util.Objects; /** - * The {@code ThrowableEvaluator} class extends {@link LevelEventEvaluator} + * The {@code ThrowableFilter} class extends {@code Filter} * to provide a custom evaluation of logging events that filters out specific exceptions * with matching messages. * @@ -34,7 +36,7 @@ *

This can be useful in logging systems where specific repetitive or expected exceptions * (e.g., timeout exceptions) need to be ignored to reduce log noise. */ -public class ThrowableEvaluator extends LevelEventEvaluator { +public class ThrowableFilter extends Filter { /** * The {@code ConditionalFilter} record represents a pair of conditions used to filter @@ -95,20 +97,18 @@ public record ConditionalFilter(String logMessage, String exceptionMessage) {} * Evaluates a logging event to determine whether it should be processed or filtered. * * @param event the {@link ILoggingEvent} instance to be evaluated. - * @return {@code true} if the event should be processed; {@code false} if the event + * @return {@code FilterReply.NEUTRAL} if the event should be processed; {@code FilterReply.DENY} if the event * should be filtered out. - * @throws NullPointerException if a null input is encountered during evaluation. - * This is unlikely since events usually undergo prior validation. */ @Override - public boolean evaluate(ILoggingEvent event) throws NullPointerException { - if (super.evaluate(event)) { - return true; // Ignore log level + public FilterReply decide(ILoggingEvent event) { + if (!isStarted()) { + return FilterReply.NEUTRAL; } ThrowableProxy throwableProxy = (ThrowableProxy) event.getThrowableProxy(); if (throwableProxy == null) { - return true; // no stacktrace present + return FilterReply.NEUTRAL; // no stacktrace present } String logMessage = event.getFormattedMessage(); @@ -121,12 +121,11 @@ public boolean evaluate(ILoggingEvent event) throws NullPointerException { // Check if logMessage and exception message match the filter if ((Objects.equals(logMessage, f.logMessage())) && (Objects.equals(throwable.getMessage(), f.exceptionMessage()))) { - return false; // Filter this event out + return FilterReply.DENY; // Filter this event out } } } } - return true; + return FilterReply.NEUTRAL; } - } diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 02dfad10..866e66d0 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -4,9 +4,6 @@ - - - - - - WARN - - DENY - NEUTRAL - + - - - WARN - - DENY - NEUTRAL - + + %message%n @@ -42,21 +28,9 @@ INFO - - - WARN - - DENY - NEUTRAL - + - - - INFO - - DENY - NEUTRAL - + diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java deleted file mode 100644 index e931e872..00000000 --- a/firmware_updater/updater/test/org/selfbus/updater/logging/LevelEventEvaluatorTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.selfbus.updater.logging; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.LoggerFactory; - -import static ch.qos.logback.classic.Level.*; -import static org.junit.jupiter.api.Assertions.*; - -class LevelEventEvaluatorTest { - - /** - * Tests the evaluate method in the LevelEventEvaluator to ensure it correctly evaluates - * logging events against its level filter. - */ - private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; - private final static Logger logger = (Logger) LoggerFactory.getLogger(LevelEventEvaluatorTest.class); - LevelEventEvaluator evaluator = null; - final String fqcn = logger.getClass().getName(); - - @BeforeEach - public void setUp() { - evaluator = new LevelEventEvaluator(); - evaluator.setLevel(TRACE); - } - - private boolean callEvaluate(Level level) { - ILoggingEvent event = new LoggingEvent(fqcn, logger, level, "a", null, null); - return evaluator.evaluate(event); - } - - @Test - void testLevel() { - for (Level evaluatorLevel : allLevels) { - evaluator.setLevel(evaluatorLevel); - for (Level level : allLevels) { - // assertFalse(callEvaluate(level)); //todo probably bug? Whole logic is inverted? - // True should be the correct one? - // assertTrue(callEvaluate(level)); - } - } - } -} \ No newline at end of file diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java deleted file mode 100644 index 2a2d794c..00000000 --- a/firmware_updater/updater/test/org/selfbus/updater/logging/MessageEvaluatorTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.selfbus.updater.logging; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.LoggerFactory; - -import static ch.qos.logback.classic.Level.*; -import static org.junit.jupiter.api.Assertions.*; - -class MessageEvaluatorTest { - - /** - * Tests the evaluate method in the MessageEvaluator to ensure it correctly evaluates - * logging events against its filter for log messages. - */ - private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; - private final static Logger logger = (Logger) LoggerFactory.getLogger(MessageEvaluatorTest.class); - MessageEvaluator evaluator = null; - final String fqcn = logger.getClass().getName(); - - @BeforeEach - public void setUp() { - evaluator = new MessageEvaluator(); - evaluator.setLevel(TRACE); - } - - private boolean callEvaluate(Level level, String message) { - ILoggingEvent event = new LoggingEvent(fqcn, logger, level, message, null, null); - return evaluator.evaluate(event); - } - - @Test - void testLevel() { - for (Level evaluatorLevel : allLevels) { - evaluator.setLevel(evaluatorLevel); - for (Level level : allLevels) { - assertTrue(callEvaluate(level, "")); - } - } - } - - @Test - void testFilteredMessagesList() { - evaluator.setLevel(INFO); - for (Level level : new Level[]{ERROR, WARN, INFO}) { - // Check some "random" log message is NOT filtered out - assertTrue(callEvaluate(level, "some log message " + level.toString())); - for (String filteredMessage : MessageEvaluator.FILTERED_MESSAGES) { - // Check matching log message is filtered out - assertFalse(callEvaluate(level, filteredMessage)); - // Check containing log message is filtered out - assertFalse(callEvaluate(level, filteredMessage + "foo")); // test - } - } - } -} \ No newline at end of file diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/MessageFilterTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/MessageFilterTest.java new file mode 100644 index 00000000..ad469ac2 --- /dev/null +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/MessageFilterTest.java @@ -0,0 +1,46 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.spi.FilterReply; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +import static ch.qos.logback.classic.Level.TRACE; +import static org.junit.jupiter.api.Assertions.*; + +class MessageFilterTest { + + /** + * Tests the evaluate method in the MessageFilter to ensure it correctly evaluates + * logging events against its filter for log messages. + */ + private final static Logger logger = (Logger) LoggerFactory.getLogger(MessageFilterTest.class); + MessageFilter messageFilter = null; + final String fqcn = logger.getClass().getName(); + + @BeforeEach + public void setUp() { + messageFilter = new MessageFilter(); + messageFilter.start(); + } + + private FilterReply callDecide(String message) { + ILoggingEvent event = new LoggingEvent(fqcn, logger, TRACE, message, null, null); + return messageFilter.decide(event); + } + + @Test + void testFilteredMessagesList() { + // Check some "random" log message is NOT filtered out + assertSame(FilterReply.NEUTRAL, callDecide("some log message ")); + for (String filteredMessage : MessageFilter.FILTERED_MESSAGES) { + // Check matching log message is filtered out + assertSame(FilterReply.DENY, callDecide(filteredMessage)); + // Check containing log message is filtered out + assertSame(FilterReply.DENY, callDecide(filteredMessage + "foo")); // test + } + } +} \ No newline at end of file diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java deleted file mode 100644 index ac944690..00000000 --- a/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableEvaluatorTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.selfbus.updater.logging; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.lang.reflect.Constructor; -import java.util.Map; - -import static ch.qos.logback.classic.Level.*; -import static org.junit.jupiter.api.Assertions.*; - -public class ThrowableEvaluatorTest { - - /** - * Tests the evaluate method in the ThrowableEvaluator to ensure it correctly evaluates - * logging events against its filter for exceptions and corresponding messages. - */ - private final static Level[] allLevels = new Level[]{TRACE, DEBUG, INFO, WARN, ERROR}; - private final static Logger logger = (Logger) LoggerFactory.getLogger(ThrowableEvaluatorTest.class); - ThrowableEvaluator evaluator = null; - final String fqcn = logger.getClass().getName(); - - @BeforeEach - public void setUp() { - evaluator = new ThrowableEvaluator(); - evaluator.setLevel(TRACE); - } - - private boolean callEvaluate(Level level, String message, Throwable throwable) { - ILoggingEvent event = new LoggingEvent(fqcn, logger, level, message, throwable, null); - return evaluator.evaluate(event); - } - - @Test - void testLevel() { - for (Level evaluatorLevel : allLevels) { - evaluator.setLevel(evaluatorLevel); - for (Level level : allLevels) { - assertTrue(callEvaluate(level, "", null)); - } - } - } - - @Test - void testThrowableIsNull() { - for (Level evaluatorLevel : allLevels) { - evaluator.setLevel(evaluatorLevel); - for (Level level : allLevels) { - assertTrue(callEvaluate(level, "testThrowableIsNull", null)); - } - } - } - - private Throwable createExceptionInstance(Class exceptionClass, String message) throws Exception { - // Use reflection to find a compatible constructor (one that accepts a String) - Constructor constructor = exceptionClass.getConstructor(String.class); - return constructor.newInstance(message); - } - - @Test - void testExceptionFilterMap() throws Exception { - evaluator.setLevel(INFO); - // Iterate over all exceptions - for (Map.Entry, List> entry : - ThrowableEvaluator.EXCEPTION_FILTER_MAP.entrySet()) { - Class exceptionClass = entry.getKey(); - - // Iterate over all log messages / exception messages - for (ThrowableEvaluator.ConditionalFilter filter : entry.getValue()) { - String logMessage = filter.logMessage(); - String exceptionMessage = filter.exceptionMessage(); - - // Check without exception is not filtered - for (Level level : allLevels) { - assertTrue(callEvaluate(level, logMessage, null)); - } - - Throwable exception = createExceptionInstance(exceptionClass, exceptionMessage); - for (Level level : new Level[]{ERROR, WARN, INFO}) { - // Check matching to EXCEPTION_FILTER_MAP is filtered out - assertFalse(callEvaluate(level, logMessage, exception)); - - // Check not matching log messages to EXCEPTION_FILTER_MAP is NOT filtered - assertTrue(callEvaluate(level, logMessage + "foo", exception)); - } - - exception = createExceptionInstance(exceptionClass, exceptionMessage + "foo"); - for (Level level : new Level[]{ERROR, WARN, INFO}) { - // Check not matching to EXCEPTION_FILTER_MAP is NOT filtered - assertTrue(callEvaluate(level, logMessage, exception)); - - // Check not matching log messages to EXCEPTION_FILTER_MAP is NOT filtered - assertTrue(callEvaluate(level, logMessage + "foo", exception)); - } - } - } - } -} \ No newline at end of file diff --git a/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableFilterTest.java b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableFilterTest.java new file mode 100644 index 00000000..69537c81 --- /dev/null +++ b/firmware_updater/updater/test/org/selfbus/updater/logging/ThrowableFilterTest.java @@ -0,0 +1,81 @@ +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.spi.FilterReply; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.lang.reflect.Constructor; +import java.util.Map; + +import static ch.qos.logback.classic.Level.*; +import static org.junit.jupiter.api.Assertions.assertSame; + +public class ThrowableFilterTest { + + /** + * Tests the evaluate method in the ThrowableFilter to ensure it correctly evaluates + * logging events against its filter for exceptions and corresponding messages. + */ + private final static Logger logger = (Logger) LoggerFactory.getLogger(ThrowableFilterTest.class); + ThrowableFilter throwableFilter = null; + final String fqcn = logger.getClass().getName(); + + @BeforeEach + public void setUp() { + throwableFilter = new ThrowableFilter(); + throwableFilter.start(); + } + + private FilterReply callDecide(String message, Throwable throwable) { + ILoggingEvent event = new LoggingEvent(fqcn, logger, TRACE, message, throwable, null); + return throwableFilter.decide(event); + } + + @Test + void testThrowableIsNull() { + assertSame(FilterReply.NEUTRAL, callDecide("testThrowableIsNull", null)); + } + + private Throwable createExceptionInstance(Class exceptionClass, String message) throws Exception { + // Use reflection to find a compatible constructor (one that accepts a String) + Constructor constructor = exceptionClass.getConstructor(String.class); + return constructor.newInstance(message); + } + + @Test + void testExceptionFilterMap() throws Exception { + // Iterate over all exceptions + for (Map.Entry, List> entry : + ThrowableFilter.EXCEPTION_FILTER_MAP.entrySet()) { + Class exceptionClass = entry.getKey(); + + // Iterate over all log messages / exception messages + for (ThrowableFilter.ConditionalFilter filter : entry.getValue()) { + String logMessage = filter.logMessage(); + String exceptionMessage = filter.exceptionMessage(); + + // Check without exception is not filtered + assertSame(FilterReply.NEUTRAL,callDecide(logMessage, null)); + + Throwable exception = createExceptionInstance(exceptionClass, exceptionMessage); + // Check matching to EXCEPTION_FILTER_MAP is filtered out + assertSame(FilterReply.DENY, callDecide(logMessage, exception)); + + // Check not matching log messages to EXCEPTION_FILTER_MAP is NOT filtered + assertSame(FilterReply.NEUTRAL, callDecide(logMessage + "foo", exception)); + + exception = createExceptionInstance(exceptionClass, exceptionMessage + "foo"); + // Check not matching to EXCEPTION_FILTER_MAP is NOT filtered + assertSame(FilterReply.NEUTRAL, callDecide(logMessage, exception)); + + // Check not matching log messages to EXCEPTION_FILTER_MAP is NOT filtered + assertSame(FilterReply.NEUTRAL, callDecide(logMessage + "foo", exception)); + } + } + } +} \ No newline at end of file From b438179c485bd97b1dc4c6001426df178d133f04 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 13:48:21 +0100 Subject: [PATCH 277/359] [Updater] Move `TextAppender` to `.logging` --- .../src/org/selfbus/updater/gui/GuiMain.java | 1 + .../updater/logging/ListTextAppenders.java | 1 - .../{gui => logging}/TextAppender.java | 189 +++++++++--------- .../updater/src/resources/logback.xml | 2 +- 4 files changed, 97 insertions(+), 96 deletions(-) rename firmware_updater/updater/src/org/selfbus/updater/{gui => logging}/TextAppender.java (94%) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index a3721322..d677ac57 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -6,6 +6,7 @@ import org.apache.commons.cli.ParseException; import org.selfbus.updater.*; import org.selfbus.updater.logging.ListTextAppenders; +import org.selfbus.updater.logging.TextAppender; import tuwien.auto.calimero.*; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java index 2bdc85d1..1184b617 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java @@ -6,7 +6,6 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.Appender; -import org.selfbus.updater.gui.TextAppender; import org.slf4j.LoggerFactory; import java.util.Iterator; diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java b/firmware_updater/updater/src/org/selfbus/updater/logging/TextAppender.java similarity index 94% rename from firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java rename to firmware_updater/updater/src/org/selfbus/updater/logging/TextAppender.java index fdccbfca..d14a6236 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/TextAppender.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/TextAppender.java @@ -1,95 +1,96 @@ -package org.selfbus.updater.gui; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.IThrowableProxy; -import ch.qos.logback.classic.spi.StackTraceElementProxy; -import ch.qos.logback.core.AppenderBase; - -import javax.swing.*; -import javax.swing.text.BadLocationException; - -import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_NO_NEWLINE; - -public class TextAppender extends AppenderBase { - private JTextPane textPane; - - @SuppressWarnings("unused") - public TextAppender() { - this.textPane = null; - } - - private void updateTextPane(String message) throws BadLocationException { - if (getTextPane() == null) { - return; - } - ConColorsToStyledDoc.Convert(message, getTextPane()); - } - - @Override - protected void append(ILoggingEvent event) { - if (getTextPane() == null) { - return; - } - - StringBuilder eventMessage = new StringBuilder(event.getFormattedMessage()); - - // Check for ThrowableProxy to retrieve the stack trace - IThrowableProxy throwableProxy = event.getThrowableProxy(); - if (throwableProxy != null) { - // Append the stack trace to the log message - eventMessage.append(System.lineSeparator()); - appendStackTrace(throwableProxy, eventMessage); - } - - String logMessage; - if ((event.getMarkerList() != null) && (event.getMarkerList().contains(CONSOLE_GUI_NO_NEWLINE))) { - logMessage = eventMessage.toString(); - } - else { - // Ensure same behavior like ConsoleAppender - // It gets a new line added to every logging message - // by it´s pattern %message%n (notice the n = new line) in logback.xml - logMessage = eventMessage.append(System.lineSeparator()).toString(); - } - - String finalLogMessage = logMessage; - SwingUtilities.invokeLater(() -> { - try { - // Append formatted message to text area using the Thread - updateTextPane(finalLogMessage); - } - catch (BadLocationException e) { - throw new RuntimeException(e); - } - }); - } - - private void appendStackTrace(IThrowableProxy throwableProxy, StringBuilder builder) { - builder.append(throwableProxy.getClassName()) - .append(": ") - .append(throwableProxy.getMessage()) - .append(System.lineSeparator()); - - // Loop through each stack trace element and append it - for (StackTraceElementProxy elementProxy : throwableProxy.getStackTraceElementProxyArray()) { - builder.append("\t ") - .append(elementProxy.toString()) - .append(System.lineSeparator()); - } - - // If there are any nested causes, recursively append them - IThrowableProxy cause = throwableProxy.getCause(); - if (cause != null) { - builder.append("Caused by: "); - appendStackTrace(cause, builder); - } - } - - public JTextPane getTextPane() { - return textPane; - } - - public void setTextPane(JTextPane textPane) { - this.textPane = textPane; - } +package org.selfbus.updater.logging; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import ch.qos.logback.core.AppenderBase; +import org.selfbus.updater.gui.ConColorsToStyledDoc; + +import javax.swing.*; +import javax.swing.text.BadLocationException; + +import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_NO_NEWLINE; + +public class TextAppender extends AppenderBase { + private JTextPane textPane; + + @SuppressWarnings("unused") + public TextAppender() { + this.textPane = null; + } + + private void updateTextPane(String message) throws BadLocationException { + if (getTextPane() == null) { + return; + } + ConColorsToStyledDoc.Convert(message, getTextPane()); + } + + @Override + protected void append(ILoggingEvent event) { + if (getTextPane() == null) { + return; + } + + StringBuilder eventMessage = new StringBuilder(event.getFormattedMessage()); + + // Check for ThrowableProxy to retrieve the stack trace + IThrowableProxy throwableProxy = event.getThrowableProxy(); + if (throwableProxy != null) { + // Append the stack trace to the log message + eventMessage.append(System.lineSeparator()); + appendStackTrace(throwableProxy, eventMessage); + } + + String logMessage; + if ((event.getMarkerList() != null) && (event.getMarkerList().contains(CONSOLE_GUI_NO_NEWLINE))) { + logMessage = eventMessage.toString(); + } + else { + // Ensure same behavior like ConsoleAppender + // It gets a new line added to every logging message + // by it´s pattern %message%n (notice the n = new line) in logback.xml + logMessage = eventMessage.append(System.lineSeparator()).toString(); + } + + String finalLogMessage = logMessage; + SwingUtilities.invokeLater(() -> { + try { + // Append formatted message to text area using the Thread + updateTextPane(finalLogMessage); + } + catch (BadLocationException e) { + throw new RuntimeException(e); + } + }); + } + + private void appendStackTrace(IThrowableProxy throwableProxy, StringBuilder builder) { + builder.append(throwableProxy.getClassName()) + .append(": ") + .append(throwableProxy.getMessage()) + .append(System.lineSeparator()); + + // Loop through each stack trace element and append it + for (StackTraceElementProxy elementProxy : throwableProxy.getStackTraceElementProxyArray()) { + builder.append("\t ") + .append(elementProxy.toString()) + .append(System.lineSeparator()); + } + + // If there are any nested causes, recursively append them + IThrowableProxy cause = throwableProxy.getCause(); + if (cause != null) { + builder.append("Caused by: "); + appendStackTrace(cause, builder); + } + } + + public JTextPane getTextPane() { + return textPane; + } + + public void setTextPane(JTextPane textPane) { + this.textPane = textPane; + } } \ No newline at end of file diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 866e66d0..157ae585 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -23,7 +23,7 @@ - + INFO From c58d687fd63398a1a05fc7d1160edb78e40a9e61 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 13:51:46 +0100 Subject: [PATCH 278/359] [Updater] Rename `TextAppender` to `JTextPaneAppender` --- .../updater/src/org/selfbus/updater/gui/GuiMain.java | 8 ++++---- .../{TextAppender.java => JTextPaneAppender.java} | 4 ++-- .../org/selfbus/updater/logging/ListTextAppenders.java | 10 +++++----- firmware_updater/updater/src/resources/logback.xml | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) rename firmware_updater/updater/src/org/selfbus/updater/logging/{TextAppender.java => JTextPaneAppender.java} (94%) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index d677ac57..9c7dd0d3 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -6,7 +6,7 @@ import org.apache.commons.cli.ParseException; import org.selfbus.updater.*; import org.selfbus.updater.logging.ListTextAppenders; -import org.selfbus.updater.logging.TextAppender; +import org.selfbus.updater.logging.JTextPaneAppender; import tuwien.auto.calimero.*; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; @@ -400,9 +400,9 @@ private void startUpdaterGui() { jLoggingPane.setBackground(DefaultBackgroundColor); jLoggingPane.setForeground(DefaultForegroundColor); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - TextAppender textAppender = ListTextAppenders.searchAppender(); - if (textAppender != null) { - textAppender.setTextPane(this.jLoggingPane); + JTextPaneAppender textPaneAppender = ListTextAppenders.searchAppender(); + if (textPaneAppender != null) { + textPaneAppender.setTextPane(this.jLoggingPane); } initGuiElementsVisibility(); fillScenarios(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/TextAppender.java b/firmware_updater/updater/src/org/selfbus/updater/logging/JTextPaneAppender.java similarity index 94% rename from firmware_updater/updater/src/org/selfbus/updater/logging/TextAppender.java rename to firmware_updater/updater/src/org/selfbus/updater/logging/JTextPaneAppender.java index d14a6236..70e25d1f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/TextAppender.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/JTextPaneAppender.java @@ -11,11 +11,11 @@ import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_NO_NEWLINE; -public class TextAppender extends AppenderBase { +public class JTextPaneAppender extends AppenderBase { private JTextPane textPane; @SuppressWarnings("unused") - public TextAppender() { + public JTextPaneAppender() { this.textPane = null; } diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java index 1184b617..4c99f667 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java @@ -14,10 +14,10 @@ public final class ListTextAppenders { @SuppressWarnings("unused") private ListTextAppenders() {} // avoids instance creation - private static TextAppender jTextPaneAppender = null; + private static JTextPaneAppender jTextPaneAppender = null; private static Logger logger = null; - public static TextAppender searchAppender() { + public static JTextPaneAppender searchAppender() { // Get the LoggerContext from the LoggerFactory LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); @@ -28,9 +28,9 @@ public static TextAppender searchAppender() { while (appenderIterator.hasNext()) { Appender appender = appenderIterator.next(); - if (appender instanceof TextAppender) { + if (appender instanceof JTextPaneAppender) { logger = iterLogger; - jTextPaneAppender = (TextAppender) appender; + jTextPaneAppender = (JTextPaneAppender) appender; return jTextPaneAppender; } } @@ -39,7 +39,7 @@ public static TextAppender searchAppender() { return jTextPaneAppender; } - public static TextAppender getJTextPaneAppender() { + public static JTextPaneAppender getJTextPaneAppender() { if (jTextPaneAppender != null) { return jTextPaneAppender; } diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 157ae585..de88da16 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -23,7 +23,7 @@ - + INFO From da6970ae1c41a0538ded9706c165fb43b457e237 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 13:54:50 +0100 Subject: [PATCH 279/359] [Updater] Rename appender `list` to `JTextPane` --- firmware_updater/updater/src/resources/logback.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index de88da16..0e069484 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -23,7 +23,7 @@ - + INFO @@ -90,7 +90,7 @@ - + From 99c7fef8790ea89102e0ec9e12051eec214d6bda Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 14:35:24 +0100 Subject: [PATCH 280/359] [Updater] Delete ignored `` Only the last `` is processed by logback-classic. --- firmware_updater/updater/src/resources/logback.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 0e069484..5c77330d 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -55,7 +55,6 @@ ${application.home:-.}/log/updater-${appStartTimestamp}.html - %thread%level%logger%msg %d{HH:mm:ss.SSS}%thread%level%logger%method%line%message @@ -75,7 +74,6 @@ - %thread%level%logger%msg %d{HH:mm:ss.SSS}%thread%level%logger%method%line%message From 26706933480f715a829214c2c75c57964be67c5e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 15:38:42 +0100 Subject: [PATCH 281/359] [Updater] No more ansi escape codes in file logs --- firmware_updater/updater/src/resources/logback.xml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 5c77330d..f1b51a6e 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -4,6 +4,11 @@ + + + + + From 0ac411ab0d7306d5c25e24edcbfd1f2630035142 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 15:59:31 +0100 Subject: [PATCH 283/359] [Updater] Reduce redundant info logs --- firmware_updater/updater/src/org/selfbus/updater/Updater.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 36761e36..02297eff 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -315,13 +315,13 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres if (!cliOptions.getNoFlashIsSet()) { // is flashing firmware disabled? for debugging use only! // Start to flash the new firmware ResponseResult resultTotal; - logger.info("{}Starting to send new firmware now:{}", ansi().bg(GREEN).fg(BLACK), ansi().reset()); if (diffMode && FlashDiffMode.isInitialized()) { logger.warn("{}Differential mode is EXPERIMENTAL -> Use with caution.{}", ansi().fgBright(RED), ansi().reset()); resultTotal = FlashDiffMode.doDifferentialFlash(dm, newFirmware.startAddress(), newFirmware.getBinData()); } else { + logger.debug("Starting FlashFullMode"); resultTotal = FlashFullMode.doFullFlash(dm, newFirmware, cliOptions.getDelayMs(), !cliOptions.getEraseFullFlashIsSet(), cliOptions.getLogStatisticsIsSet()); } BootloaderStatistic bootloaderStatistic = dm.requestBootLoaderStatistic(); @@ -350,7 +350,6 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres logger.info("Finished programming device {}{}{} with '{}{}{}'", ansi().fgBright(YELLOW), deviceInfo, ansi().reset(), ansi().fgBright(YELLOW), shortenPath(cliOptions.getFileName(), 1), ansi().reset()); - logger.info("{}Firmware Update done, Restarting device{}", ansi().bg(GREEN).fg(BLACK), ansi().reset()); dm.restartProgrammingDevice(); dm.close(); From ac035c37b3da4eb6ca9b725d589ea2bd39e47e8c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 16:31:27 +0100 Subject: [PATCH 284/359] [Updater] Shorter log of BL and sblib version --- .../src/org/selfbus/updater/bootloader/BootloaderIdentity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java index e1a969de..801510d4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java @@ -48,7 +48,7 @@ public static BootloaderIdentity fromArray(byte[] parse) { } public String toString() { - return String.format("Version: %s, sbLib Version: %s, Features: 0x%04X, App-start: 0x%04X", + return String.format("v%s, sbLib v%s, Features: 0x%04X, App-start: 0x%04X", getVersion(), getVersionSBLib(), features(), applicationFirstAddress()); } From a21fd2a716d093b724b1580b9e1ad3b851c7f58a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 16:33:06 +0100 Subject: [PATCH 285/359] [Updater] Use `logFile` for all `FileAppenders` --- firmware_updater/updater/src/resources/logback.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 6c80a39a..83aea393 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -1,8 +1,10 @@ - + + + @@ -40,7 +42,7 @@ - ${application.home:-.}/log/updater-${appStartTimestamp}.log + ${logFile}.log false %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level [%logger] %method \(%line\) %${messageNoAnsi}%n @@ -58,7 +60,7 @@ - ${application.home:-.}/log/updater-${appStartTimestamp}.html + ${logFile}.html ${htmlPattern} @@ -74,7 +76,7 @@ - - - - - - - - INFO - - - %message%n - - - - - - - - From 2d8862addbc29951e7c06ea1bc6d6fcc45fac583 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 18:14:49 +0100 Subject: [PATCH 287/359] [Updater] Reorganize `logback.xml` into multiplexe `.xml` files --- .../updater/logging/LoggingManager.java | 2 +- .../updater/src/resources/logback.xml | 104 +----------------- .../src/resources/logback/fileAppenders.xml | 68 ++++++++++++ .../updater/src/resources/logback/noAnsi.xml | 5 + .../src/resources/logback/uiAppenders.xml | 36 ++++++ .../src/resources/logback/vendorLoggers.xml | 7 ++ 6 files changed, 123 insertions(+), 99 deletions(-) create mode 100644 firmware_updater/updater/src/resources/logback/fileAppenders.xml create mode 100644 firmware_updater/updater/src/resources/logback/noAnsi.xml create mode 100644 firmware_updater/updater/src/resources/logback/uiAppenders.xml create mode 100644 firmware_updater/updater/src/resources/logback/vendorLoggers.xml diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java index 9efe9162..df477562 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java @@ -18,7 +18,7 @@ public final class LoggingManager { // In case CONSOLE_APPENDER_NAME is changed, - // change also CONSOLE appender name in src/resources/logback.xml + // change also CONSOLE appender name in src/resources/logback/uiAppenders.xml public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; @SuppressWarnings("unused") diff --git a/firmware_updater/updater/src/resources/logback.xml b/firmware_updater/updater/src/resources/logback.xml index 83aea393..11a1bc2f 100644 --- a/firmware_updater/updater/src/resources/logback.xml +++ b/firmware_updater/updater/src/resources/logback.xml @@ -1,109 +1,17 @@ - + + - - - - + + + - - - - - - - - - true - - INFO - - - - - - - - %message%n - - - - - - INFO - - - - - - - - - ${logFile}.log - false - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level [%logger] %method \(%line\) %${messageNoAnsi}%n - - - - CONSOLE_GUI_ONLY - - - CONSOLE_GUI_NO_NEWLINE - - - - - ${logFile}.html - - - ${htmlPattern} - - - - CONSOLE_GUI_ONLY - - - CONSOLE_GUI_NO_NEWLINE - - - - - + - - - - - diff --git a/firmware_updater/updater/src/resources/logback/fileAppenders.xml b/firmware_updater/updater/src/resources/logback/fileAppenders.xml new file mode 100644 index 00000000..39efbf71 --- /dev/null +++ b/firmware_updater/updater/src/resources/logback/fileAppenders.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + ${logFile}.log + false + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level [%logger] %method \(%line\) %${messageNoAnsi}%n + + + + CONSOLE_GUI_ONLY + + + CONSOLE_GUI_NO_NEWLINE + + + + + ${logFile}.html + + + ${htmlPattern} + + + + CONSOLE_GUI_ONLY + + + CONSOLE_GUI_NO_NEWLINE + + + + + ${logFile}-warn.html + + WARN + + + + ${htmlPattern} + + + + CONSOLE_GUI_ONLY + + + CONSOLE_GUI_NO_NEWLINE + + + \ No newline at end of file diff --git a/firmware_updater/updater/src/resources/logback/noAnsi.xml b/firmware_updater/updater/src/resources/logback/noAnsi.xml new file mode 100644 index 00000000..c462635a --- /dev/null +++ b/firmware_updater/updater/src/resources/logback/noAnsi.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/firmware_updater/updater/src/resources/logback/uiAppenders.xml b/firmware_updater/updater/src/resources/logback/uiAppenders.xml new file mode 100644 index 00000000..20b2126c --- /dev/null +++ b/firmware_updater/updater/src/resources/logback/uiAppenders.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + true + + INFO + + + + + + + %message%n + + + + + + INFO + + + + + + \ No newline at end of file diff --git a/firmware_updater/updater/src/resources/logback/vendorLoggers.xml b/firmware_updater/updater/src/resources/logback/vendorLoggers.xml new file mode 100644 index 00000000..fa488c2a --- /dev/null +++ b/firmware_updater/updater/src/resources/logback/vendorLoggers.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file From b9169bfde4ebfbabe9a325a80054250a6c2579f3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 21:05:35 +0100 Subject: [PATCH 288/359] [Updater] Make `searchAppender()` universal and move it to `LoggingManager` --- .../src/org/selfbus/updater/gui/GuiMain.java | 5 +- .../updater/logging/ListTextAppenders.java | 65 ---------------- .../updater/logging/LoggingManager.java | 74 +++++++++---------- .../src/resources/logback/uiAppenders.xml | 4 + 4 files changed, 42 insertions(+), 106 deletions(-) delete mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 9c7dd0d3..24c14e6a 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -5,8 +5,8 @@ import com.intellij.uiDesigner.core.Spacer; import org.apache.commons.cli.ParseException; import org.selfbus.updater.*; -import org.selfbus.updater.logging.ListTextAppenders; import org.selfbus.updater.logging.JTextPaneAppender; +import org.selfbus.updater.logging.LoggingManager; import tuwien.auto.calimero.*; import tuwien.auto.calimero.knxnetip.Discoverer; import tuwien.auto.calimero.knxnetip.servicetype.SearchResponse; @@ -39,6 +39,7 @@ import static org.selfbus.updater.gui.CliConverter.argument; import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultBackgroundColor; import static org.selfbus.updater.gui.ConColorsToStyledDoc.DefaultForegroundColor; +import static org.selfbus.updater.logging.LoggingManager.JTEXTPANE_APPENDER_NAME; public class GuiMain extends JFrame { private JButton buttonLoadFile; @@ -400,7 +401,7 @@ private void startUpdaterGui() { jLoggingPane.setBackground(DefaultBackgroundColor); jLoggingPane.setForeground(DefaultForegroundColor); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - JTextPaneAppender textPaneAppender = ListTextAppenders.searchAppender(); + JTextPaneAppender textPaneAppender = (JTextPaneAppender) LoggingManager.getAppender(JTEXTPANE_APPENDER_NAME); if (textPaneAppender != null) { textPaneAppender.setTextPane(this.jLoggingPane); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java b/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java deleted file mode 100644 index 4c99f667..00000000 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/ListTextAppenders.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.selfbus.updater.logging; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.Appender; -import org.slf4j.LoggerFactory; - -import java.util.Iterator; - -public final class ListTextAppenders { - @SuppressWarnings("unused") - private ListTextAppenders() {} // avoids instance creation - - private static JTextPaneAppender jTextPaneAppender = null; - private static Logger logger = null; - - public static JTextPaneAppender searchAppender() { - // Get the LoggerContext from the LoggerFactory - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - - // Iterate over all loggers in the context - for (Logger iterLogger : loggerContext.getLoggerList()) { - // Get all appenders for the iterLogger - Iterator> appenderIterator = iterLogger.iteratorForAppenders(); - - while (appenderIterator.hasNext()) { - Appender appender = appenderIterator.next(); - if (appender instanceof JTextPaneAppender) { - logger = iterLogger; - jTextPaneAppender = (JTextPaneAppender) appender; - return jTextPaneAppender; - } - } - } - jTextPaneAppender = null; - return jTextPaneAppender; - } - - public static JTextPaneAppender getJTextPaneAppender() { - if (jTextPaneAppender != null) { - return jTextPaneAppender; - } - else { - return searchAppender(); - } - } - - public static void appendEvent(Level level, String logMessage) { - if (getJTextPaneAppender() == null) { - return; - } - - if (logger == null) { - return; - } - - ILoggingEvent loggingEvent = new LoggingEvent(logger.getName(), logger, - level, logMessage, null, null); - getJTextPaneAppender().doAppend(loggingEvent); - } -} - diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java index df477562..f4d920da 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java @@ -4,27 +4,46 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.filter.ThresholdFilter; import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.Appender; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.spi.FilterReply; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.management.ManagementFactory; public final class LoggingManager { + /** + * The name of the console appender used for logging. + * This constant is referenced in the logging configuration defined in + * {@code src/resources/logback/uiAppenders.xml}. + *

+ * Note: If the value of this constant is changed, ensure to update the + * corresponding appender name in the `logback/uiAppenders.xml` file. + */ + public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; + + /** + * The name of the appender used for logging messages to a {@code JTextPane}. + * This constant is referenced in the logging configuration defined in + * {@code src/resources/logback/uiAppenders.xml}. + *

+ * Note: If the value of this constant is changed, ensure to update the + * corresponding appender name in the `logback/uiAppenders.xml` file. + * */ + public static final String JTEXTPANE_APPENDER_NAME = "JTextPane"; + private final static Logger logger = LoggerFactory.getLogger(LoggingManager.class); + private static LoggerContext getLoggerContext() { + return (LoggerContext) LoggerFactory.getILoggerFactory(); + } - // In case CONSOLE_APPENDER_NAME is changed, - // change also CONSOLE appender name in src/resources/logback/uiAppenders.xml - public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; + public static Appender getAppender(String appenderName) { + return getLoggerContext().getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); + } @SuppressWarnings("unused") public static void disableAppender(String appenderName) { - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); + Appender appender = getAppender(appenderName); if (appender == null) { logger.warn("disableAppender: appender {} not found.", appenderName); @@ -33,16 +52,13 @@ public static void disableAppender(String appenderName) { logger.trace("Stopping and disabling {} appender.", appenderName); appender.stop(); - loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(appenderName); + getLoggerContext().getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(appenderName); logger.trace("{} appender has been disabled.", appenderName); } - public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); - + public static void setAppenderLogLevel(Appender appender, Level newLogLevel) { if (appender == null) { - logger.warn("appender {} not found.", appenderName); + logger.warn("appender is null"); return; } @@ -52,36 +68,16 @@ public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { ThresholdFilter thresholdFilter = new ThresholdFilter(); thresholdFilter.setLevel(newLogLevel.toString()); - thresholdFilter.setContext(loggerContext); + thresholdFilter.setContext(getLoggerContext()); thresholdFilter.start(); appender.addFilter(thresholdFilter); - logger.trace("{} appender log level set to: {}", appenderName, newLogLevel); + logger.trace("{} appender log level set to: {}", appender.getName(), newLogLevel); } - public static Level getAppenderLogLevel(String appenderName) { - LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - Appender appender = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName.toUpperCase()); - - if (appender == null) { - return null; - } - - final Level[] testLevels = {Level.INFO, Level.DEBUG, Level.WARN, Level.ERROR, Level.TRACE}; - - for (Filter filter : appender.getCopyOfAttachedFiltersList()) { - if (filter instanceof ThresholdFilter thresholdFilter) { - // Test with different levels to infer the threshold - for (Level testLevel : testLevels) { - LoggingEvent testEvent = new LoggingEvent(); - testEvent.setLevel(testLevel); - if (thresholdFilter.decide(testEvent) != FilterReply.DENY) { - return testLevel; - } - } - } - } - return null; + public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { + Appender appender = getAppender(appenderName); + setAppenderLogLevel(appender, newLogLevel); } public static boolean isRunningInIntelliJ() { diff --git a/firmware_updater/updater/src/resources/logback/uiAppenders.xml b/firmware_updater/updater/src/resources/logback/uiAppenders.xml index 20b2126c..49996839 100644 --- a/firmware_updater/updater/src/resources/logback/uiAppenders.xml +++ b/firmware_updater/updater/src/resources/logback/uiAppenders.xml @@ -25,6 +25,10 @@ + INFO From e0553f536c027f0ecde1572ccb1d7b8c0e3178db Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 21:40:47 +0100 Subject: [PATCH 289/359] [Updater] Fix Set new log level without deleting all other filters --- .../src/org/selfbus/updater/Updater.java | 2 +- .../updater/logging/LoggingManager.java | 41 +++++-------------- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 02297eff..c21fbfaf 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -71,7 +71,7 @@ public static void main(final String[] args) { if (args.length == 0) { if (!LoggingManager.isRunningInIntelliJ()) { logger.info("GUI start => console output set to log level {}", Level.WARN); - LoggingManager.setAppenderLogLevel(CONSOLE_APPENDER_NAME, Level.WARN); + LoggingManager.setThresholdFilterLogLevel(CONSOLE_APPENDER_NAME, Level.WARN); } GuiMain.startSwingGui(); finalizeJansi(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java index f4d920da..dc2bad04 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/LoggingManager.java @@ -5,6 +5,7 @@ import ch.qos.logback.classic.filter.ThresholdFilter; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; +import ch.qos.logback.core.spi.ContextAwareBase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,43 +42,21 @@ public static Appender getAppender(String appenderName) { return getLoggerContext().getLogger(Logger.ROOT_LOGGER_NAME).getAppender(appenderName); } - @SuppressWarnings("unused") - public static void disableAppender(String appenderName) { + public static void setThresholdFilterLogLevel(String appenderName, Level newLogLevel) { Appender appender = getAppender(appenderName); - if (appender == null) { - logger.warn("disableAppender: appender {} not found.", appenderName); + logger.warn("Appender {} not found", appenderName); return; } - logger.trace("Stopping and disabling {} appender.", appenderName); - appender.stop(); - getLoggerContext().getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(appenderName); - logger.trace("{} appender has been disabled.", appenderName); - } - - public static void setAppenderLogLevel(Appender appender, Level newLogLevel) { - if (appender == null) { - logger.warn("appender is null"); - return; + for (ContextAwareBase filter : appender.getCopyOfAttachedFiltersList()) { + if (filter instanceof ThresholdFilter thresholdFilter) { + logger.debug("Appender {} ThresholdFilter log level set to: {}", appender.getName(), newLogLevel); + thresholdFilter.setLevel(newLogLevel.toString()); + return; + } } - - //todo this might be quite problematic, we loose all other possible defined filters here - appender.clearAllFilters(); - logger.info("Deleted all filters of {}", appender.getName()); - - ThresholdFilter thresholdFilter = new ThresholdFilter(); - thresholdFilter.setLevel(newLogLevel.toString()); - thresholdFilter.setContext(getLoggerContext()); - thresholdFilter.start(); - - appender.addFilter(thresholdFilter); - logger.trace("{} appender log level set to: {}", appender.getName(), newLogLevel); - } - - public static void setAppenderLogLevel(String appenderName, Level newLogLevel) { - Appender appender = getAppender(appenderName); - setAppenderLogLevel(appender, newLogLevel); + logger.warn("Appender {} has no {}", appenderName, ThresholdFilter.class.getSimpleName()); } public static boolean isRunningInIntelliJ() { From 3d3d53d932f340fe7e60e522697c904bf82e9231 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 21:42:57 +0100 Subject: [PATCH 290/359] [Updater] Fix comment out not used `htmlWarnAppender` --- .../updater/src/resources/logback/fileAppenders.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/resources/logback/fileAppenders.xml b/firmware_updater/updater/src/resources/logback/fileAppenders.xml index 39efbf71..80dda59d 100644 --- a/firmware_updater/updater/src/resources/logback/fileAppenders.xml +++ b/firmware_updater/updater/src/resources/logback/fileAppenders.xml @@ -47,7 +47,7 @@ CONSOLE_GUI_NO_NEWLINE - + \ No newline at end of file From 815192201fc2c3de33ffaeb2b881aaf613578949 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 23:12:32 +0100 Subject: [PATCH 291/359] [Updater] Prettier logging? --- .../org/selfbus/updater/devicemgnt/DeviceManagement.java | 4 ++-- .../updater/src/org/selfbus/updater/upd/UDPResult.java | 6 +++++- .../updater/src/org/selfbus/updater/upd/UPDCommand.java | 2 +- .../updater/src/org/selfbus/updater/upd/UPDProtocol.java | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 9ab7f5ef..351deb42 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -426,8 +426,8 @@ private void handleKNXException(final UPDCommand command, final KNXException e, logger.warn("{}Unexpected: never seen before {}{}", ansi().fg(RED), e.getClass().getSimpleName(),ansi().reset()); } - logger.warn("{}{} {} : {}{}", ansi().fg(RED), command, e.getMessage(), - e.getClass().getSimpleName(), ansi().reset()); + logger.warn("{}{}{} ({} {})", ansi().fg(RED), e.getMessage(), ansi().reset(), + e.getClass().getSimpleName(), command); if (!reconnect) { return; diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java index 00acd11d..de9f6316 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java @@ -119,7 +119,7 @@ public static UDPResult valueOf(byte index) { @Override public String toString() { - return String.format("0x%02X %s", this.id, this.description); + return String.format("%s (%s)", this.description, super.toString()); } /** @@ -130,5 +130,9 @@ public boolean isError() { return this.isError; } + public String getDescription() { + return description; + } + } diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java index a4eb2d70..a9192974 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDCommand.java @@ -177,6 +177,6 @@ public static UPDCommand fromByteArray(byte[] bytes) throws UPDProtocolException @Override public String toString() { - return String.format("0x%02X %s %s", this.id, this.getClass().getSimpleName(), this.description); + return String.format("%s", this.description); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java index 05061fbf..073cce2c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java @@ -40,10 +40,10 @@ public static UDPResult checkResult(byte[] result, boolean verbose) { UDPResult udpResult = UDPResult.valueOf(result[DATA_POSITION]); if (udpResult.isError()) { - logger.error("{}UDPResult: {}{}", ansi().fgBright(RED), udpResult, ansi().reset()); + logger.error("{}{}{} ({})", ansi().fgBright(RED), udpResult.getDescription(), ansi().reset(), udpResult.name()); } else { if (verbose) { - logger.trace("{}done ({}){}", ansi().fgBright(GREEN), udpResult, ansi().reset()); + logger.trace("done ({})", udpResult.name()); } } return udpResult; From c8881ce2fa8b53a55a1cfb30b83d843d3d653af4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 14 Feb 2025 23:13:51 +0100 Subject: [PATCH 292/359] [Updater] No ansi in trace log --- .../src/org/selfbus/updater/mode/differential/FlashDiff.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java index 2e736e87..00eed5d4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java @@ -248,7 +248,7 @@ public void generateDiff(BinImage img1Orig, BinImage img2, FlashProgrammer flash logger.trace("Page {}, ", pages); flashProgrammer.sendCompressedPage(outputDiffStream, crc32Block.getValue()); totalBytesTransferred = size; - logger.trace("OK! Total diff stream length={}{}{} bytes", ansi().fgBright(GREEN), size, ansi().reset()); + logger.trace("OK! Total diff stream length={} bytes", size); } //dumpSideBySide(img1, img2); } From 6eb77184ca239610bea93945e76350992ed89a8b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 01:06:25 +0100 Subject: [PATCH 293/359] [Updater] Always reconnect in `handleKNXException` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved `KNXAckTimeoutException` logging to `SBManagementClientImpl`, but I´m pretty sure we will never catch it, maybe sometimes as cause? `ManagementClientImpl.sendWait` doesn´t throw a `KNXRemoteException` so we will never see it in `sendWithRetry`. --- .../updater/SBManagementClientImpl.java | 24 ++++++++++++++++++- .../updater/devicemgnt/DeviceManagement.java | 20 ++++------------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index b18cb740..cac50fd7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -182,9 +182,31 @@ public byte[] sendUpdateData(final Destination dst, final int cmd, final byte[] throw new UpdaterException(String.format("asdu.length (%d) exceeds MAX_ASDU_LENGTH (%d)", asdu.length, MAX_ASDU_LENGTH)); } + send = DataUnitBuilder.createAPDU(apciWrite, asdu); - return this.sendWait(dst, getPriority(), send, apciResponse, 2, MAX_ASDU_LENGTH, responseTimeout()); + byte[] response; + try { + response = this.sendWait(dst, getPriority(), send, apciResponse, 2, MAX_ASDU_LENGTH, responseTimeout()); + } + // We "try catch" here to catch at least once a KNXAckTimeoutException. + // It´s thrown on missing/faulty ack at linklayer level, but we never have seen it. + // Check public void send(final CEMI frame, final BlockingMode mode) in ConnectionBase.java + catch (KNXAckTimeoutException e) { + logger.error("Unexpected: never seen before {}", e); + // Delete logger.error, if you don´t get in line above IDEA warning "Fewer arguments provided (0) than placeholders specified (1)" + logger.error("", e); + throw e; + } + catch (KNXException e) { + if (e.getCause() instanceof KNXAckTimeoutException) { + logger.error("Unexpected: never seen before e.getCause() {}", e.getCause()); + // Delete logger.error, if you don´t get in line above IDEA warning "Fewer arguments provided (0) than placeholders specified (1)" + logger.error("", e.getCause()); + } + throw e; + } + return response; } @Override diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 351deb42..2264ba49 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -418,21 +418,10 @@ private boolean isLinkAlive() { return link.isOpen(); } - private void handleKNXException(final UPDCommand command, final KNXException e, final boolean reconnect) throws + private void handleKNXException(final UPDCommand command, final KNXException e) throws UpdaterException, InterruptedException { - if (e instanceof KNXAckTimeoutException) { - // This exception is on missing/faulty ack at linklayer level thrown, but we never see it here - // todo check public void send(final CEMI frame, final BlockingMode mode) in ConnectionBase - logger.warn("{}Unexpected: never seen before {}{}", ansi().fg(RED), e.getClass().getSimpleName(),ansi().reset()); - } - logger.warn("{}{}{} ({} {})", ansi().fg(RED), e.getMessage(), ansi().reset(), e.getClass().getSimpleName(), command); - - if (!reconnect) { - return; - } - try { reconnect(cliOptions.getReconnectMs()); } @@ -451,21 +440,20 @@ public ResponseResult sendWithRetry(final UPDCommand command, final byte[] data, result.copyFromArray(data2); return result; } - ///\todo check causes of KNXRemoteException, i think they are unrecoverable catch (KNXDisconnectException | KNXLinkClosedException e) { lastCaughtException = e; result.incDropCount(); - handleKNXException(command, e, true); + handleKNXException(command, e); } catch (KNXTimeoutException e) { // Can happen on a L2 tunnel request ACK timeout or a TL4 ACK timeout lastCaughtException = e; result.incTimeoutCount(); - handleKNXException(command, e, true); + handleKNXException(command, e); } catch (KNXInvalidResponseException e) { lastCaughtException = e; - handleKNXException(command, e, true); + handleKNXException(command, e); } finally { if (!isLinkAlive()) { From 8d0f04d08f796719d012cfda89918c00f87887dc Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 02:55:55 +0100 Subject: [PATCH 294/359] =?UTF-8?q?[Updater]=20Don=C2=B4t=20change=20`wait?= =?UTF-8?q?Ms`=20of=20caller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../selfbus/updater/devicemgnt/DeviceManagement.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 2264ba49..b23e1344 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -77,13 +77,15 @@ public DeviceManagement(CliOptions cliOptions) { this.cliOptions = cliOptions; } - public void reconnect(int waitMs) throws KNXException, UpdaterException, UnknownHostException, InterruptedException { + public void reconnect(final int waitMs) throws KNXException, UpdaterException, UnknownHostException, InterruptedException { close(); - if (waitMs <= 0) { - waitMs = 100; + if (waitMs > 0) { + logger.info("Reconnecting in {}ms", waitMs); + Thread.sleep(waitMs); + } + else { + logger.info("Reconnecting now"); } - logger.info("Reconnecting in {} ms", waitMs); - Thread.sleep(waitMs); open(); } From 2873c35515bb2b1121c4a7a39816653571d854f1 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 03:21:19 +0100 Subject: [PATCH 295/359] =?UTF-8?q?[Updater]=20Private=20constructor,=20bu?= =?UTF-8?q?ilder=20and=20don=C2=B4t=20throw=20an=20exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/org/selfbus/updater/gui/CliConverter.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java b/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java index 6763010f..e8723562 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/CliConverter.java @@ -6,26 +6,26 @@ public final class CliConverter { @SuppressWarnings("unused") - CliConverter() {} + private CliConverter() {} public static String argument(String cliLongOption, JTextField textField) { - return argument(cliLongOption, textField.getText(), true); + return buildArgument(cliLongOption, textField.getText(), true); } public static String argument(String cliLongOption, JComboBox comboBox) { - return argument(cliLongOption, Objects.requireNonNull(comboBox.getSelectedItem()).toString(), true); + return buildArgument(cliLongOption, Objects.requireNonNull(comboBox.getSelectedItem()).toString(), true); } public static String argument(String cliLongOption) { - return argument(cliLongOption, "", false); + return buildArgument(cliLongOption, "", false); } - public static String argument(String cliLongOption, String argValue, boolean argValueRequired) { + private static String buildArgument(String cliLongOption, String argValue, boolean argValueRequired) { cliLongOption = cliLongOption.trim(); argValue = argValue.trim(); - if ((argValueRequired) && (argValue.isEmpty())) { - return ""; //todo maybe throw an exception in this case? + if (argValueRequired && argValue.isEmpty()) { + return ""; // Discard the argument if argValue is required, but not provided } if (argValue.isEmpty()) { From 8cae268758a5b836cb7c34d9c6e425d3efb5633f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 03:42:46 +0100 Subject: [PATCH 296/359] [Updater] In theory, these cursor movements should work. --- .../updater/gui/ConColorsToStyledDoc.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index c184ca6c..fbaa8ea7 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -146,34 +146,44 @@ private static boolean processAnsiCursor(String ansiCode, JTextPane textPane) th } switch (cursorMovementCode) { - //todo implement remaining cursor movements case 65: // A Move up by n rows + moveCursor.y -= number; //todo cursor movement not tested + break; + case 66: // B Move down by n rows + moveCursor.y += number; //todo cursor movement not tested + break; + case 67: // C Move right by n columns + moveCursor.x += number; //todo cursor movement not tested + break; + case 68: // D Move left by n columns + moveCursor.x -= number; //todo cursor movement not tested + break; + case 72: // H Move cursor to row n and column m throw new IllegalStateException("Not implemented cursorMovement: " + cursorMovement); case 69: // E Move cursor to beginning of line and n lines down moveCursor.x = 1; moveCursor.y += number; - setCursorPosition(textPane, moveCursor); - return true; + break; case 70: // F Move cursor to beginning of line and n lines up moveCursor.x = 1; moveCursor.y -= number; - setCursorPosition(textPane, moveCursor); - return true; + break; case 71: // G Move cursor to column n moveCursor.x = number; - setCursorPosition(textPane, moveCursor); - return true; + break; default: throw new IllegalStateException("Unexpected cursorMovement: " + cursorMovement); } + setCursorPosition(textPane, moveCursor); + return true; } return false; } From c6018fd61812a0388c0c35b51286acd1a913f36b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 04:59:50 +0100 Subject: [PATCH 297/359] [Updater] Use the same json root key name for settings write and read --- .../src/org/selfbus/updater/gui/GuiSettings.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java index 05b9c0e7..42290802 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiSettings.java @@ -24,6 +24,10 @@ private GuiSettings() { this.parent = null; } + private ObjectNode getObjectNode() { + return objectMapper.createObjectNode(); + } + public GuiSettings(JFrame parent) { this.parent = parent; logger.debug("parent: {} name: {}", this.parent.getClass().getName(), this.parent.getName()); @@ -43,10 +47,10 @@ public void writeComponentSettings(String fileName) throws IOException { private void readComponentData(Component component, JsonNode node) { String nodeKey; - //todo this is a bad hack, because our GuiMain.MainFrame is saved with json key `ObjectNode` - // not sure why + // GuiMain.MainFrame is saved with json root key `ObjectNode`s simple class name + // this.getObjectNode() ensures that we set the same nodeKey as in writeComponentsData() if (component == parent) { - nodeKey = "ObjectNode"; + nodeKey = this.getObjectNode().getClass().getSimpleName(); } else { nodeKey = getComponentSettingKey(component); @@ -76,7 +80,7 @@ else if (component instanceof Container container) { } private JsonNode writeComponentsData(Component component) { - ObjectNode node = objectMapper.createObjectNode(); + ObjectNode node = getObjectNode(); if (component instanceof JCheckBox checkBox) { node.put("selected", checkBox.isSelected()); } From 7b9b04a61851bfb14951705df7b6753c89c01816 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 16:37:53 +0100 Subject: [PATCH 298/359] [Updater] Add normal/bright fore/background handling Also use `java.awt.Color.magenta` for `Ansi.Color.MAGENTA`. --- .../updater/gui/ConColorsToStyledDoc.java | 111 +++++++++++------- .../gui/ConColorsToStyledDocTests.java | 14 +-- 2 files changed, 77 insertions(+), 48 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index fbaa8ea7..99df83fd 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -21,6 +21,10 @@ private ConColorsToStyledDoc() {} // avoids instance creation public static final java.awt.Color DefaultForegroundColor = java.awt.Color.white; public static final java.awt.Color DefaultBackgroundColor = java.awt.Color.black; + + private static final double normalScaling = 0.7; + private static final double brightnessScaling = 1; + private static final String AnsiEscape = "\033"; // ASCII escape character (ESC) private static final String AnsiBracket = "["; // Ansi second character [ @@ -265,69 +269,67 @@ private static Style colorCodeToStyle(String [] colorCodes) { // Foreground colors // case 30: // Foreground color black - StyleConstants.setForeground(stringStyle, java.awt.Color.black); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.black)); break; case 31: // Foreground color red - StyleConstants.setForeground(stringStyle, java.awt.Color.red); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.red)); break; case 32: // Foreground color green - StyleConstants.setForeground(stringStyle, java.awt.Color.green); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.green)); break; case 33: // Foreground color yellow // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO - StyleConstants.setForeground(stringStyle, java.awt.Color.orange); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.orange)); break; case 34: // Foreground color blue - StyleConstants.setForeground(stringStyle, java.awt.Color.blue); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.blue)); break; case 35: // Foreground color magenta - //todo check which one looks better: java.awt.Color.magenta or java.awt.Color.pink? - StyleConstants.setForeground(stringStyle, java.awt.Color.pink); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.magenta)); break; case 36: // Foreground color cyan - StyleConstants.setForeground(stringStyle, java.awt.Color.cyan); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.cyan)); break; case 37: // Foreground color white - StyleConstants.setForeground(stringStyle, java.awt.Color.white); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.white)); break; case 38: throw new IllegalStateException("256 color foreground not implemented"); case 39: // Foreground color default - StyleConstants.setForeground(stringStyle, DefaultForegroundColor); + StyleConstants.setForeground(stringStyle, normalColor(DefaultForegroundColor)); break; // // Background colors // case 40: // Background color black - StyleConstants.setBackground(stringStyle, java.awt.Color.black); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.black)); break; case 41: // Background color red - StyleConstants.setBackground(stringStyle, java.awt.Color.red); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.red)); break; case 42: // Background color green - StyleConstants.setBackground(stringStyle, java.awt.Color.green); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.green)); break; case 43: // Background color yellow // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO - StyleConstants.setBackground(stringStyle, java.awt.Color.orange); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.orange)); break; case 44: // Background color blue - StyleConstants.setBackground(stringStyle, java.awt.Color.blue); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.blue)); break; case 45: // Background color magenta - //todo check which one looks better: java.awt.Color.magenta or java.awt.Color.pink? - StyleConstants.setBackground(stringStyle, java.awt.Color.pink); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.magenta)); break; case 46: // Background color cyan - StyleConstants.setBackground(stringStyle, java.awt.Color.cyan); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.cyan)); break; case 47: // Background color white - StyleConstants.setBackground(stringStyle, java.awt.Color.white); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.white)); break; case 48: throw new IllegalStateException("256 color background not implemented"); case 49: // Background color default - StyleConstants.setBackground(stringStyle, DefaultBackgroundColor); + StyleConstants.setBackground(stringStyle, normalColor(DefaultBackgroundColor)); break; // // 50-89 reserved @@ -336,66 +338,68 @@ private static Style colorCodeToStyle(String [] colorCodes) { // // Bright foreground colors // - //todo find better java.awt.Color values, right now, they are the same as "normal" case 90: // Bright foreground color black - StyleConstants.setForeground(stringStyle, java.awt.Color.black); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.black)); break; case 91: // Bright foreground color red - StyleConstants.setForeground(stringStyle, java.awt.Color.red); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.red)); break; case 92: // Bright foreground color green - StyleConstants.setForeground(stringStyle, java.awt.Color.green); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.green)); break; case 93: // Bright foreground color yellow // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO - StyleConstants.setForeground(stringStyle, java.awt.Color.orange); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.orange)); break; case 94: // Bright foreground color blue - StyleConstants.setForeground(stringStyle, java.awt.Color.blue); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.blue)); break; case 95: // Bright foreground color magenta - //todo check which one looks better: java.awt.Color.magenta or java.awt.Color.pink? - StyleConstants.setForeground(stringStyle, java.awt.Color.pink); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.magenta)); break; case 96: // Bright foreground color cyan - StyleConstants.setForeground(stringStyle, java.awt.Color.cyan); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.cyan)); break; case 97: // Bright foreground color white - StyleConstants.setForeground(stringStyle, java.awt.Color.white); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.white)); break; // - // 98-99 reserved + // 98 reserved // + case 99: // Bright foreground color default + StyleConstants.setForeground(stringStyle, brightColor(DefaultBackgroundColor)); + break; // // Bright background colors // - //todo find better java.awt.Color values, right now, they are the same as "normal" case 100: // Bright background color black - StyleConstants.setBackground(stringStyle, java.awt.Color.black); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.black)); break; case 101: // Bright background color red - StyleConstants.setBackground(stringStyle, java.awt.Color.red); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.red)); break; case 102: // Bright background color green - StyleConstants.setBackground(stringStyle, java.awt.Color.green); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.green)); break; case 103: // Bright background color yellow // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO - StyleConstants.setBackground(stringStyle, java.awt.Color.orange); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.orange)); break; case 104: // Bright background color blue - StyleConstants.setBackground(stringStyle, java.awt.Color.blue); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.blue)); break; case 105: // Bright background color magenta - //todo check which one looks better: java.awt.Color.magenta or java.awt.Color.pink? - StyleConstants.setBackground(stringStyle, java.awt.Color.pink); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.magenta)); break; case 106: // Bright background color cyan - StyleConstants.setBackground(stringStyle, java.awt.Color.cyan); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.cyan)); break; case 107: // Bright background color white - StyleConstants.setBackground(stringStyle, java.awt.Color.white); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.white)); + break; + case 109: // Bright background color default + StyleConstants.setBackground(stringStyle, brightColor(DefaultBackgroundColor)); break; default: throw new IllegalStateException("Unexpected code value: " + Integer.parseInt(code)); @@ -467,6 +471,31 @@ public static void setCursorPosition(JTextPane textPane, Point newPosition) thro textPane.setCaretPosition(doc.getLength()); } + /** + * Changes the brightness of a given color. + * @param color The original color. + * @param factor Brightness factor (e.g., 1.2 for higher brightness, 0.8 for lower brightness). + * @return A new color with adjusted brightness. + */ + private static Color changeBrightness(Color color, double factor) { + int red = (int) Math.min(255, color.getRed() * factor); + int green = (int) Math.min(255, color.getGreen() * factor); + int blue = (int) Math.min(255, color.getBlue() * factor); + + red = Math.max(0, red); + green = Math.max(0, green); + blue = Math.max(0, blue); + return new Color(red, green, blue); + } + + public static Color brightColor(Color color) { + return changeBrightness(color, brightnessScaling); + } + + public static Color normalColor(Color color) { + return changeBrightness(color, normalScaling); + } + /** *

Warning: This method is intended for unit tests only.

* It invokes the {@link #colorCodeToStyle} method with the provided color codes. diff --git a/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java b/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java index 66636f25..9840c3fa 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java +++ b/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java @@ -79,15 +79,15 @@ public void testStandardReset() { public void testForegroundColors() { java.awt.Color backgroundColor = StyleConstants.getBackground(testStringStyle()); testColorCodeToStyle(new String[]{"30"}); - assertStyleForeground(Color.black); + assertStyleForeground(normalColor(Color.black)); assertStyleBackground(backgroundColor); testColorCodeToStyle(new String[]{"1", "31"}); - assertStyleForeground(Color.red); + assertStyleForeground(normalColor(Color.red)); assertStyleBackground(backgroundColor); testColorCodeToStyle(new String[]{"2", "32", "3"}); - assertStyleForeground(Color.green); + assertStyleForeground(normalColor(Color.green)); assertStyleBackground(backgroundColor); } @@ -95,19 +95,19 @@ public void testForegroundColors() { public void testBackgroundColors() { java.awt.Color foregroundColor = StyleConstants.getForeground(testStringStyle()); testColorCodeToStyle(new String[]{"1", "40"}); - assertStyleBackground(java.awt.Color.black); + assertStyleBackground(normalColor(java.awt.Color.black)); assertStyleForeground(foregroundColor); testColorCodeToStyle(new String[]{"2", "41"}); - assertStyleBackground(java.awt.Color.red); + assertStyleBackground(normalColor(java.awt.Color.red)); assertStyleForeground(foregroundColor); testColorCodeToStyle(new String[]{"42", "3"}); - assertStyleBackground(java.awt.Color.green); + assertStyleBackground(normalColor(java.awt.Color.green)); assertStyleForeground(foregroundColor); testColorCodeToStyle(new String[]{"43"}); - assertStyleBackground(java.awt.Color.orange); + assertStyleBackground(normalColor(java.awt.Color.orange)); assertStyleForeground(foregroundColor); } From c82a85ee7c8a503e580eb394aab914f9dbb4e1d5 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 17:13:11 +0100 Subject: [PATCH 299/359] [Updater] Use always default background color --- firmware_updater/updater/src/org/selfbus/updater/Updater.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index c21fbfaf..0c16b0ad 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -200,7 +200,7 @@ public void run() { } else { logger.info("{}No firmware file (*.hex) specified! Specify with --{}{}", - ansi().bg(RED).fg(BLACK), CliOptions.OPT_LONG_FILENAME, ansi().reset()); + ansi().fg(RED), CliOptions.OPT_LONG_FILENAME, ansi().reset()); logger.info("{}Reading only device information{}", ansi().fgBright(YELLOW), ansi().reset()); } @@ -355,7 +355,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres if (newFirmware.getAppVersion().contains(BootloaderUpdater.BOOTLOADER_UPDATER_ID_STRING)) { logger.info("{}Wait {} second(s) for Bootloader Updater to finish its job{}", - ansi().bg(GREEN).fg(BLACK), + ansi().fg(GREEN), String.format("%.2f", BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS / 1000.0f), ansi().reset()); Thread.sleep(BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS); From f8198c829b6cfc7317972bbb970c2381ba22c169 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 17:21:13 +0100 Subject: [PATCH 300/359] [Updater] Use always bright foreground colors --- .../src/org/selfbus/updater/BinImage.java | 4 +- .../src/org/selfbus/updater/CliOptions.java | 54 +++++++++---------- .../org/selfbus/updater/FlashFullMode.java | 2 +- .../src/org/selfbus/updater/SBKNXLink.java | 6 +-- .../src/org/selfbus/updater/Updater.java | 12 ++--- .../updater/bootloader/BootDescriptor.java | 2 +- .../updater/devicemgnt/DeviceManagement.java | 8 +-- .../src/org/selfbus/updater/gui/GuiMain.java | 2 +- .../gui/GuiUncaughtExceptionHandler.java | 2 +- 9 files changed, 46 insertions(+), 46 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/BinImage.java b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java index fed2f7f8..c1ffd0aa 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java @@ -129,7 +129,7 @@ public final void writeToBinFile(String fileName) { File binFile = new File(fileName); if (!binFile.getParentFile().exists()) { if (!binFile.getParentFile().mkdirs()) { - logger.warn("{}Could not create bin-file directory {}{}", ansi().fg(RED), + logger.warn("{}Could not create bin-file directory {}{}", ansi().fgBright(RED), binFile.getParentFile().toString(), ansi().reset()); } return; @@ -138,7 +138,7 @@ public final void writeToBinFile(String fileName) { try (FileOutputStream fos = new FileOutputStream(binFile)) { fos.write(binData); } catch (IOException e) { - logger.warn("{}Could not write bin-file {}{}", ansi().fg(RED), binFile.getPath(), ansi().reset()); + logger.warn("{}Could not write bin-file {}{}", ansi().fgBright(RED), binFile.getPath(), ansi().reset()); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 7f65c8f6..7cf15b50 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -561,57 +561,57 @@ private void validateCliParameters() throws CliInvalidException { // differential mode and eraseflash makes no sense if (getEraseFullFlashIsSet() && (!getFlashingFullModeIsSet())) { setFlashingFullModeIsSet(true); - logger.info("{}--{} is set. --> switching to full flash mode{}", ansi().fg(RED), + logger.info("{}--{} is set. --> switching to full flash mode{}", ansi().fgBright(RED), OPT_LONG_ERASEFLASH, ansi().reset()); } // nat only possible with tunneling v1 if (getNatIsSet() && (!getTunnelingV1isSet())) { throw new CliInvalidException(String.format("%sOption --%s can only be used together with --%s%s", - ansi().fg(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ansi().reset())); + ansi().fgBright(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ansi().reset())); } // nat not allowed with tunneling v2 if (getNatIsSet() && (getTunnelingV2isSet())) { throw new CliInvalidException(String.format("%sOption --%s can not be used together with --%s%s", - ansi().fg(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2, ansi().reset())); + ansi().fgBright(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2, ansi().reset())); } // check IP-secure configuration if (!(getKnxSecureUserPassword().isEmpty()) || !(getKnxSecureDevicePassword().isEmpty())) { if (getKnxInterface().isBlank()) { throw new CliInvalidException(String.format("%sNo IP-Interface specified for IP-secure%s", - ansi().fg(RED), ansi().reset())); + ansi().fgBright(RED), ansi().reset())); } else if (!getUsbVendorIdAndProductId().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_USB, ansi().reset())); + ansi().fgBright(RED), OPT_LONG_USB, ansi().reset())); } else if (!getFt12SerialPort().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_FT12, ansi().reset())); + ansi().fgBright(RED), OPT_LONG_FT12, ansi().reset())); } else if (!getTpuartSerialPort().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_TPUART, ansi().reset())); + ansi().fgBright(RED), OPT_LONG_TPUART, ansi().reset())); } else if (getNatIsSet()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_NAT, ansi().reset())); + ansi().fgBright(RED), OPT_LONG_NAT, ansi().reset())); } else if (getTunnelingV1isSet()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_TUNNEL_V1, ansi().reset())); + ansi().fgBright(RED), OPT_LONG_TUNNEL_V1, ansi().reset())); } else if (getTunnelingV2isSet()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fg(RED), OPT_LONG_TUNNEL_V2, ansi().reset())); + ansi().fgBright(RED), OPT_LONG_TUNNEL_V2, ansi().reset())); } // ensure that all three IP-Secure arguments are set if ((getKnxSecureUserPassword().isEmpty()) || (getKnxSecureDevicePassword().isEmpty())) { throw new CliInvalidException(String.format("%sFor IP-secure --%s, --%s and --%s must be set%s", - ansi().fg(RED), OPT_LONG_USER_ID, OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD, + ansi().fgBright(RED), OPT_LONG_USER_ID, OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD, ansi().reset())); } } @@ -789,7 +789,7 @@ private void setLocalPort(int localPort) { else { this.localPort = 0; logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fg(RED), OPT_LONG_LOCALPORT, localPort, 0, 65535, this.localPort, ansi().reset()); + ansi().fgBright(RED), OPT_LONG_LOCALPORT, localPort, 0, 65535, this.localPort, ansi().reset()); } } @@ -803,7 +803,7 @@ private void setLocalPort(String localPort) { catch (NumberFormatException e) { setLocalPort(0); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fg(RED), OPT_LONG_LOCALPORT, localPort, getLocalPort(), ansi().reset()); + ansi().fgBright(RED), OPT_LONG_LOCALPORT, localPort, getLocalPort(), ansi().reset()); } } @@ -819,7 +819,7 @@ private void setPort(int port) { else { this.port = KNXnetIPConnection.DEFAULT_PORT; logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fg(RED), OPT_LONG_PORT, port, 1, 65535, this.port, ansi().reset()); + ansi().fgBright(RED), OPT_LONG_PORT, port, 1, 65535, this.port, ansi().reset()); } } @@ -833,7 +833,7 @@ private void setPort(String port) { catch (NumberFormatException e) { setPort(KNXnetIPConnection.DEFAULT_PORT); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fg(RED), OPT_LONG_PORT, port, getPort(), ansi().reset()); + ansi().fgBright(RED), OPT_LONG_PORT, port, getPort(), ansi().reset()); } } @@ -967,7 +967,7 @@ public int getDelayMs() { private void setDelayMs(int delayMs) { if ((delayMs < Updater.DELAY_MIN) || (delayMs > Updater.DELAY_MAX)) { logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fg(RED), OPT_LONG_DELAY, delayMs, Updater.DELAY_MIN, + ansi().fgBright(RED), OPT_LONG_DELAY, delayMs, Updater.DELAY_MIN, Updater.DELAY_MAX, Updater.DELAY_MIN, ansi().reset()); delayMs = Updater.DELAY_MIN; // set to DELAY_MIN in case of invalid waiting time } @@ -985,7 +985,7 @@ private void setDelayMs(String delayMs) { catch (NumberFormatException e) { setDelayMs(Updater.DELAY_MIN); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fg(RED), OPT_LONG_DELAY, delayMs, getDelayMs(), ansi().reset()); + ansi().fgBright(RED), OPT_LONG_DELAY, delayMs, getDelayMs(), ansi().reset()); } } @@ -1056,7 +1056,7 @@ private void setLogLevel(String newLevel) { setLogLevel(Level.toLevel(newLevel)); } else { - logger.warn("{}invalid {} {}, using {}{}", ansi().fg(RED), OPT_LONG_LOGLEVEL, newLevel, + logger.warn("{}invalid {} {}, using {}{}", ansi().fgBright(RED), OPT_LONG_LOGLEVEL, newLevel, getLogLevel().toString(), ansi().reset()); } } @@ -1078,7 +1078,7 @@ private void setKnxSecureUserId(int knxSecureUserId) { } else { logger.warn("{}option --{} *** is invalid => set to default{}", - ansi().fg(RED), OPT_LONG_USER_ID, ansi().reset()); + ansi().fgBright(RED), OPT_LONG_USER_ID, ansi().reset()); this.knxSecureUserId = INVALID_SECURE_USER_ID; logger.debug("{}=", OPT_LONG_USER_ID); } @@ -1094,7 +1094,7 @@ private void setKnxSecureUserId(String knxSecureUserId) { catch (NumberFormatException e) { setKnxSecureUserId(INVALID_SECURE_USER_ID); logger.warn("{}option --{} *** is invalid => set to default{}", - ansi().fg(RED), OPT_LONG_USER_ID, ansi().reset()); + ansi().fgBright(RED), OPT_LONG_USER_ID, ansi().reset()); } } @@ -1132,7 +1132,7 @@ private void setPriority(String priority) { logger.debug("{}={}", OPT_LONG_PRIORITY, getPriority().toString()); } catch (KNXIllegalArgumentException e) { - logger.warn("{}invalid --{} {}, using {}{}",ansi().fg(RED), OPT_LONG_PRIORITY, + logger.warn("{}invalid --{} {}, using {}{}",ansi().fgBright(RED), OPT_LONG_PRIORITY, priority, getPriority(), ansi().reset()); } } @@ -1156,7 +1156,7 @@ private void setBlockSize(int blockSize) { } else { this.blockSize = Mcu.UPD_PROGRAM_SIZE; - logger.info("{}--{} {} is not supported => Set --{} to default {} bytes{}", ansi().fg(YELLOW), + logger.info("{}--{} {} is not supported => Set --{} to default {} bytes{}", ansi().fgBright(YELLOW), OPT_LONG_BLOCKSIZE, blockSize, OPT_LONG_BLOCKSIZE, getBlockSize(), ansi().reset()); } logger.debug("{}={}", OPT_LONG_BLOCKSIZE, getBlockSize()); @@ -1172,7 +1172,7 @@ private void setBlockSize(String blockSize) { catch (NumberFormatException e) { setBlockSize(Mcu.UPD_PROGRAM_SIZE); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fg(RED), OPT_LONG_BLOCKSIZE, blockSize, getBlockSize(), ansi().reset()); + ansi().fgBright(RED), OPT_LONG_BLOCKSIZE, blockSize, getBlockSize(), ansi().reset()); } } @@ -1201,7 +1201,7 @@ public int getReconnectMs() { private void setReconnectMs(int reconnectMs) { if ((reconnectMs < RECONNECT_MIN_MS) || (reconnectMs > RECONNECT_MAX_MS)) { logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fg(RED), OPT_LONG_RECONNECT, reconnectMs, RECONNECT_MIN_MS, + ansi().fgBright(RED), OPT_LONG_RECONNECT, reconnectMs, RECONNECT_MIN_MS, RECONNECT_MAX_MS, RECONNECT_MIN_MS, ansi().reset()); reconnectMs = RECONNECT_MIN_MS; // set to RECONNECT_MIN_MS in case of invalid time } @@ -1219,7 +1219,7 @@ private void setReconnectMs(String reconnectMs) { catch (NumberFormatException e) { setReconnectMs(RECONNECT_MIN_MS); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fg(RED), OPT_LONG_RECONNECT, reconnectMs, getReconnectMs(), ansi().reset()); + ansi().fgBright(RED), OPT_LONG_RECONNECT, reconnectMs, getReconnectMs(), ansi().reset()); } } @@ -1231,7 +1231,7 @@ private void setReconnectSeqNumber(int reconnectSeqNumber) { if (((reconnectSeqNumber < RECONNECT_MIN_SEQ_NUMBER) || (reconnectSeqNumber > RECONNECT_MAX_SEQ_NUMBER)) && (reconnectSeqNumber != RECONNECT_INVALID_SEQ_NUMBER)) { logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fg(RED), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, RECONNECT_MIN_SEQ_NUMBER, + ansi().fgBright(RED), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, RECONNECT_MIN_SEQ_NUMBER, RECONNECT_MAX_SEQ_NUMBER, RECONNECT_INVALID_SEQ_NUMBER, ansi().reset()); reconnectSeqNumber = RECONNECT_INVALID_SEQ_NUMBER; // set to invalid } @@ -1249,7 +1249,7 @@ private void setReconnectSeqNumber(String reconnectSeqNumber) { catch (NumberFormatException e) { setReconnectSeqNumber(RECONNECT_INVALID_SEQ_NUMBER); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fg(RED), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, getReconnectMs(), ansi().reset()); + ansi().fgBright(RED), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, getReconnectMs(), ansi().reset()); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index e7ca4831..cda1aac4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -112,7 +112,7 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa case IAP_COMPARE_ERROR: throw new UpdaterException(String.format("ProgramData update failed. %sTry again with option '--%s 256'%s", - ansi().fg(RED), CliOptions.OPT_LONG_BLOCKSIZE, ansi().reset())); + ansi().fgBright(RED), CliOptions.OPT_LONG_BLOCKSIZE, ansi().reset())); default: dm.restartProgrammingDevice(); throw new UpdaterException("ProgramData update failed."); diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index e69f48e0..fb341fb9 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -59,13 +59,13 @@ private KNXNetworkLink createTunnelingLinkV2(InetSocketAddress local, InetSocket private KNXNetworkLink createTunnelingLinkV1(InetSocketAddress local, InetSocketAddress remote, boolean useNat, KNXMediumSettings medium) throws KNXException, InterruptedException { - logger.info("{}Connect using UDP tunneling v1 (nat:{}){}", ansi().fg(YELLOW), useNat, ansi().reset()); + logger.info("{}Connect using UDP tunneling v1 (nat:{}){}", ansi().fgBright(YELLOW), useNat, ansi().reset()); return KNXNetworkLinkIP.newTunnelingLink(local, remote, useNat, medium); } private KNXNetworkLink createRoutingLink(InetSocketAddress local, KNXMediumSettings medium) throws KNXException { logger.info("{}Connect using routing (multicast:{}){}", - ansi().fg(YELLOW), KNXnetIPRouting.DefaultMulticast, ansi().reset()); + ansi().fgBright(YELLOW), KNXnetIPRouting.DefaultMulticast, ansi().reset()); return KNXNetworkLinkIP.newRoutingLink(local.getAddress(), KNXnetIPRouting.DefaultMulticast, medium); } @@ -175,7 +175,7 @@ private KNXNetworkLink doOpenLink() throws KNXException, return testLink; } catch (final KNXException e) { cliOptions.setTunnelingV1isSet(false); - logger.info("{}failed with {}: {}{}", ansi().fg(YELLOW), e.getClass().getSimpleName(), e.getMessage(), + logger.info("{}failed with {}: {}{}", ansi().fgBright(YELLOW), e.getClass().getSimpleName(), e.getMessage(), ansi().reset()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 0c16b0ad..d45e7bb6 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -186,7 +186,7 @@ public void run() { if (!hexFileName.isEmpty()) { // check if the firmware file exists if (!Utils.fileExists(hexFileName)) { - logger.error("{}File '{}' does not exist!{}", ansi().fg(RED), cliOptions.getFileName(), ansi().reset()); + logger.error("{}File '{}' does not exist!{}", ansi().fgBright(RED), cliOptions.getFileName(), ansi().reset()); throw new UpdaterException(String.format("File '%s' does not exist!", cliOptions.getFileName())); } // Load Firmware hex file @@ -200,7 +200,7 @@ public void run() { } else { logger.info("{}No firmware file (*.hex) specified! Specify with --{}{}", - ansi().fg(RED), CliOptions.OPT_LONG_FILENAME, ansi().reset()); + ansi().fgBright(RED), CliOptions.OPT_LONG_FILENAME, ansi().reset()); logger.info("{}Reading only device information{}", ansi().fgBright(YELLOW), ansi().reset()); } @@ -309,7 +309,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres if (!dm.setBlockSize(cliOptions.getBlockSize())) { logger.info("{}Connected bootloader doesn't support block size {}. Using {} bytes.{}", - ansi().fg(YELLOW), cliOptions.getBlockSize(), dm.getBlockSize(), ansi().reset()); + ansi().fgBright(YELLOW), cliOptions.getBlockSize(), dm.getBlockSize(), ansi().reset()); } if (!cliOptions.getNoFlashIsSet()) { // is flashing firmware disabled? for debugging use only! @@ -336,7 +336,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } else { logger.warn("--{} => {}only boot description block will be written{}", CliOptions.OPT_LONG_NO_FLASH, - ansi().fg(RED), ansi().reset()); + ansi().fgBright(RED), ansi().reset()); } BootDescriptor newBootDescriptor = new BootDescriptor(newFirmware.startAddress(), @@ -355,7 +355,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres if (newFirmware.getAppVersion().contains(BootloaderUpdater.BOOTLOADER_UPDATER_ID_STRING)) { logger.info("{}Wait {} second(s) for Bootloader Updater to finish its job{}", - ansi().fg(GREEN), + ansi().fgBright(GREEN), String.format("%.2f", BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS / 1000.0f), ansi().reset()); Thread.sleep(BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS); @@ -417,7 +417,7 @@ public String requestUid() throws KNXException, UpdaterException, UnknownHostExc } catch (UpdaterException | KNXException e) { logger.error("{}An error occurred while retrieving the UID. {}{}{}", - ansi().fg(RED), System.lineSeparator(), e, ansi().reset()); + ansi().fgBright(RED), System.lineSeparator(), e, ansi().reset()); throw e; } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java index 6254ded5..602315d8 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java @@ -89,7 +89,7 @@ public String toString() { res = String.format("%s valid%s", ansi().fgBright(GREEN), ansi().reset()); } else { - res = String.format("%sinvalid%s", ansi().fg(RED), ansi().reset()); + res = String.format("%sinvalid%s", ansi().fgBright(RED), ansi().reset()); } res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X, APP_VERSION pointer: 0x%04X", res, long2short(startAddress()), long2short(endAddress()), length(), crc32(), long2short(appVersionAddress())); diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index b23e1344..625e4943 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -199,7 +199,7 @@ public BootloaderIdentity requestBootloaderIdentity() long minMajorVersion = result[DATA_POSITION] & 0xff; long minMinorVersion = result[DATA_POSITION + 1] & 0xff; logger.error("{}Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.{}", - ansi().fg(RED), ToolInfo.getVersion(), minMajorVersion, minMinorVersion, ansi().reset()); + ansi().fgBright(RED), ToolInfo.getVersion(), minMajorVersion, minMinorVersion, ansi().reset()); } restartProgrammingDevice(); throw new UpdaterException("Requesting Bootloader Identity failed!"); @@ -214,7 +214,7 @@ public BootloaderIdentity requestBootloaderIdentity() if (!versionsMatch) { logger.error("{}Bootloader version {} is not compatible, please update Bootloader to version {} or higher{}", - ansi().fg(RED), bl.getVersion(), ToolInfo.minVersionBootloader(), ansi().reset()); + ansi().fgBright(RED), bl.getVersion(), ToolInfo.minVersionBootloader(), ansi().reset()); throw new UpdaterException("Bootloader version not compatible!"); } return bl; @@ -404,7 +404,7 @@ public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); UPDCommand command = UPDCommand.tryFromByteArray(result); if (command != UPDCommand.RESPONSE_STATISTIC) { - logger.warn("Requesting Bootloader statistic {}failed!{}", ansi().fg(RED), ansi().reset()); + logger.warn("Requesting Bootloader statistic {}failed!{}", ansi().fgBright(RED), ansi().reset()); return null; } BootloaderStatistic blStatistic = BootloaderStatistic.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); @@ -422,7 +422,7 @@ private boolean isLinkAlive() { private void handleKNXException(final UPDCommand command, final KNXException e) throws UpdaterException, InterruptedException { - logger.warn("{}{}{} ({} {})", ansi().fg(RED), e.getMessage(), ansi().reset(), + logger.warn("{}{}{} ({} {})", ansi().fgBright(RED), e.getMessage(), ansi().reset(), e.getClass().getSimpleName(), command); try { reconnect(cliOptions.getReconnectMs()); diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 24c14e6a..32062f3c 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -386,7 +386,7 @@ private CliOptions getCliOptions() throws KNXFormatException, ParseException { } private void displayCommandLine(CliOptions cliOptions) { - logger.info("{}java -jar {} {}{}", ansi().fg(YELLOW), ToolInfo.getToolJarName(), + logger.info("{}java -jar {} {}{}", ansi().fgBright(YELLOW), ToolInfo.getToolJarName(), cliOptions.reconstructCommandLine(), ansi().reset()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java index c4e96015..0a642c5b 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java @@ -28,7 +28,7 @@ public GuiUncaughtExceptionHandler(JFrame parentFrame) { @Override public void uncaughtException(Thread thread, Throwable throwable) { logger.error("{}Uncaught exception in thread {} {}{}", - ansi().fg(RED) , thread.getName(), throwable.toString(), ansi().reset(), throwable); + ansi().fgBright(RED) , thread.getName(), throwable.toString(), ansi().reset(), throwable); // todo see logback issue #876 // https://github.com/qos-ch/logback/issues/876 From 16f71d6bd5cc9c4e6cc87269b297192406209692 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 19:28:33 +0100 Subject: [PATCH 301/359] [Updater] Use constants `OK, INFO, WARN` for colored logging --- .../src/org/selfbus/updater/BinImage.java | 6 +- .../src/org/selfbus/updater/CliOptions.java | 56 +++++++++---------- .../org/selfbus/updater/FlashDiffMode.java | 4 +- .../org/selfbus/updater/FlashFullMode.java | 4 +- .../src/org/selfbus/updater/SBKNXLink.java | 8 +-- .../src/org/selfbus/updater/Updater.java | 54 +++++++++--------- .../updater/bootloader/BootDescriptor.java | 6 +- .../bootloader/BootloaderStatistic.java | 6 +- .../updater/devicemgnt/DeviceManagement.java | 24 ++++---- .../src/org/selfbus/updater/gui/GuiMain.java | 4 +- .../gui/GuiUncaughtExceptionHandler.java | 4 +- .../org/selfbus/updater/logging/Color.java | 20 +++++++ .../updater/mode/differential/FlashDiff.java | 3 - .../updater/progress/ProgressInfo.java | 7 +-- .../org/selfbus/updater/upd/UPDProtocol.java | 4 +- .../updater/BootloaderStatisticTest.java | 7 ++- 16 files changed, 117 insertions(+), 100 deletions(-) create mode 100644 firmware_updater/updater/src/org/selfbus/updater/logging/Color.java diff --git a/firmware_updater/updater/src/org/selfbus/updater/BinImage.java b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java index c1ffd0aa..79703a3f 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java @@ -18,7 +18,7 @@ import java.util.zip.CRC32; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; public class BinImage { private final static Logger logger = LoggerFactory.getLogger(BinImage.class); @@ -129,7 +129,7 @@ public final void writeToBinFile(String fileName) { File binFile = new File(fileName); if (!binFile.getParentFile().exists()) { if (!binFile.getParentFile().mkdirs()) { - logger.warn("{}Could not create bin-file directory {}{}", ansi().fgBright(RED), + logger.warn("{}Could not create bin-file directory {}{}", ansi().fgBright(WARN), binFile.getParentFile().toString(), ansi().reset()); } return; @@ -138,7 +138,7 @@ public final void writeToBinFile(String fileName) { try (FileOutputStream fos = new FileOutputStream(binFile)) { fos.write(binData); } catch (IOException e) { - logger.warn("{}Could not write bin-file {}{}", ansi().fgBright(RED), binFile.getPath(), ansi().reset()); + logger.warn("{}Could not write bin-file {}{}", ansi().fgBright(WARN), binFile.getPath(), ansi().reset()); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 7cf15b50..e860980b 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -30,7 +30,7 @@ import java.util.Objects; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; /** * Parses command line interface (cli) options for the application @@ -561,57 +561,57 @@ private void validateCliParameters() throws CliInvalidException { // differential mode and eraseflash makes no sense if (getEraseFullFlashIsSet() && (!getFlashingFullModeIsSet())) { setFlashingFullModeIsSet(true); - logger.info("{}--{} is set. --> switching to full flash mode{}", ansi().fgBright(RED), + logger.info("{}--{} is set. --> switching to full flash mode{}", ansi().fgBright(WARN), OPT_LONG_ERASEFLASH, ansi().reset()); } // nat only possible with tunneling v1 if (getNatIsSet() && (!getTunnelingV1isSet())) { throw new CliInvalidException(String.format("%sOption --%s can only be used together with --%s%s", - ansi().fgBright(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ansi().reset())); + ansi().fgBright(WARN), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ansi().reset())); } // nat not allowed with tunneling v2 if (getNatIsSet() && (getTunnelingV2isSet())) { throw new CliInvalidException(String.format("%sOption --%s can not be used together with --%s%s", - ansi().fgBright(RED), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2, ansi().reset())); + ansi().fgBright(WARN), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2, ansi().reset())); } // check IP-secure configuration if (!(getKnxSecureUserPassword().isEmpty()) || !(getKnxSecureDevicePassword().isEmpty())) { if (getKnxInterface().isBlank()) { throw new CliInvalidException(String.format("%sNo IP-Interface specified for IP-secure%s", - ansi().fgBright(RED), ansi().reset())); + ansi().fgBright(WARN), ansi().reset())); } else if (!getUsbVendorIdAndProductId().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fgBright(RED), OPT_LONG_USB, ansi().reset())); + ansi().fgBright(WARN), OPT_LONG_USB, ansi().reset())); } else if (!getFt12SerialPort().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fgBright(RED), OPT_LONG_FT12, ansi().reset())); + ansi().fgBright(WARN), OPT_LONG_FT12, ansi().reset())); } else if (!getTpuartSerialPort().isBlank()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fgBright(RED), OPT_LONG_TPUART, ansi().reset())); + ansi().fgBright(WARN), OPT_LONG_TPUART, ansi().reset())); } else if (getNatIsSet()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fgBright(RED), OPT_LONG_NAT, ansi().reset())); + ansi().fgBright(WARN), OPT_LONG_NAT, ansi().reset())); } else if (getTunnelingV1isSet()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fgBright(RED), OPT_LONG_TUNNEL_V1, ansi().reset())); + ansi().fgBright(WARN), OPT_LONG_TUNNEL_V1, ansi().reset())); } else if (getTunnelingV2isSet()) { throw new CliInvalidException(String.format("%sIP-secure is not possible with --%s%s", - ansi().fgBright(RED), OPT_LONG_TUNNEL_V2, ansi().reset())); + ansi().fgBright(WARN), OPT_LONG_TUNNEL_V2, ansi().reset())); } // ensure that all three IP-Secure arguments are set if ((getKnxSecureUserPassword().isEmpty()) || (getKnxSecureDevicePassword().isEmpty())) { throw new CliInvalidException(String.format("%sFor IP-secure --%s, --%s and --%s must be set%s", - ansi().fgBright(RED), OPT_LONG_USER_ID, OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD, + ansi().fgBright(WARN), OPT_LONG_USER_ID, OPT_LONG_USER_PASSWORD, OPT_LONG_DEVICE_PASSWORD, ansi().reset())); } } @@ -789,7 +789,7 @@ private void setLocalPort(int localPort) { else { this.localPort = 0; logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fgBright(RED), OPT_LONG_LOCALPORT, localPort, 0, 65535, this.localPort, ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_LOCALPORT, localPort, 0, 65535, this.localPort, ansi().reset()); } } @@ -803,7 +803,7 @@ private void setLocalPort(String localPort) { catch (NumberFormatException e) { setLocalPort(0); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fgBright(RED), OPT_LONG_LOCALPORT, localPort, getLocalPort(), ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_LOCALPORT, localPort, getLocalPort(), ansi().reset()); } } @@ -819,7 +819,7 @@ private void setPort(int port) { else { this.port = KNXnetIPConnection.DEFAULT_PORT; logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fgBright(RED), OPT_LONG_PORT, port, 1, 65535, this.port, ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_PORT, port, 1, 65535, this.port, ansi().reset()); } } @@ -833,7 +833,7 @@ private void setPort(String port) { catch (NumberFormatException e) { setPort(KNXnetIPConnection.DEFAULT_PORT); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fgBright(RED), OPT_LONG_PORT, port, getPort(), ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_PORT, port, getPort(), ansi().reset()); } } @@ -967,7 +967,7 @@ public int getDelayMs() { private void setDelayMs(int delayMs) { if ((delayMs < Updater.DELAY_MIN) || (delayMs > Updater.DELAY_MAX)) { logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fgBright(RED), OPT_LONG_DELAY, delayMs, Updater.DELAY_MIN, + ansi().fgBright(WARN), OPT_LONG_DELAY, delayMs, Updater.DELAY_MIN, Updater.DELAY_MAX, Updater.DELAY_MIN, ansi().reset()); delayMs = Updater.DELAY_MIN; // set to DELAY_MIN in case of invalid waiting time } @@ -985,7 +985,7 @@ private void setDelayMs(String delayMs) { catch (NumberFormatException e) { setDelayMs(Updater.DELAY_MIN); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fgBright(RED), OPT_LONG_DELAY, delayMs, getDelayMs(), ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_DELAY, delayMs, getDelayMs(), ansi().reset()); } } @@ -1056,7 +1056,7 @@ private void setLogLevel(String newLevel) { setLogLevel(Level.toLevel(newLevel)); } else { - logger.warn("{}invalid {} {}, using {}{}", ansi().fgBright(RED), OPT_LONG_LOGLEVEL, newLevel, + logger.warn("{}invalid {} {}, using {}{}", ansi().fgBright(WARN), OPT_LONG_LOGLEVEL, newLevel, getLogLevel().toString(), ansi().reset()); } } @@ -1078,7 +1078,7 @@ private void setKnxSecureUserId(int knxSecureUserId) { } else { logger.warn("{}option --{} *** is invalid => set to default{}", - ansi().fgBright(RED), OPT_LONG_USER_ID, ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_USER_ID, ansi().reset()); this.knxSecureUserId = INVALID_SECURE_USER_ID; logger.debug("{}=", OPT_LONG_USER_ID); } @@ -1094,7 +1094,7 @@ private void setKnxSecureUserId(String knxSecureUserId) { catch (NumberFormatException e) { setKnxSecureUserId(INVALID_SECURE_USER_ID); logger.warn("{}option --{} *** is invalid => set to default{}", - ansi().fgBright(RED), OPT_LONG_USER_ID, ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_USER_ID, ansi().reset()); } } @@ -1132,7 +1132,7 @@ private void setPriority(String priority) { logger.debug("{}={}", OPT_LONG_PRIORITY, getPriority().toString()); } catch (KNXIllegalArgumentException e) { - logger.warn("{}invalid --{} {}, using {}{}",ansi().fgBright(RED), OPT_LONG_PRIORITY, + logger.warn("{}invalid --{} {}, using {}{}",ansi().fgBright(WARN), OPT_LONG_PRIORITY, priority, getPriority(), ansi().reset()); } } @@ -1156,7 +1156,7 @@ private void setBlockSize(int blockSize) { } else { this.blockSize = Mcu.UPD_PROGRAM_SIZE; - logger.info("{}--{} {} is not supported => Set --{} to default {} bytes{}", ansi().fgBright(YELLOW), + logger.info("{}--{} {} is not supported => Set --{} to default {} bytes{}", ansi().fgBright(INFO), OPT_LONG_BLOCKSIZE, blockSize, OPT_LONG_BLOCKSIZE, getBlockSize(), ansi().reset()); } logger.debug("{}={}", OPT_LONG_BLOCKSIZE, getBlockSize()); @@ -1172,7 +1172,7 @@ private void setBlockSize(String blockSize) { catch (NumberFormatException e) { setBlockSize(Mcu.UPD_PROGRAM_SIZE); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fgBright(RED), OPT_LONG_BLOCKSIZE, blockSize, getBlockSize(), ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_BLOCKSIZE, blockSize, getBlockSize(), ansi().reset()); } } @@ -1201,7 +1201,7 @@ public int getReconnectMs() { private void setReconnectMs(int reconnectMs) { if ((reconnectMs < RECONNECT_MIN_MS) || (reconnectMs > RECONNECT_MAX_MS)) { logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fgBright(RED), OPT_LONG_RECONNECT, reconnectMs, RECONNECT_MIN_MS, + ansi().fgBright(WARN), OPT_LONG_RECONNECT, reconnectMs, RECONNECT_MIN_MS, RECONNECT_MAX_MS, RECONNECT_MIN_MS, ansi().reset()); reconnectMs = RECONNECT_MIN_MS; // set to RECONNECT_MIN_MS in case of invalid time } @@ -1219,7 +1219,7 @@ private void setReconnectMs(String reconnectMs) { catch (NumberFormatException e) { setReconnectMs(RECONNECT_MIN_MS); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fgBright(RED), OPT_LONG_RECONNECT, reconnectMs, getReconnectMs(), ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_RECONNECT, reconnectMs, getReconnectMs(), ansi().reset()); } } @@ -1231,7 +1231,7 @@ private void setReconnectSeqNumber(int reconnectSeqNumber) { if (((reconnectSeqNumber < RECONNECT_MIN_SEQ_NUMBER) || (reconnectSeqNumber > RECONNECT_MAX_SEQ_NUMBER)) && (reconnectSeqNumber != RECONNECT_INVALID_SEQ_NUMBER)) { logger.warn("{}option --{} {} is invalid (min:{}, max:{}) => set to {}{}", - ansi().fgBright(RED), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, RECONNECT_MIN_SEQ_NUMBER, + ansi().fgBright(WARN), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, RECONNECT_MIN_SEQ_NUMBER, RECONNECT_MAX_SEQ_NUMBER, RECONNECT_INVALID_SEQ_NUMBER, ansi().reset()); reconnectSeqNumber = RECONNECT_INVALID_SEQ_NUMBER; // set to invalid } @@ -1249,7 +1249,7 @@ private void setReconnectSeqNumber(String reconnectSeqNumber) { catch (NumberFormatException e) { setReconnectSeqNumber(RECONNECT_INVALID_SEQ_NUMBER); logger.warn("{}option --{} {} is invalid => set to default {}{}", - ansi().fgBright(RED), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, getReconnectMs(), ansi().reset()); + ansi().fgBright(WARN), OPT_LONG_RECONNECT_SEQ_NUMBER, reconnectSeqNumber, getReconnectMs(), ansi().reset()); } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index d56e8c6d..92e7f7d8 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -19,7 +19,7 @@ import java.util.concurrent.atomic.AtomicReference; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; import static org.selfbus.updater.Mcu.MAX_PAYLOAD; /** @@ -66,7 +66,7 @@ public static boolean setupDifferentialMode(BootDescriptor bootDescriptor) { } else { logger.warn("Current device firmware not found in cache {}", Utils.shortenPath(oldFileName, DEFAULT_DISPLAYED_PATH_DEPTH)); - logger.warn("{} --> switching to FULL upload mode{}", ansi().fgBright(RED), ansi().reset()); + logger.warn("{} --> switching to FULL upload mode{}", ansi().fgBright(WARN), ansi().reset()); initialized = false; } return initialized; diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java index cda1aac4..54258902 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashFullMode.java @@ -16,7 +16,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; /** * Provides full flash mode for the bootloader (MCU) @@ -112,7 +112,7 @@ public static ResponseResult doFullFlash(DeviceManagement dm, BinImage newFirmwa case IAP_COMPARE_ERROR: throw new UpdaterException(String.format("ProgramData update failed. %sTry again with option '--%s 256'%s", - ansi().fgBright(RED), CliOptions.OPT_LONG_BLOCKSIZE, ansi().reset())); + ansi().fgBright(WARN), CliOptions.OPT_LONG_BLOCKSIZE, ansi().reset())); default: dm.restartProgrammingDevice(); throw new UpdaterException("ProgramData update failed."); diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index fb341fb9..e8989e99 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -22,7 +22,7 @@ import java.util.Objects; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; public class SBKNXLink { private static final Logger logger = LoggerFactory.getLogger(SBKNXLink.class); @@ -59,13 +59,13 @@ private KNXNetworkLink createTunnelingLinkV2(InetSocketAddress local, InetSocket private KNXNetworkLink createTunnelingLinkV1(InetSocketAddress local, InetSocketAddress remote, boolean useNat, KNXMediumSettings medium) throws KNXException, InterruptedException { - logger.info("{}Connect using UDP tunneling v1 (nat:{}){}", ansi().fgBright(YELLOW), useNat, ansi().reset()); + logger.info("{}Connect using UDP tunneling v1 (nat:{}){}", ansi().fgBright(INFO), useNat, ansi().reset()); return KNXNetworkLinkIP.newTunnelingLink(local, remote, useNat, medium); } private KNXNetworkLink createRoutingLink(InetSocketAddress local, KNXMediumSettings medium) throws KNXException { logger.info("{}Connect using routing (multicast:{}){}", - ansi().fgBright(YELLOW), KNXnetIPRouting.DefaultMulticast, ansi().reset()); + ansi().fgBright(INFO), KNXnetIPRouting.DefaultMulticast, ansi().reset()); return KNXNetworkLinkIP.newRoutingLink(local.getAddress(), KNXnetIPRouting.DefaultMulticast, medium); } @@ -175,7 +175,7 @@ private KNXNetworkLink doOpenLink() throws KNXException, return testLink; } catch (final KNXException e) { cliOptions.setTunnelingV1isSet(false); - logger.info("{}failed with {}: {}{}", ansi().fgBright(YELLOW), e.getClass().getSimpleName(), e.getMessage(), + logger.info("{}failed with {}: {}{}", ansi().fgBright(INFO), e.getClass().getSimpleName(), e.getMessage(), ansi().reset()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index d45e7bb6..1c89d29e 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -17,7 +17,7 @@ import org.slf4j.LoggerFactory; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; import static org.selfbus.updater.logging.LoggingManager.CONSOLE_APPENDER_NAME; import static org.selfbus.updater.Utils.shortenPath; @@ -79,13 +79,13 @@ public static void main(final String[] args) { } try { - logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), ToolInfo.getToolAndVersion(), ansi().reset()); + logger.info("{}{}{}", ansi().fgBright(OK).bold(), ToolInfo.getToolAndVersion(), ansi().reset()); // read in user-supplied command line options CliOptions options = new CliOptions(args, ToolInfo.getToolJarName() , "Selfbus KNX-Firmware update tool options", ""); if (options.getVersionIsSet()) { - logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), Credits.getAsciiLogo(), ansi().reset()); - logger.info("{}{}{}", ansi().fgBright(GREEN).bold(), Credits.getAuthors(), ansi().reset()); + logger.info("{}{}{}", ansi().fgBright(OK).bold(), Credits.getAsciiLogo(), ansi().reset()); + logger.info("{}{}{}", ansi().fgBright(OK).bold(), Credits.getAuthors(), ansi().reset()); ToolInfo.showVersion(); finalizeJansi(); return; @@ -186,7 +186,7 @@ public void run() { if (!hexFileName.isEmpty()) { // check if the firmware file exists if (!Utils.fileExists(hexFileName)) { - logger.error("{}File '{}' does not exist!{}", ansi().fgBright(RED), cliOptions.getFileName(), ansi().reset()); + logger.error("{}File '{}' does not exist!{}", ansi().fgBright(WARN), cliOptions.getFileName(), ansi().reset()); throw new UpdaterException(String.format("File '%s' does not exist!", cliOptions.getFileName())); } // Load Firmware hex file @@ -194,14 +194,14 @@ public void run() { newFirmware = BinImage.readFromHex(hexFileName); // Check for APP_VERSION string in new firmware if (newFirmware.getAppVersion().isEmpty()) { - logger.warn("{} Missing APP_VERSION string in new firmware!{}", ansi().fgBright(RED), ansi().reset()); + logger.warn("{} Missing APP_VERSION string in new firmware!{}", ansi().fgBright(WARN), ansi().reset()); throw new UpdaterException("Missing APP_VERSION string in firmware!"); } } else { logger.info("{}No firmware file (*.hex) specified! Specify with --{}{}", - ansi().fgBright(RED), CliOptions.OPT_LONG_FILENAME, ansi().reset()); - logger.info("{}Reading only device information{}", ansi().fgBright(YELLOW), ansi().reset()); + ansi().fgBright(WARN), CliOptions.OPT_LONG_FILENAME, ansi().reset()); + logger.info("{}Reading only device information{}", ansi().fgBright(INFO), ansi().reset()); } dm = DeviceManagementFactory.getDeviceManagement(cliOptions); @@ -226,7 +226,7 @@ public void run() { if ((cliOptions.getDumpFlashStartAddress() >= 0) && (cliOptions.getDumpFlashEndAddress() >= 0)) { logger.warn("{}Dumping flash content range {} to bootloader's serial port.{}", - ansi().fgBright(GREEN), + ansi().fgBright(OK), String.format("0x%04X-0x%04X", cliOptions.getDumpFlashStartAddress(), cliOptions.getDumpFlashEndAddress()), ansi().reset()); dm.dumpFlashRange(cliOptions.getDumpFlashStartAddress(), cliOptions.getDumpFlashEndAddress()); @@ -244,9 +244,9 @@ public void run() { logger.debug("Requesting APP_VERSION"); String appVersion = dm.requestAppVersionString(); if (appVersion.isEmpty()) { - logger.info("Current APP_VERSION: {}invalid{} ", ansi().fgBright(RED), ansi().reset()); + logger.info("Current APP_VERSION: {}invalid{} ", ansi().fgBright(WARN), ansi().reset()); } else { - logger.info("Current APP_VERSION: {}{}{}", ansi().fgBright(GREEN), appVersion, ansi().reset()); + logger.info("Current APP_VERSION: {}{}{}", ansi().fgBright(OK), appVersion, ansi().reset()); } // From here on we need a valid firmware @@ -264,28 +264,28 @@ public void run() { BinImage imageCache = BinImage.copyFromArray(newFirmware.getBinData(), newFirmware.startAddress()); imageCache.writeToBinFile(cacheFileName); - logger.info("File APP_VERSION : {}{}{}", ansi().fgBright(GREEN), newFirmware.getAppVersion(), ansi().reset()); + logger.info("File APP_VERSION : {}{}{}", ansi().fgBright(OK), newFirmware.getAppVersion(), ansi().reset()); // Check if FW image has correct offset for MCUs bootloader size if (newFirmware.startAddress() < bootLoaderIdentity.applicationFirstAddress()) { logger.error("{} Error! The specified firmware image would overwrite parts of the bootloader. Check FW offset setting in the linker!{}", - ansi().fgBright(RED), ansi().reset()); - logger.error("{} Firmware needs to start at or beyond 0x{}{}", ansi().fgBright(RED), + ansi().fgBright(WARN), ansi().reset()); + logger.error("{} Firmware needs to start at or beyond 0x{}{}", ansi().fgBright(WARN), String.format("%04X", bootLoaderIdentity.applicationFirstAddress()), ansi().reset()); throw new UpdaterException("Firmware offset not correct!"); } else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddress()) { - logger.debug("{} Firmware starts directly beyond bootloader.{}", ansi().fgBright(GREEN), ansi().reset()); + logger.debug("{} Firmware starts directly beyond bootloader.{}", ansi().fgBright(OK), ansi().reset()); } else { logger.debug("{} Info: There are {} bytes of unused flash between bootloader and firmware.{}", - ansi().fgBright(YELLOW), newFirmware.startAddress() - bootLoaderIdentity.applicationFirstAddress(), + ansi().fgBright(INFO), newFirmware.startAddress() - bootLoaderIdentity.applicationFirstAddress(), ansi().reset()); } if (cliOptions.getEraseFullFlashIsSet()) { logger.warn("{}Deleting the entire flash except from the bootloader itself!{}", - ansi().fgBright(RED), ansi().reset()); + ansi().fgBright(WARN), ansi().reset()); dm.eraseFlash(); } @@ -296,7 +296,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } else { logger.warn("{} BootDescriptor is not valid -> switching to full mode{}", - ansi().fgBright(RED), ansi().reset()); + ansi().fgBright(WARN), ansi().reset()); } } @@ -309,7 +309,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres if (!dm.setBlockSize(cliOptions.getBlockSize())) { logger.info("{}Connected bootloader doesn't support block size {}. Using {} bytes.{}", - ansi().fgBright(YELLOW), cliOptions.getBlockSize(), dm.getBlockSize(), ansi().reset()); + ansi().fgBright(INFO), cliOptions.getBlockSize(), dm.getBlockSize(), ansi().reset()); } if (!cliOptions.getNoFlashIsSet()) { // is flashing firmware disabled? for debugging use only! @@ -317,7 +317,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres ResponseResult resultTotal; if (diffMode && FlashDiffMode.isInitialized()) { logger.warn("{}Differential mode is EXPERIMENTAL -> Use with caution.{}", - ansi().fgBright(RED), ansi().reset()); + ansi().fgBright(WARN), ansi().reset()); resultTotal = FlashDiffMode.doDifferentialFlash(dm, newFirmware.startAddress(), newFirmware.getBinData()); } else { @@ -336,7 +336,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } else { logger.warn("--{} => {}only boot description block will be written{}", CliOptions.OPT_LONG_NO_FLASH, - ansi().fgBright(RED), ansi().reset()); + ansi().fgBright(WARN), ansi().reset()); } BootDescriptor newBootDescriptor = new BootDescriptor(newFirmware.startAddress(), @@ -348,14 +348,14 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres deviceInfo = cliOptions.getDevicePhysicalAddress().toString(); } logger.info("Finished programming device {}{}{} with '{}{}{}'", - ansi().fgBright(YELLOW), deviceInfo, ansi().reset(), - ansi().fgBright(YELLOW), shortenPath(cliOptions.getFileName(), 1), ansi().reset()); + ansi().fgBright(INFO), deviceInfo, ansi().reset(), + ansi().fgBright(INFO), shortenPath(cliOptions.getFileName(), 1), ansi().reset()); dm.restartProgrammingDevice(); dm.close(); if (newFirmware.getAppVersion().contains(BootloaderUpdater.BOOTLOADER_UPDATER_ID_STRING)) { logger.info("{}Wait {} second(s) for Bootloader Updater to finish its job{}", - ansi().fgBright(GREEN), + ansi().fgBright(OK), String.format("%.2f", BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS / 1000.0f), ansi().reset()); Thread.sleep(BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS); @@ -399,9 +399,9 @@ public String requestUid() throws KNXException, UpdaterException, UnknownHostExc dm.requestBootDescriptor(); String appVersion = dm.requestAppVersionString(); if (appVersion.isEmpty()) { - logger.info("APP_VERSION: {}invalid{} ", ansi().fgBright(RED), ansi().reset()); + logger.info("APP_VERSION: {}invalid{} ", ansi().fgBright(WARN), ansi().reset()); } else { - logger.info("APP_VERSION: {}{}{}", ansi().fgBright(GREEN), appVersion, ansi().reset()); + logger.info("APP_VERSION: {}{}{}", ansi().fgBright(OK), appVersion, ansi().reset()); } if (cliOptions.getDevicePhysicalAddress() != null) { @@ -417,7 +417,7 @@ public String requestUid() throws KNXException, UpdaterException, UnknownHostExc } catch (UpdaterException | KNXException e) { logger.error("{}An error occurred while retrieving the UID. {}{}{}", - ansi().fgBright(RED), System.lineSeparator(), e, ansi().reset()); + ansi().fgBright(WARN), System.lineSeparator(), e, ansi().reset()); throw e; } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java index 602315d8..15f09b67 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java @@ -3,7 +3,7 @@ import org.selfbus.updater.Utils; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; /** * Holds Application Boot Block Descriptor the MCU's bootloader @@ -86,10 +86,10 @@ private short long2short(long a) { public String toString() { String res; if (valid()) { - res = String.format("%s valid%s", ansi().fgBright(GREEN), ansi().reset()); + res = String.format("%s valid%s", ansi().fgBright(OK), ansi().reset()); } else { - res = String.format("%sinvalid%s", ansi().fgBright(RED), ansi().reset()); + res = String.format("%sinvalid%s", ansi().fgBright(WARN), ansi().reset()); } res = String.format("%s, 0x%04X-0x%04X, %5d byte(s), crc32 0x%08X, APP_VERSION pointer: 0x%04X", res, long2short(startAddress()), long2short(endAddress()), length(), crc32(), long2short(appVersionAddress())); diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java index cf80a0b2..33e2f007 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java @@ -3,7 +3,7 @@ import org.selfbus.updater.Utils; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; public class BootloaderStatistic { public static final int THRESHOLD_DISCONNECT = 1; @@ -26,9 +26,9 @@ public static BootloaderStatistic fromArray(byte[] parse) { private static String toColoredThreshold(int value, int threshold) { String ansiColor; if (value > threshold) { - ansiColor = ansi().fgBright(YELLOW).toString(); + ansiColor = ansi().fgBright(INFO).toString(); } else { - ansiColor = ansi().fgBright(GREEN).toString(); + ansiColor = ansi().fgBright(OK).toString(); } return String.format("%s%2d%s", ansiColor, value, ansi().reset()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 625e4943..b185bc95 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -26,7 +26,7 @@ import java.util.Arrays; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; import static org.selfbus.updater.Mcu.MAX_FLASH_ERASE_TIMEOUT; import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_ONLY; import static org.selfbus.updater.logging.Markers.CONSOLE_GUI_NO_NEWLINE; @@ -129,7 +129,7 @@ public void restartProgrammingDevice() private void waitRestartTime(int restartTimeSeconds) throws InterruptedException { while (restartTimeSeconds > 0) { Thread.sleep(1000); - logger.info(CONSOLE_GUI_NO_NEWLINE, String.format("%s.%s", ansi().fgBright(GREEN), ansi().reset())); + logger.info(CONSOLE_GUI_NO_NEWLINE, String.format("%s.%s", ansi().fgBright(OK), ansi().reset())); restartTimeSeconds--; } logger.info(CONSOLE_GUI_ONLY, ""); // Just a new line @@ -146,14 +146,14 @@ public void restartDeviceToBootloader(IndividualAddress device) throws Interrupt logger.info("Restarting device {} into bootloader", device); restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); logger.info("Device {} reported {}{}{} second(s) for restarting", - device, ansi().fgBright(GREEN), restartProcessTime, ansi().reset()); + device, ansi().fgBright(OK), restartProcessTime, ansi().reset()); waitRestartTime(restartProcessTime); } catch (final KNXException e) { logger.info("{}Restart state of device {} unknown. {}{}", - ansi().fgBright(RED), device, e.getMessage(), ansi().reset()); + ansi().fgBright(WARN), device, e.getMessage(), ansi().reset()); logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 logger.info("Waiting {}{}{} seconds for device {} to restart", - ansi().fgBright(GREEN), restartProcessTime, ansi().reset(), device); + ansi().fgBright(OK), restartProcessTime, ansi().reset(), device); waitRestartTime(restartProcessTime); } } @@ -199,14 +199,14 @@ public BootloaderIdentity requestBootloaderIdentity() long minMajorVersion = result[DATA_POSITION] & 0xff; long minMinorVersion = result[DATA_POSITION + 1] & 0xff; logger.error("{}Selfbus Updater version {} is not compatible. Please update to version {}.{} or higher.{}", - ansi().fgBright(RED), ToolInfo.getVersion(), minMajorVersion, minMinorVersion, ansi().reset()); + ansi().fgBright(WARN), ToolInfo.getVersion(), minMajorVersion, minMinorVersion, ansi().reset()); } restartProgrammingDevice(); throw new UpdaterException("Requesting Bootloader Identity failed!"); } BootloaderIdentity bl = BootloaderIdentity.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); - logger.info("Device Bootloader: {}{}{}", ansi().fgBright(YELLOW), bl, ansi().reset()); + logger.info("Device Bootloader: {}{}{}", ansi().fgBright(INFO), bl, ansi().reset()); boolean versionsMatch = (bl.versionMajor() > ToolInfo.minMajorVersionBootloader()) || ((bl.versionMajor() == ToolInfo.minMajorVersionBootloader()) && (bl.versionMinor() >= ToolInfo.minMinorVersionBootloader())); @@ -214,7 +214,7 @@ public BootloaderIdentity requestBootloaderIdentity() if (!versionsMatch) { logger.error("{}Bootloader version {} is not compatible, please update Bootloader to version {} or higher{}", - ansi().fgBright(RED), bl.getVersion(), ToolInfo.minVersionBootloader(), ansi().reset()); + ansi().fgBright(WARN), bl.getVersion(), ToolInfo.minVersionBootloader(), ansi().reset()); throw new UpdaterException("Bootloader version not compatible!"); } return bl; @@ -356,7 +356,7 @@ private void printProgressInfo(ProgressInfo progressInfo) { // append one space, just in case an exception message may come up String logText = String.format("%s%s%s%s ", AnsiCursor.off(), - ansi().cursorToColumn(1).fgBright(GREEN).a(SpinningCursor.getNext()).reset(), + ansi().cursorToColumn(1).fgBright(OK).a(SpinningCursor.getNext()).reset(), progressInfo, AnsiCursor.on()); logger.info(CONSOLE_GUI_NO_NEWLINE, logText); @@ -404,7 +404,7 @@ public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, byte[] result = sendWithRetry(UPDCommand.REQUEST_STATISTIC, new byte[0], getMaxUpdCommandRetry()).data(); UPDCommand command = UPDCommand.tryFromByteArray(result); if (command != UPDCommand.RESPONSE_STATISTIC) { - logger.warn("Requesting Bootloader statistic {}failed!{}", ansi().fgBright(RED), ansi().reset()); + logger.warn("Requesting Bootloader statistic {}failed!{}", ansi().fgBright(WARN), ansi().reset()); return null; } BootloaderStatistic blStatistic = BootloaderStatistic.fromArray(Arrays.copyOfRange(result, DATA_POSITION, result.length)); @@ -422,7 +422,7 @@ private boolean isLinkAlive() { private void handleKNXException(final UPDCommand command, final KNXException e) throws UpdaterException, InterruptedException { - logger.warn("{}{}{} ({} {})", ansi().fgBright(RED), e.getMessage(), ansi().reset(), + logger.warn("{}{}{} ({} {})", ansi().fgBright(WARN), e.getMessage(), ansi().reset(), e.getClass().getSimpleName(), command); try { reconnect(cliOptions.getReconnectMs()); @@ -485,7 +485,7 @@ else if ((devices.length == 1) && (progDeviceAddr != null) && (progDeviceAddr.eq return; } logger.warn("{}{} Device(s) in bootloader/programming mode: {}{}", - ansi().fgBright(RED), devices.length, Arrays.stream(devices).toArray(), ansi().reset()); + ansi().fgBright(WARN), devices.length, Arrays.stream(devices).toArray(), ansi().reset()); if (devices.length == 0) { throw new UpdaterException("No device in programming mode."); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 32062f3c..b102ed28 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -33,7 +33,7 @@ import java.util.List; import static java.awt.Font.PLAIN; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; import static org.fusesource.jansi.Ansi.ansi; import static org.selfbus.updater.CliOptions.*; import static org.selfbus.updater.gui.CliConverter.argument; @@ -386,7 +386,7 @@ private CliOptions getCliOptions() throws KNXFormatException, ParseException { } private void displayCommandLine(CliOptions cliOptions) { - logger.info("{}java -jar {} {}{}", ansi().fgBright(YELLOW), ToolInfo.getToolJarName(), + logger.info("{}java -jar {} {}{}", ansi().fgBright(INFO), ToolInfo.getToolJarName(), cliOptions.reconstructCommandLine(), ansi().reset()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java index 0a642c5b..c2249345 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiUncaughtExceptionHandler.java @@ -7,7 +7,7 @@ import java.io.PrintWriter; import java.io.StringWriter; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; import static org.fusesource.jansi.Ansi.ansi; public class GuiUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { @@ -28,7 +28,7 @@ public GuiUncaughtExceptionHandler(JFrame parentFrame) { @Override public void uncaughtException(Thread thread, Throwable throwable) { logger.error("{}Uncaught exception in thread {} {}{}", - ansi().fgBright(RED) , thread.getName(), throwable.toString(), ansi().reset(), throwable); + ansi().fgBright(WARN) , thread.getName(), throwable.toString(), ansi().reset(), throwable); // todo see logback issue #876 // https://github.com/qos-ch/logback/issues/876 diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/Color.java b/firmware_updater/updater/src/org/selfbus/updater/logging/Color.java new file mode 100644 index 00000000..35a19ba8 --- /dev/null +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/Color.java @@ -0,0 +1,20 @@ +package org.selfbus.updater.logging; + +import org.fusesource.jansi.Ansi; + +import static org.fusesource.jansi.Ansi.Color.*; + +public final class Color { + @SuppressWarnings("unused") + private Color() {} + + // old style colors + public static final Ansi.Color INFO = YELLOW; + public static final Ansi.Color OK = GREEN; + public static final Ansi.Color WARN = RED; + + //todo more red/green friendly? +// public static final Ansi.Color INFO = WHITE; +// public static final Ansi.Color OK = YELLOW; +// public static final Ansi.Color WARN = MAGENTA; +} \ No newline at end of file diff --git a/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java index 00eed5d4..2791c6af 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java +++ b/firmware_updater/updater/src/org/selfbus/updater/mode/differential/FlashDiff.java @@ -11,9 +11,6 @@ import java.util.List; import java.util.zip.CRC32; -import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; - public class FlashDiff { private final static Logger logger = LoggerFactory.getLogger(FlashDiff.class); private static final int MINIMUM_PATTERN_LENGTH = 6; // less that this is not efficient (metadata would be larger than data) diff --git a/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java b/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java index ac5aba69..16ff263d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java @@ -2,9 +2,8 @@ import org.fusesource.jansi.Ansi; -import static org.fusesource.jansi.Ansi.Color.GREEN; -import static org.fusesource.jansi.Ansi.Color.YELLOW; import static org.fusesource.jansi.Ansi.ansi; +import static org.selfbus.updater.logging.Color.*; public class ProgressInfo { private long startTimeMs; @@ -60,10 +59,10 @@ private String getSpeed(boolean averageSpeed) { } Ansi.Color color; if (bytesPerSecond >= 50.0) { - color = GREEN; + color = OK; } else { - color = YELLOW; + color = INFO; } return String.format("%s%5.1f%s", ansi().fgBright(color), bytesPerSecond, ansi().reset()); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java index 073cce2c..a0202fd4 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UPDProtocol.java @@ -4,7 +4,7 @@ import org.slf4j.LoggerFactory; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; +import static org.selfbus.updater.logging.Color.*; /** * Implementation of the UPD/UDP protocol handling @@ -40,7 +40,7 @@ public static UDPResult checkResult(byte[] result, boolean verbose) { UDPResult udpResult = UDPResult.valueOf(result[DATA_POSITION]); if (udpResult.isError()) { - logger.error("{}{}{} ({})", ansi().fgBright(RED), udpResult.getDescription(), ansi().reset(), udpResult.name()); + logger.error("{}{}{} ({})", ansi().fgBright(WARN), udpResult.getDescription(), ansi().reset(), udpResult.name()); } else { if (verbose) { logger.trace("done ({})", udpResult.name()); diff --git a/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java b/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java index 39cba2cb..aef8535e 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/BootloaderStatisticTest.java @@ -5,13 +5,14 @@ import static org.junit.jupiter.api.Assertions.*; import static org.fusesource.jansi.Ansi.*; -import static org.fusesource.jansi.Ansi.Color.*; + +import static org.selfbus.updater.logging.Color.*; public class BootloaderStatisticTest { @Test public void testToString() { - String colorOK = ansi().fgBright(GREEN).toString(); - String colorWarn = ansi().fgBright(YELLOW).toString(); + String colorOK = ansi().fgBright(OK).toString(); + String colorWarn = ansi().fgBright(INFO).toString(); String colorReset = ansi().reset().toString(); String info1 = ""; String info2 = " "; From 4526e6166bb307bdc3ad09c0f7ddac588cdb117a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 19:38:23 +0100 Subject: [PATCH 302/359] [Updater] Delete obsolete todo `dm.sendWithRetry(..)` reconnects already on connection failure. --- .../updater/src/org/selfbus/updater/FlashDiffMode.java | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java index 92e7f7d8..716963cd 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java +++ b/firmware_updater/updater/src/org/selfbus/updater/FlashDiffMode.java @@ -84,7 +84,6 @@ public static boolean isInitialized() { // Differential update routine public static ResponseResult doDifferentialFlash(DeviceManagement dm, long startAddress, byte[] binData) throws KNXTimeoutException, KNXLinkClosedException, InterruptedException, UpdaterException { - ///\todo add connection reset and sending again on failure, like in full flash mode if (!isInitialized()) { throw new UpdaterException("Differential mode not initialized!"); } From 30369865b34c5ef2a3c3d171ade54eec3f19d5ea Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sat, 15 Feb 2025 23:46:42 +0100 Subject: [PATCH 303/359] [Updater] Fix typo --- .../updater/src/org/selfbus/updater/upd/UDPResult.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java index de9f6316..c0d927ab 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java +++ b/firmware_updater/updater/src/org/selfbus/updater/upd/UDPResult.java @@ -40,7 +40,7 @@ public enum UDPResult { /** Command is not defined */ UNKNOWN_COMMAND((byte)0x5f, "Command unknown", true), /** CRC calculated on the device and by the updater don't match */ - CRC_ERROR((byte)0x5e, "CRC error, try option -full for a clean and full flash", true), + CRC_ERROR((byte)0x5e, "CRC error, try option --full for a clean and full flash", true), /** Specified address cannot be programmed */ ADDRESS_NOT_ALLOWED_TO_FLASH((byte)0x5d, "Address not allowed to flash", true), /** The specified sector cannot be erased */ From a8e9dc85a29e7f05359c08bcca52ea020275f8e0 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 16 Feb 2025 00:06:53 +0100 Subject: [PATCH 304/359] [Updater] Use consistent colors for gui and console These color choices are hopefully a good compromise for everyone. --- .../selfbus/updater/gui/ConColorsToStyledDoc.java | 12 ++++-------- .../src/org/selfbus/updater/logging/Color.java | 5 ----- .../updater/gui/ConColorsToStyledDocTests.java | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java index 99df83fd..ef6f3429 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/ConColorsToStyledDoc.java @@ -278,8 +278,7 @@ private static Style colorCodeToStyle(String [] colorCodes) { StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.green)); break; case 33: // Foreground color yellow - // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO - StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.orange)); + StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.yellow)); break; case 34: // Foreground color blue StyleConstants.setForeground(stringStyle, normalColor(java.awt.Color.blue)); @@ -311,8 +310,7 @@ private static Style colorCodeToStyle(String [] colorCodes) { StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.green)); break; case 43: // Background color yellow - // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO - StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.orange)); + StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.yellow)); break; case 44: // Background color blue StyleConstants.setBackground(stringStyle, normalColor(java.awt.Color.blue)); @@ -348,8 +346,7 @@ private static Style colorCodeToStyle(String [] colorCodes) { StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.green)); break; case 93: // Bright foreground color yellow - // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO - StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.orange)); + StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.yellow)); break; case 94: // Bright foreground color blue StyleConstants.setForeground(stringStyle, brightColor(java.awt.Color.blue)); @@ -383,8 +380,7 @@ private static Style colorCodeToStyle(String [] colorCodes) { StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.green)); break; case 103: // Bright background color yellow - // java.awt.Color.yellow would be correct, but java.awt.Color.orange just looks better IMHO - StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.orange)); + StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.yellow)); break; case 104: // Bright background color blue StyleConstants.setBackground(stringStyle, brightColor(java.awt.Color.blue)); diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/Color.java b/firmware_updater/updater/src/org/selfbus/updater/logging/Color.java index 35a19ba8..ce86f395 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/Color.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/Color.java @@ -12,9 +12,4 @@ private Color() {} public static final Ansi.Color INFO = YELLOW; public static final Ansi.Color OK = GREEN; public static final Ansi.Color WARN = RED; - - //todo more red/green friendly? -// public static final Ansi.Color INFO = WHITE; -// public static final Ansi.Color OK = YELLOW; -// public static final Ansi.Color WARN = MAGENTA; } \ No newline at end of file diff --git a/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java b/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java index 9840c3fa..37cbc697 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java +++ b/firmware_updater/updater/test/org/selfbus/updater/gui/ConColorsToStyledDocTests.java @@ -107,7 +107,7 @@ public void testBackgroundColors() { assertStyleForeground(foregroundColor); testColorCodeToStyle(new String[]{"43"}); - assertStyleBackground(normalColor(java.awt.Color.orange)); + assertStyleBackground(normalColor(java.awt.Color.yellow)); assertStyleForeground(foregroundColor); } From a0b9b729a1f979f4cd7f77a5772806893c953d6d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 16 Feb 2025 00:26:34 +0100 Subject: [PATCH 305/359] [Updater] Discover on startup only if no gateway is set --- .../src/org/selfbus/updater/gui/GuiMain.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index b102ed28..9d06a2e3 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -429,11 +429,28 @@ public void windowClosing(WindowEvent e) { } }); setVisible(true); - if (textBoxKnxGatewayIpAddr.getText().isEmpty()) { + if (!isGatewaySet()) { new Thread(this::loadKnxIpInterfacesAndFillComboBox).start(); } } + private boolean isGatewaySet() { + if (!textBoxKnxGatewayIpAddr.getText().isEmpty()) { + return true; + } + if (!textFieldSerial.getText().isEmpty()) { + return true; + } + + //todo implement USB check ala + /* + if (!textFieldUSB.getText().isEmpty()) { + return true; + } + */ + return !textFieldTpuart.getText().isEmpty(); + } + private void loadKnxIpInterfacesAndFillComboBox() { reloadGatewaysButton.setEnabled(false); // ActionListener löschen und nach dem Füllen der ComboBox wieder hinzufügen From 67866da9c70f02d1fd9bd70dfaee372100b4e6f3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 16 Feb 2025 00:28:12 +0100 Subject: [PATCH 306/359] [Updater] Add warning for experimental TPUART support --- .../updater/src/org/selfbus/updater/CliOptions.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index e860980b..f1805b3a 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -862,6 +862,10 @@ public String getTpuartSerialPort() { private void setTpuartSerialPort(String tpuartSerialPort) { this.tpuartSerialPort = tpuartSerialPort; logger.debug("{}={}", OPT_LONG_TPUART, getTpuartSerialPort()); + if (!tpuartSerialPort.isBlank()) { + logger.warn("{}TPUART support is experimental!{} Set TPUARTs own address with --{}", + ansi().fgBright(WARN), ansi().reset(), OPT_LONG_OWN_ADDRESS); + } } public boolean getTunnelingV2isSet() { From 317b132deeff6bda5a230e0b4427cac6aa085247 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 16 Feb 2025 00:36:35 +0100 Subject: [PATCH 307/359] =?UTF-8?q?[Updater]=20No=20todo,=20it=C2=B4s=20go?= =?UTF-8?q?od=20to=20ack=20our=20own=20address?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `linkTpuart.addAddress` adds our own (`--own x.y.z, default 0.0.0`) address to the set of addresses which we will ack on L2. --- firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java index e8989e99..de694c3d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBKNXLink.java @@ -129,7 +129,7 @@ private KNXNetworkLink doOpenLink() throws KNXException, } else if (!cliOptions.getTpuartSerialPort().isEmpty()) { // create TPUART network link KNXNetworkLinkTpuart linkTpuart = new KNXNetworkLinkTpuart(cliOptions.getTpuartSerialPort(), medium, Collections.emptyList()); - linkTpuart.addAddress(cliOptions.getOwnPhysicalAddress()); //\todo check if this is rly needed + linkTpuart.addAddress(cliOptions.getOwnPhysicalAddress()); return linkTpuart; } else if (!cliOptions.getUsbVendorIdAndProductId().isEmpty()) { // create USB network link From 1435b09e6c653f99e25e9f683b92e8b9765de079 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 16 Feb 2025 01:25:08 +0100 Subject: [PATCH 308/359] [Updater] Delete unclear todo and extend test --- .../updater/test/org/selfbus/updater/UtilsTest.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/firmware_updater/updater/test/org/selfbus/updater/UtilsTest.java b/firmware_updater/updater/test/org/selfbus/updater/UtilsTest.java index efb6dcec..e86195aa 100644 --- a/firmware_updater/updater/test/org/selfbus/updater/UtilsTest.java +++ b/firmware_updater/updater/test/org/selfbus/updater/UtilsTest.java @@ -7,15 +7,14 @@ public class UtilsTest { @Test public void testStreamToLong() { - // a full range test takes about 40s, so try to avoid it - // for lower two bytes we only check the signed bit range of 126..129 - ///\todo implement test of offset + // In 2025 this full test runs in under 6s. + // Let´s meet in the "middle" (~1s). byte[] testStream = new byte[]{0, 0, 0, 0}; - int start0 = 126; // min. 0, max. 255 - int end0 = 129; // min. 0, max. 255 + int start0 = 90; // min. 0, max. 255 + int end0 = 150; // min. 0, max. 255 - int start1 = 126; // min. 0, max. 255 - int end1 = 129; // min. 0, max. 255 + int start1 = 30; // min. 0, max. 255 + int end1 = 220; // min. 0, max. 255 int start2 = 0; // min. 0, max. 255 int end2 = 255; // min. 0, max. 255 From 81dc9c1c72c1847378c558bf7f8439b781e4f4c0 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Feb 2025 17:28:56 +0100 Subject: [PATCH 309/359] [Updater] Rename Operation to Update --- .../updater/src/org/selfbus/updater/Updater.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 1c89d29e..4cd29cdc 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -361,16 +361,16 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres Thread.sleep(BootloaderUpdater.BOOTLOADER_UPDATER_MAX_RESTART_TIME_MS); } - logger.info("Operation finished successfully."); + logger.info("Update finished successfully."); } catch (final InterruptedException | IllegalStateException e) { Thread.currentThread().interrupt(); - logger.info("{}Operation canceled.", System.lineSeparator()); + logger.info("{}Update canceled.", System.lineSeparator()); logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 } catch (Throwable e) { logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 - logger.error("Operation did not finish."); + logger.error("Update did not finish."); } finally { if (dm != null) { @@ -411,7 +411,7 @@ public String requestUid() throws KNXException, UpdaterException, UnknownHostExc return uid; } catch (final InterruptedException e) { - logger.info("Operation requestUid canceled."); + logger.info("requestUid canceled."); Thread.currentThread().interrupt(); return ""; } From 2243af280ca119ed3c94469a6ee2db0833f4ed03 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Feb 2025 21:48:25 +0100 Subject: [PATCH 310/359] [Updater] Prettier logging --- .../selfbus/updater/devicemgnt/DeviceManagement.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index b185bc95..bb705b50 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -145,8 +145,15 @@ public void restartDeviceToBootloader(IndividualAddress device) throws Interrupt try (Destination dest = this.mc.createDestination(device, true, false, false)) { logger.info("Restarting device {} into bootloader", device); restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); - logger.info("Device {} reported {}{}{} second(s) for restarting", - device, ansi().fgBright(OK), restartProcessTime, ansi().reset()); + String timeStr; + if (restartProcessTime <= 1) { + timeStr = "second"; + } + else { + timeStr = "seconds"; + } + logger.info(CONSOLE_GUI_NO_NEWLINE, "Device {} reported {}{}{} {} for restarting", + device, ansi().fgBright(OK), restartProcessTime, ansi().reset(), timeStr); waitRestartTime(restartProcessTime); } catch (final KNXException e) { logger.info("{}Restart state of device {} unknown. {}{}", From 887ef9178ca614bde89065727eee83cc568f84e4 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 20 Feb 2025 03:02:27 +0100 Subject: [PATCH 311/359] [Updater] No new line for 6 seconds wait "ticker" --- .../src/org/selfbus/updater/devicemgnt/DeviceManagement.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index bb705b50..6da3ca6d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -154,13 +154,14 @@ public void restartDeviceToBootloader(IndividualAddress device) throws Interrupt } logger.info(CONSOLE_GUI_NO_NEWLINE, "Device {} reported {}{}{} {} for restarting", device, ansi().fgBright(OK), restartProcessTime, ansi().reset(), timeStr); - waitRestartTime(restartProcessTime); } catch (final KNXException e) { logger.info("{}Restart state of device {} unknown. {}{}", ansi().fgBright(WARN), device, e.getMessage(), ansi().reset()); logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 - logger.info("Waiting {}{}{} seconds for device {} to restart", + logger.info(CONSOLE_GUI_NO_NEWLINE, "Waiting {}{}{} seconds for device {} to restart", ansi().fgBright(OK), restartProcessTime, ansi().reset(), device); + } + finally { waitRestartTime(restartProcessTime); } } From 3d10e53f3507d5007002a4835656fc29cbe1e619 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 20 Feb 2025 03:07:48 +0100 Subject: [PATCH 312/359] [Updater] No full stacktrace for `UpdaterException` and `KNXException` - More detailed message of no/wrong device(s) in bootloader/programming mode - Added little help message for `KNXPortClosedException: error sending report over USB` with usb interface on windows --- .../src/org/selfbus/updater/Updater.java | 34 +++++++++++----- .../updater/devicemgnt/DeviceManagement.java | 40 +++++++++++++------ 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 4cd29cdc..f7f47461 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -22,6 +22,7 @@ import static org.selfbus.updater.Utils.shortenPath; import org.selfbus.updater.gui.GuiMain; +import tuwien.auto.calimero.serial.KNXPortClosedException; import java.net.UnknownHostException; @@ -186,7 +187,6 @@ public void run() { if (!hexFileName.isEmpty()) { // check if the firmware file exists if (!Utils.fileExists(hexFileName)) { - logger.error("{}File '{}' does not exist!{}", ansi().fgBright(WARN), cliOptions.getFileName(), ansi().reset()); throw new UpdaterException(String.format("File '%s' does not exist!", cliOptions.getFileName())); } // Load Firmware hex file @@ -194,7 +194,6 @@ public void run() { newFirmware = BinImage.readFromHex(hexFileName); // Check for APP_VERSION string in new firmware if (newFirmware.getAppVersion().isEmpty()) { - logger.warn("{} Missing APP_VERSION string in new firmware!{}", ansi().fgBright(WARN), ansi().reset()); throw new UpdaterException("Missing APP_VERSION string in firmware!"); } } @@ -268,19 +267,17 @@ public void run() { // Check if FW image has correct offset for MCUs bootloader size if (newFirmware.startAddress() < bootLoaderIdentity.applicationFirstAddress()) { - logger.error("{} Error! The specified firmware image would overwrite parts of the bootloader. Check FW offset setting in the linker!{}", + logger.error("{}Error! The specified firmware image would overwrite parts of the bootloader. Check FW offset setting in the linker!{}", ansi().fgBright(WARN), ansi().reset()); - logger.error("{} Firmware needs to start at or beyond 0x{}{}", ansi().fgBright(WARN), - String.format("%04X", bootLoaderIdentity.applicationFirstAddress()), ansi().reset()); - throw new UpdaterException("Firmware offset not correct!"); + throw new UpdaterException(String.format("Firmware needs to start at or beyond 0x%04X", + bootLoaderIdentity.applicationFirstAddress())); } else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddress()) { - logger.debug("{} Firmware starts directly beyond bootloader.{}", ansi().fgBright(OK), ansi().reset()); + logger.debug("Firmware starts directly beyond bootloader."); } else { - logger.debug("{} Info: There are {} bytes of unused flash between bootloader and firmware.{}", - ansi().fgBright(INFO), newFirmware.startAddress() - bootLoaderIdentity.applicationFirstAddress(), - ansi().reset()); + logger.debug("Info: There are {} bytes of unused flash between bootloader and firmware.", + newFirmware.startAddress() - bootLoaderIdentity.applicationFirstAddress()); } if (cliOptions.getEraseFullFlashIsSet()) { @@ -368,7 +365,22 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres logger.info("{}Update canceled.", System.lineSeparator()); logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 } - catch (Throwable e) { + catch (final KNXPortClosedException e) { + logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + logger.error("Update failed."); + if (e.getMessage().contains("error sending report over USB") && + (!cliOptions.getUsbVendorIdAndProductId().isBlank()) && + System.getProperty("os.name").startsWith("Windows")) { + logger.info("{}Make sure the USB Interface {} uses the WinUSB driver. Checkout https://zadig.akeo.ie{}", + ansi().fgBright(WARN), cliOptions.getUsbVendorIdAndProductId(), ansi().reset() ); + } + } + catch (final UpdaterException | KNXException e) { + logger.error("{}{}{} ({})", ansi().fgBright(WARN), e.getMessage(), ansi().reset(), + e.getClass().getSimpleName()); + logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + } + catch (final Throwable e) { logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 logger.error("Update did not finish."); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 6da3ca6d..ef580178 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -185,7 +185,6 @@ public String requestUIDFromDevice() uid = Arrays.copyOfRange(result, DATA_POSITION, result.length - DATA_POSITION); String errorMsg = String.format("Request UID failed %s result.length=%d, UID_LENGTH_USED=%d, UID_LENGTH_MAX=%d", UPDProtocol.byteArrayToHex(uid), uid.length, UPDProtocol.UID_LENGTH_USED, UPDProtocol.UID_LENGTH_MAX); - logger.error(errorMsg); restartProgrammingDevice(); throw new UpdaterException(errorMsg); } @@ -221,9 +220,8 @@ public BootloaderIdentity requestBootloaderIdentity() if (!versionsMatch) { - logger.error("{}Bootloader version {} is not compatible, please update Bootloader to version {} or higher{}", - ansi().fgBright(WARN), bl.getVersion(), ToolInfo.minVersionBootloader(), ansi().reset()); - throw new UpdaterException("Bootloader version not compatible!"); + throw new UpdaterException(String.format("Bootloader version %s is not compatible, please update Bootloader to version %s or higher.", + bl.getVersion(), ToolInfo.minVersionBootloader())); } return bl; } @@ -492,19 +490,37 @@ public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throw else if ((devices.length == 1) && (progDeviceAddr != null) && (progDeviceAddr.equals(devices[0]))) { // correct device in prog mode return; } - logger.warn("{}{} Device(s) in bootloader/programming mode: {}{}", - ansi().fgBright(WARN), devices.length, Arrays.stream(devices).toArray(), ansi().reset()); - if (devices.length == 0) { - throw new UpdaterException("No device in programming mode."); - } - else { - throw new UpdaterException(String.format("%d wrong device(s) %s are already in programming mode.", devices.length, Arrays.toString(devices))); - } + + throw new UpdaterException(getExceptionMessage(devices, progDeviceAddr)); } catch (KNXException e ) { throw new UpdaterException(String.format("checkDevicesInProgrammingMode failed. %s", e.getMessage()), e); } } + private static String getExceptionMessage(IndividualAddress[] devicesInProgMode, IndividualAddress progDeviceAddr) { + String exceptionMessage; + if (devicesInProgMode.length == 0) { + exceptionMessage = "No device in programming mode."; + } + else if (devicesInProgMode.length == 1) { + exceptionMessage = String.format("Device %s is already in bootloader/programming mode.", + Arrays.toString(devicesInProgMode)); + } + else { + exceptionMessage = String.format("%d other devices %s are already in bootloader/programming mode.", + devicesInProgMode.length, Arrays.toString(devicesInProgMode)); + } + String expectedDeviceAddr; + if (progDeviceAddr == null) { + expectedDeviceAddr = "none"; + + } + else { + expectedDeviceAddr = progDeviceAddr.toString(); + } + return String.format("%s Expected [%s]", exceptionMessage, expectedDeviceAddr); + } + public UDPProtocolVersion getProtocolVersion() { return protocolVersion; } From ce4f872f94ea59c7d311e19ccfc285868107c703 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 20 Feb 2025 03:09:15 +0100 Subject: [PATCH 313/359] [Updater] Added todo for an WinUSB alternative under windows --- .../updater/src/resources/javax.usb.properties | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/resources/javax.usb.properties b/firmware_updater/updater/src/resources/javax.usb.properties index 2d32e866..159fe29a 100644 --- a/firmware_updater/updater/src/resources/javax.usb.properties +++ b/firmware_updater/updater/src/resources/javax.usb.properties @@ -6,4 +6,9 @@ javax.usb.services = org.usb4java.javax.Services # KNX USB communication timeout org.usb4java.javax.timeout = 1000 # USB device scan interval (defaults to 500 ms, assigning 0 will scan only once at startup) -org.usb4java.javax.scanInterval = 1000 \ No newline at end of file +org.usb4java.javax.scanInterval = 1000 + +# todo this might be a USB driver solution on windows instead of zadig and WinUSB +# https://stackoverflow.com/questions/57241363/java-usb4java-reading-from-usb-device-on-windows-10-platform +# https://github.com/daynix/UsbDk +#org.usb4java.javax.useUSBDK = true From 3f57d94a79764ca312103c48bac2b58c91501291 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 20 Feb 2025 10:52:48 +0100 Subject: [PATCH 314/359] [Updater] Upgrade gradle to 8.12.1 --- firmware_updater/updater/README.md | 2 +- .../updater/gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index b0e7f81d..860a6aaf 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -3,7 +3,7 @@ ## Requirements * JDK 17+ -* gradle >=8.9 +* gradle >=8.12.1 * Selfbus device with flashed [bootloader](../bootloader) version 1.00 or higher ## Build diff --git a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties index 18362b78..9bf7bd33 100644 --- a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties +++ b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From c0c284cd4489f27f1c88c7d471213d9472b463c3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 20 Feb 2025 11:53:12 +0100 Subject: [PATCH 315/359] [Updater] Add com port logging to `--discover` --- .../updater/DiscoverKnxInterfaces.java | 22 +++++++++++++++++-- .../src/org/selfbus/updater/Updater.java | 5 +++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java index 523d6547..612617fd 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java +++ b/firmware_updater/updater/src/org/selfbus/updater/DiscoverKnxInterfaces.java @@ -16,6 +16,8 @@ import java.util.Set; import java.util.concurrent.ExecutionException; +import static tuwien.auto.calimero.serial.SerialConnectionFactory.portIdentifiers; + public class DiscoverKnxInterfaces { private final static Logger logger = LoggerFactory.getLogger(DiscoverKnxInterfaces.class); @@ -62,7 +64,11 @@ public static Set getUsbInterfaces() throws InterruptedException { return knxUsbDevices; } - public static void toText(List> ifaceList) { + public static Set getCOMPorts() { + return portIdentifiers(); + } + + public static void logIPInterfaces(List> ifaceList) { for (Discoverer.Result r : ifaceList) { SearchResponse sr = r.response(); logger.info("Found IP interface: {}", sr.getDevice().getName()); @@ -70,11 +76,23 @@ public static void toText(List> ifaceList) { } } - public static void toText(Set usbInterfaces) { + public static void logUSBInterfaces(Set usbInterfaces) { for (final var d : usbInterfaces) { final String vp = String.format("%04x:%04x", d.vendorId(), d.productId()); logger.info("Found USB Interface: {} {} S/N {} VID/PID {}", d.manufacturer(), d.product(), d.serialNumber(), vp); } } + + public static void logCOMPorts(Set comPorts) { + if (comPorts.isEmpty()) { + logger.info("Found 0 COM ports."); + } + else if (comPorts.size() == 1) { + logger.info("Found COM port: {}", comPorts); + } + else { + logger.info("Found {} COM ports: {}", comPorts.size() , comPorts); + } + } } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index f7f47461..8e594809 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -100,8 +100,9 @@ public static void main(final String[] args) { if (options.getDiscoverIsSet()) { logger.info("Discovering KNX network..."); - DiscoverKnxInterfaces.toText(DiscoverKnxInterfaces.getAllnetIPInterfaces()); - DiscoverKnxInterfaces.toText(DiscoverKnxInterfaces.getUsbInterfaces()); + DiscoverKnxInterfaces.logIPInterfaces(DiscoverKnxInterfaces.getAllnetIPInterfaces()); + DiscoverKnxInterfaces.logUSBInterfaces(DiscoverKnxInterfaces.getUsbInterfaces()); + DiscoverKnxInterfaces.logCOMPorts(DiscoverKnxInterfaces.getCOMPorts()); finalizeJansi(); return; } From 725c480ccb69d50c2a54238afeca41a96ffb6a13 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 21 Feb 2025 18:12:26 +0100 Subject: [PATCH 316/359] =?UTF-8?q?Don=C2=B4t=20change=20whitespace=20in?= =?UTF-8?q?=20mark-down=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d12d8a04 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.md -whitespace From d79674dbd8c7eaf2b117826e05ae4cbba428c903 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 21 Feb 2025 18:20:48 +0100 Subject: [PATCH 317/359] [Updater] Add known issues and TOC to README --- firmware_updater/updater/README.md | 47 +++++++++++++++++- .../images/libusb4java_assertion_error.png | Bin 0 -> 14885 bytes .../images/zadig_knx_interface_selected.png | Bin 0 -> 25023 bytes 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 firmware_updater/updater/images/libusb4java_assertion_error.png create mode 100644 firmware_updater/updater/images/zadig_knx_interface_selected.png diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 860a6aaf..02983620 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -1,5 +1,25 @@ # Selfbus-Updater + +* [Selfbus-Updater](#selfbus-updater) + * [Wiki](#wiki) + * [Requirements](#requirements) + * [Build](#build) + * [Usage](#usage) + * [Common use cases:](#common-use-cases) + * [Known Issues](#known-issues) + * [Error *Assertion failed* with KNX USB-Interface under Windows](#error-assertion-failed-with-knx-usb-interface-under-windows) + * [Loxone Miniserver Gen 1](#loxone-miniserver-gen-1) + * [Development](#development) + * [IDEs:](#ides) + * [IntelliJ IDEA Settings for Updater GUI development:](#intellij-idea-settings-for-updater-gui-development) + * [gradle:](#gradle) + + +## Wiki + +The Updater Wiki article can be found [here](https://selfbus.org/wiki/software/tools/7-selfbus-firmware-updater). + ## Requirements * JDK 17+ @@ -105,8 +125,31 @@ Important for Loxone Miniserver Gen 1: ``` java -jar SB_updater-x.xx-all.jar --fileName "out8-bcu1_flashstart_*.hex" --uid 05:B0:01:02:E9:80:AC:AE:E9:07:47:55 --full --reconnect 500 --ip-tunnel-reconnect 247 --own x.y.z ``` -**For Loxone Miniserver Gen 1 `--ip-tunnel-reconnect` and `--own x.y.z` are mandatory.** -**`--own x.y.z` must match the Loxone Miniserver´s own physical KNX address** +**For Loxone Miniserver Gen 1 `--ip-tunnel-reconnect 247` and `--own x.y.z` are mandatory.** +**`x.y.z` must match the Loxone Miniserver´s own physical KNX address** + +## Known Issues + +### Error *Assertion failed* with KNX USB-Interface under Windows + +Windows error assertion failed + +For the KNX USB interface to work with the Updater, the interface must use the WinUSB driver under Windows. +The WinUSB driver can be installed with e.g. [zadig](https://zadig.akeo.ie). +- Open zadig and click in the menu *Options->List all devices* +- In the drop-down select the KNX USB-Interface (e.g. KNX-Interface). +Zadig KNX USB Interface selected + +- Make sure that driver *WinUSB* is selected and click on *Replace Driver*. + +Note: After replacing the standard *HID* driver, the ETS no longer works with the KNX USB interface. +To uninstall the *WinUSB* driver, search for the interface in the Windows Device Manager and uninstall its driver. +Windows will reinstall the standard *HID* driver after reconnecting the interface. + +### Loxone Miniserver Gen 1 + +For Loxone Miniserver Gen 1 `--ip-tunnel-reconnect 247` and `--own x.y.z` are mandatory. +`x.y.z` must match the Loxone Miniserver´s own physical KNX address. ## Development diff --git a/firmware_updater/updater/images/libusb4java_assertion_error.png b/firmware_updater/updater/images/libusb4java_assertion_error.png new file mode 100644 index 0000000000000000000000000000000000000000..448da55265a25bba3aa2ac8ae58a703d4e0c0769 GIT binary patch literal 14885 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 literal 0 HcmV?d00001 diff --git a/firmware_updater/updater/images/zadig_knx_interface_selected.png b/firmware_updater/updater/images/zadig_knx_interface_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..ab23aaa1b9100de827a771c192da82357fc22c23 GIT binary patch literal 25023 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 QUY1f&lvS1aDrE}(UujmFvj6}9 literal 0 HcmV?d00001 From f1070f822e7a796320d378ccf158e2fa4ba50464 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 21 Feb 2025 18:23:41 +0100 Subject: [PATCH 318/359] [Updater] Link to readme instead of zadig --- .../updater/src/org/selfbus/updater/Updater.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 8e594809..f62d42a6 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -42,6 +42,8 @@ public class Updater implements Runnable { @SuppressWarnings("unused") private Updater() {} // disable default constructor + private final static String UPDATER_README_LINK = "https://github.com/selfbus/software-arm-lib/blob/main/firmware_updater/updater/README.md"; + private final static Logger logger = LoggerFactory.getLogger(Updater.class); private CliOptions cliOptions = null; private static DeviceManagement dm = null; @@ -372,8 +374,10 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres if (e.getMessage().contains("error sending report over USB") && (!cliOptions.getUsbVendorIdAndProductId().isBlank()) && System.getProperty("os.name").startsWith("Windows")) { - logger.info("{}Make sure the USB Interface {} uses the WinUSB driver. Checkout https://zadig.akeo.ie{}", + logger.info("{}Make sure the USB Interface {} uses the WinUSB driver.{}", ansi().fgBright(WARN), cliOptions.getUsbVendorIdAndProductId(), ansi().reset() ); + logger.info("Checkout {}{}{} for more info.", + ansi().fgBright(INFO), UPDATER_README_LINK, ansi().reset() ); } } catch (final UpdaterException | KNXException e) { From ca1acfe616b356903b3ff2a60bcb3dafdb853a44 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 21 Feb 2025 18:25:44 +0100 Subject: [PATCH 319/359] [Updater] Reconnect if necessary in `checkDeviceInProgrammingMode` --- .../selfbus/updater/devicemgnt/DeviceManagement.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index ef580178..0fce89dc 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -89,6 +89,10 @@ public void reconnect(final int waitMs) throws KNXException, UpdaterException, U open(); } + public void reconnect() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { + reconnect(cliOptions.getReconnectMs()); + } + public void open() throws KNXException, UpdaterException, UnknownHostException, InterruptedException { close(); this.link = new SBKNXLink(this.cliOptions).openLink(); @@ -431,7 +435,7 @@ private void handleKNXException(final UPDCommand command, final KNXException e) logger.warn("{}{}{} ({} {})", ansi().fgBright(WARN), e.getMessage(), ansi().reset(), e.getClass().getSimpleName(), command); try { - reconnect(cliOptions.getReconnectMs()); + reconnect(); } catch (KNXException | UnknownHostException e2) { throw new UpdaterException(String.format("%s failed.", command), e); @@ -481,6 +485,10 @@ public ResponseResult sendWithRetry(final UPDCommand command, final byte[] data, public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throws UpdaterException, InterruptedException { try { + if (!isLinkAlive()) { + reconnect(); + } + ManagementProcedures mgmt = new ManagementProceduresImpl(link); IndividualAddress[] devices = mgmt.readAddress(); mgmt.close(); @@ -492,7 +500,7 @@ else if ((devices.length == 1) && (progDeviceAddr != null) && (progDeviceAddr.eq } throw new UpdaterException(getExceptionMessage(devices, progDeviceAddr)); - } catch (KNXException e ) { + } catch (KNXException | UnknownHostException e ) { throw new UpdaterException(String.format("checkDevicesInProgrammingMode failed. %s", e.getMessage()), e); } } From 0af722232986d7613d863401b48a52915384c702 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Fri, 21 Feb 2025 18:30:24 +0100 Subject: [PATCH 320/359] [Updater] No stacktrace in ui on Updater exit caused by an exception --- .../src/org/selfbus/updater/Updater.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index f62d42a6..4211ce27 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -115,7 +115,7 @@ public static void main(final String[] args) { shutdownHandler.unregister(); } catch (KNXFormatException | ParseException | InterruptedException e) { - logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + logExceptionUserFriendly(e); } finally { if (dm != null) { @@ -381,9 +381,7 @@ else if (newFirmware.startAddress() == bootLoaderIdentity.applicationFirstAddres } } catch (final UpdaterException | KNXException e) { - logger.error("{}{}{} ({})", ansi().fgBright(WARN), e.getMessage(), ansi().reset(), - e.getClass().getSimpleName()); - logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + logExceptionUserFriendly(e); } catch (final Throwable e) { logger.error("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 @@ -438,4 +436,15 @@ public String requestUid() throws KNXException, UpdaterException, UnknownHostExc throw e; } } + + private static void logExceptionUserFriendly(Throwable e) { + logger.error("{}{}{} ({})", ansi().fgBright(WARN), e.getMessage(), ansi().reset(), + e.getClass().getSimpleName()); + Throwable cause = e.getCause(); + if (cause != null) { + logger.error("{}Caused by: {}{} ({})", ansi().fgBright(WARN), cause.getMessage(), ansi().reset(), + cause.getClass().getSimpleName()); + } + logger.debug("", e); // todo see logback issue https://github.com/qos-ch/logback/issues/876 + } } From 696829d68d5806986a0b12e635f42482f62be090 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 5 Mar 2025 00:47:25 +0100 Subject: [PATCH 321/359] [Updater] Upgrade to `gradle 8.13` --- .../updater/gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 43705 bytes .../gradle/wrapper/gradle-wrapper.properties | 3 +- firmware_updater/updater/gradlew | 33 +++++++++++------- firmware_updater/updater/gradlew.bat | 22 ++++++------ 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.jar b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.jar index ccebba7710deaf9f98673a68957ea02138b60d0a..9bbc975c742b298b441bfb90dbc124400a3751b9 100644 GIT binary patch literal 43705 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 DHx0+F literal 61608 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 diff --git a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties index 9bf7bd33..37f853b1 100644 --- a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties +++ b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/firmware_updater/updater/gradlew b/firmware_updater/updater/gradlew index 79a61d42..faf93008 100755 --- a/firmware_updater/updater/gradlew +++ b/firmware_updater/updater/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,10 +85,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +133,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +147,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +155,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +200,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/firmware_updater/updater/gradlew.bat b/firmware_updater/updater/gradlew.bat index 93e3f59f..9d21a218 100644 --- a/firmware_updater/updater/gradlew.bat +++ b/firmware_updater/updater/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From 1e334b925a6ddbf527924f5eca970ea8b59f8097 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 5 Mar 2025 04:15:46 +0100 Subject: [PATCH 322/359] [Updater] Upgrade to `calimero-core 2.6-rc2` --- firmware_updater/updater/build.gradle | 6 ++---- firmware_updater/updater/settings.gradle | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 1da2b18e..ee0b4114 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -106,12 +106,10 @@ tasks.named('check') { dependencies { // Use this implementation for debugging with local calimero-core // Set location of local calimero-core in settings.gradle - implementation 'com.github.calimero:calimero-core' + //implementation 'com.github.calimero:calimero-core' - //todo use maven repo again when it contains commit b4114ab (probably with release candidate 2?) - // https://github.com/calimero-project/calimero-core/commit/b4114abe79bc9448e0e0c5b02c480229f4de6953 // calimero knx bus access library - //implementation 'com.github.calimero:calimero-core:2.6-rc1' + implementation 'com.github.calimero:calimero-core:2.6-rc2' // calimero serial tx/rx lib for ft1.2 and tpuart support implementation 'com.github.calimero:calimero-rxtx:2.6-rc1' diff --git a/firmware_updater/updater/settings.gradle b/firmware_updater/updater/settings.gradle index 8497d26f..94e79588 100644 --- a/firmware_updater/updater/settings.gradle +++ b/firmware_updater/updater/settings.gradle @@ -1,11 +1,8 @@ rootProject.name = 'SB_updater' -//todo use maven repo again when it contains commit b4114ab (probably with release candidate 2?) -// https://github.com/calimero-project/calimero-core/commit/b4114abe79bc9448e0e0c5b02c480229f4de6953 - // Use this to build with local calimero-core // Change also dependency in build.gradle -includeBuild('../../../calimero/calimero-core') +//includeBuild('../../../calimero/calimero-core') dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) From 5adc00e19e7bb1593e2b41cab671f7be22407fe7 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 9 Mar 2025 16:36:49 +0100 Subject: [PATCH 323/359] [Updater] Revert `Filter eibd hop count decrement info message` This commit reverts partially the commit 2d918cc. Just use knxd if this info message bothers you. --- .../src/org/selfbus/updater/logging/MessageFilter.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java index a6e4d5d2..297a79f5 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java @@ -23,10 +23,7 @@ public class MessageFilter extends Filter { */ public static final List FILTERED_MESSAGES = List.of( // Loxone Miniserver Gen1 returns always status 0x04 on KNX tunnel requests with sequence number 255 - "received service acknowledgment status sequence number out of order", - // from calimero-core KNXnetIPTunnel.java: - // we could get a .con with its hop count already decremented by 1 (eibd does that) - "received L_Data.con with hop count decremented by 1 (sent" + "received service acknowledgment status sequence number out of order" ); /** From 9b1f5a344fb26363549c43d99cbf28b9c9192872 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 9 Mar 2025 16:54:15 +0100 Subject: [PATCH 324/359] [Updater] Revert `Filter received service acknowledgment status sequence number out of order` This commit reverts partially the commit c698f41. This message will most likely appear with an Loxone Minserver Gen1 as IP-Interface. In this case, just call Updater with option `--ip-tunnel-reconnect 247` --- .../src/org/selfbus/updater/logging/MessageFilter.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java index 297a79f5..098fa594 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java +++ b/firmware_updater/updater/src/org/selfbus/updater/logging/MessageFilter.java @@ -21,10 +21,7 @@ public class MessageFilter extends Filter { * If an event's formatted message contains any of the strings in this list, * the event will be excluded from logging. */ - public static final List FILTERED_MESSAGES = List.of( - // Loxone Miniserver Gen1 returns always status 0x04 on KNX tunnel requests with sequence number 255 - "received service acknowledgment status sequence number out of order" - ); + public static final List FILTERED_MESSAGES = List.of(); /** * Evaluates a logging event and determines whether the event should From fdb00442022faf6840f0b1bd13079ab6aba85985 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 9 Mar 2025 16:55:52 +0100 Subject: [PATCH 325/359] [Updater] Bump version to 1.25 --- firmware_updater/updater/build.gradle | 2 +- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index ee0b4114..32915465 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.selfbus' -version = '1.24' ///\todo Change also in ToolInfo.java (versionMajor, versionMinor) +version = '1.25' ///\todo Change also in ToolInfo.java (versionMajor, versionMinor) description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 70e9b812..e4b192de 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -14,7 +14,7 @@ public final class ToolInfo { private static final long versionMajor = 1; ///\todo change also in build.gradle - private static final long versionMinor = 24; + private static final long versionMinor = 25; private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md private static final long minMinorVersionBootloader = 0; From d03f8876c34e40fc9b7a0a6a3001c157aad0dccd Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Mar 2025 08:49:57 +0100 Subject: [PATCH 326/359] [sblib] Fix `Decompressor` RAM buffer alignment Use `alignas(...)` to ensure correct buffer alignment for in appliaction programming (IAP), otherwise IAP command "Copy to flash" might fail. (See UM10398 NXP usermanual 26.7.2) --- firmware_updater/bootloader/inc/decompressor.h | 4 ++-- firmware_updater/bootloader/src/update.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/bootloader/inc/decompressor.h b/firmware_updater/bootloader/inc/decompressor.h index f8993670..6e673fd3 100644 --- a/firmware_updater/bootloader/inc/decompressor.h +++ b/firmware_updater/bootloader/inc/decompressor.h @@ -49,12 +49,12 @@ class Decompressor uint8_t cmdBuffer[5] = {0}; int expectedCmdLength = 0; int cmdBufferLength = 0; - uint8_t scratchpad[FLASH_PAGE_SIZE] = {0}; + alignas(FLASH_RAM_BUFFER_ALIGNMENT) uint8_t scratchpad[FLASH_PAGE_SIZE] = {0}; uint8_t oldPages[FLASH_PAGE_SIZE * REMEMBER_OLD_PAGES_COUNT] = {0}; int bytesToFlash = 0; int rawLength = 0; State state = State::EXPECT_COMMAND_BYTE; - __attribute__ ((aligned (FLASH_RAM_BUFFER_ALIGNMENT))) uint8_t * startAddrOfPageToBeFlashed = 0; + alignas(FLASH_RAM_BUFFER_ALIGNMENT) uint8_t * startAddrOfPageToBeFlashed = 0; uint8_t * startAddrOfFlash = 0; public: diff --git a/firmware_updater/bootloader/src/update.cpp b/firmware_updater/bootloader/src/update.cpp index 08e3a9fa..b8fc3bb1 100644 --- a/firmware_updater/bootloader/src/update.cpp +++ b/firmware_updater/bootloader/src/update.cpp @@ -57,7 +57,7 @@ * Maximum extended frame length is 254 bytes - 1 byte for the @ref UPD_Command totaling in 1265 bytes */ constexpr uint16_t bufferSize = 1265; -static uint8_t __attribute__ ((aligned (FLASH_RAM_BUFFER_ALIGNMENT))) ramBuffer[bufferSize]; //!< RAM buffer used for flash operations +alignas(FLASH_RAM_BUFFER_ALIGNMENT) static uint8_t ramBuffer[bufferSize]; //!< RAM buffer used for flash operations static uint8_t * retTelegram = nullptr; //!< pointer to return buffer, as a field for easier access and smaller code size // Try to avoid direct access to these global variables. From dc4c5717abf0727945fc5eb62b2b5e7873413ade Mon Sep 17 00:00:00 2001 From: Darthyson Date: Wed, 26 Mar 2025 11:29:01 +0100 Subject: [PATCH 327/359] [Updater] Add remaining frame images --- .../src/org/selfbus/updater/gui/GuiMain.java | 2 +- .../frame_images/selfbus_logo_256x256.png | Bin 0 -> 6755 bytes .../frame_images/selfbus_logo_32x32.png | Bin 0 -> 914 bytes .../frame_images/selfbus_logo_60x60.png | Bin 0 -> 1525 bytes .../frame_images/selfbus_logo_72x72.png | Bin 0 -> 1820 bytes .../frame_images/selfbus_logo_96x96.png | Bin 0 -> 2353 bytes 6 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 firmware_updater/updater/src/resources/frame_images/selfbus_logo_256x256.png create mode 100644 firmware_updater/updater/src/resources/frame_images/selfbus_logo_32x32.png create mode 100644 firmware_updater/updater/src/resources/frame_images/selfbus_logo_60x60.png create mode 100644 firmware_updater/updater/src/resources/frame_images/selfbus_logo_72x72.png create mode 100644 firmware_updater/updater/src/resources/frame_images/selfbus_logo_96x96.png diff --git a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java index 9d06a2e3..5baa2afc 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java +++ b/firmware_updater/updater/src/org/selfbus/updater/gui/GuiMain.java @@ -530,7 +530,7 @@ private void fillTelegramPriorityComboBox() { private void setFrameImages() { String resourceTemplate = "/frame_images/selfbus_logo_%sx%s.png"; - String[] resolutions = {"16", "24", "32", "48", "256"}; + String[] resolutions = {"16", "32", "60", "72", "96", "256"}; List frameImageList = new ArrayList<>(); for (String resolution : resolutions) { diff --git a/firmware_updater/updater/src/resources/frame_images/selfbus_logo_256x256.png b/firmware_updater/updater/src/resources/frame_images/selfbus_logo_256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..2340777a523871e077498d0616c68a095cbb8415 GIT binary patch literal 6755 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 literal 0 HcmV?d00001 diff --git a/firmware_updater/updater/src/resources/frame_images/selfbus_logo_96x96.png b/firmware_updater/updater/src/resources/frame_images/selfbus_logo_96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..cef451bf5dd465eced200bc0ac9f830e26653c37 GIT binary patch literal 2353 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 literal 0 HcmV?d00001 From cc444214dc5cdd606fc272bd3b206ac45959dd58 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 30 Oct 2025 10:04:19 +0100 Subject: [PATCH 328/359] [Updater] Display device PA in "OK" text color --- .../src/org/selfbus/updater/devicemgnt/DeviceManagement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 0fce89dc..e7048f8a 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -147,7 +147,7 @@ private void waitRestartTime(int restartTimeSeconds) throws InterruptedException public void restartDeviceToBootloader(IndividualAddress device) throws InterruptedException { int restartProcessTime = Mcu.DEFAULT_RESTART_TIME_SECONDS; try (Destination dest = this.mc.createDestination(device, true, false, false)) { - logger.info("Restarting device {} into bootloader", device); + logger.info("Restarting device {}{}{} into bootloader", ansi().fgBright(OK), device, ansi().reset()); restartProcessTime = this.mc.restart(dest, RESTART_ERASE_CODE, RESTART_CHANNEL); String timeStr; if (restartProcessTime <= 1) { From 01994ce502fa0d75bf43b7050070f0ef4a10f80b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 30 Oct 2025 10:06:25 +0100 Subject: [PATCH 329/359] [Updater] Upgrade to `calimero 2.6` final release --- firmware_updater/updater/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 32915465..69936710 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -109,13 +109,13 @@ dependencies { //implementation 'com.github.calimero:calimero-core' // calimero knx bus access library - implementation 'com.github.calimero:calimero-core:2.6-rc2' + implementation 'com.github.calimero:calimero-core:2.6' // calimero serial tx/rx lib for ft1.2 and tpuart support - implementation 'com.github.calimero:calimero-rxtx:2.6-rc1' + implementation 'com.github.calimero:calimero-rxtx:2.6' // calimero usb support - implementation 'io.calimero:calimero-usb:2.6-rc1' + implementation 'io.calimero:calimero-usb:2.6' // find specific directories under linux and windows implementation 'net.harawata:appdirs:1.3.0' From b36c03ea3469901988dd6e2f1719fd5afc31dea8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 30 Oct 2025 10:07:11 +0100 Subject: [PATCH 330/359] [Updater] Upgrade to `logback-classic 1.5.19` --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 69936710..7ac461b5 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -127,7 +127,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.9.0' // console and file logging - implementation 'ch.qos.logback:logback-classic:1.5.16' + implementation 'ch.qos.logback:logback-classic:1.5.19' // Redirect System.Logger (used by calimero >=3.0) to slf4J (logback) implementation 'org.slf4j:slf4j-jdk-platform-logging:2.0.16' From a30f6ea36076fcd22235ea7f22ce9f8561919d2a Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 30 Oct 2025 10:50:45 +0100 Subject: [PATCH 331/359] [Updater] Upgrade gradle to 8.14.3 --- .../updater/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties index 37f853b1..d4081da4 100644 --- a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties +++ b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 04c699acba571c2cc23c41ce39d53db4ce713c6b Mon Sep 17 00:00:00 2001 From: Darthyson Date: Thu, 30 Oct 2025 10:54:37 +0100 Subject: [PATCH 332/359] [Updater] Bump version to 1.26 --- firmware_updater/updater/build.gradle | 2 +- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 7ac461b5..eef4e3ca 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.selfbus' -version = '1.25' ///\todo Change also in ToolInfo.java (versionMajor, versionMinor) +version = '1.26' ///\todo Change also in ToolInfo.java (versionMajor, versionMinor) description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index e4b192de..23af5477 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -14,7 +14,7 @@ public final class ToolInfo { private static final long versionMajor = 1; ///\todo change also in build.gradle - private static final long versionMinor = 25; + private static final long versionMinor = 26; private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md private static final long minMinorVersionBootloader = 0; From 4a2f1e4b4e6a6257dad734fb083b6ae196e389d7 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 13:00:11 +0100 Subject: [PATCH 333/359] Add `Update` flag as in KNX Spec. 3.0 3/5/1 4.18.4 specified --- sblib/inc/sblib/eib/types.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sblib/inc/sblib/eib/types.h b/sblib/inc/sblib/eib/types.h index b6b4614e..16de521e 100644 --- a/sblib/inc/sblib/eib/types.h +++ b/sblib/inc/sblib/eib/types.h @@ -86,6 +86,9 @@ struct ComConfig */ enum ComConfigFlag { + /** Com object configuration flag: update enabled */ + COMCONF_UPDATE = 0x80, + /** Com object configuration flag: transmit enabled */ COMCONF_TRANS = 0x40, From cb1fc12d4d9173b4dab6331edcc644ebe9e41098 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 13:02:47 +0100 Subject: [PATCH 334/359] Format ComObject debug logging --- sblib/src/eib/bcu_default.cpp | 1 + sblib/src/eib/com_objects.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sblib/src/eib/bcu_default.cpp b/sblib/src/eib/bcu_default.cpp index 41e7f44a..fcbcd946 100644 --- a/sblib/src/eib/bcu_default.cpp +++ b/sblib/src/eib/bcu_default.cpp @@ -209,6 +209,7 @@ bool BcuDefault::processGroupAddressTelegram(ApciCommand apciCmd, uint16_t group DB_COM_OBJ( serial.println(); serial.print("BCU grp addr: 0x", (unsigned int)groupAddress, HEX, 4); + serial.print(" "); ); comObjects->processGroupTelegram(groupAddress, apciCmd & APCI_GROUP_MASK, telegram); diff --git a/sblib/src/eib/com_objects.cpp b/sblib/src/eib/com_objects.cpp index 1afcdfc6..2f5b7dc4 100644 --- a/sblib/src/eib/com_objects.cpp +++ b/sblib/src/eib/com_objects.cpp @@ -233,16 +233,16 @@ void ComObjects::addObjectFlags(int objno, int flags) if (objno & 1) flags <<= 4; - d(serial.print(" addObjFlags in (obj, flags): ");) - d(serial.print(objno, DEC, 2);) - d(serial.print(", ");) + d(serial.print(" addObjFlags(");) + d(serial.print(objno, DEC, 3);) + d(serial.print(", 0x");) d(serial.print(flags, HEX, 2);) - d(serial.print(", is: ");) + d(serial.print(") old: 0x");) d(serial.print(flagsTab[objno >> 1], HEX, 2);) flagsTab[objno >> 1] |= flags; - d(serial.print(", out: ");) + d(serial.print(" new: ");) d(serial.print(flagsTab[objno >> 1], HEX, 2);) d(serial.println();) } @@ -257,9 +257,9 @@ void ComObjects::setObjectFlags(int objno, int flags) flagsPtr += objno >> 1; // "select" high or low nibble according to objno odd or even d( - serial.print(" setObjFlags obj: ", objno, DEC, 2); - serial.print(" is: ", *flagsPtr, HEX, 2); - serial.print(" to: ", flags, HEX, 2); + serial.print(" setObjFlags obj: ", objno, DEC, 3); + serial.print(" is: 0x", *flagsPtr, HEX, 2); + serial.print(" to: 0x", flags, HEX, 2); ) if (objno & 1) @@ -272,7 +272,7 @@ void ComObjects::setObjectFlags(int objno, int flags) *flagsPtr &= 0xf0; *flagsPtr |= flags; } - d(serial.println(" out: ", *flagsPtr, HEX, 2);) + d(serial.println(" out: 0x", *flagsPtr, HEX, 2);) } unsigned int ComObjects::objectRead(int objno) From 10e411d23a5326083fa569c7cab2b230caca0273 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 13:04:54 +0100 Subject: [PATCH 335/359] Fix `objConf` debug logging `objno` was logged instead of `objConf` --- sblib/src/eib/com_objectsBCU1.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sblib/src/eib/com_objectsBCU1.cpp b/sblib/src/eib/com_objectsBCU1.cpp index 5329b3c2..c0d2c176 100644 --- a/sblib/src/eib/com_objectsBCU1.cpp +++ b/sblib/src/eib/com_objectsBCU1.cpp @@ -72,7 +72,7 @@ void ComObjectsBCU1::processGroupTelegram(uint16_t addr, int apci, byte* tel, in */ const byte* assocTab = bcu->addrTables->assocTable(); const int endAssoc = 1 + (*assocTab) * 2; - int objno, objConf; + int objno; DB_COM_OBJ( serial.print("grpAddr ", mainGroup(addr)); @@ -112,8 +112,8 @@ void ComObjectsBCU1::processGroupTelegram(uint16_t addr, int apci, byte* tel, in } //DB_COM_OBJ(serial.println("commsTabAddr: 0x", ((UserEepromBCU1*)((BcuDefault*)bcu)->userEeprom)->commsTabAddr(), HEX);); - objConf = objectConfig(objno).config; - DB_COM_OBJ(serial.println("objConf: 0x", objno, HEX, 2);); + uint8_t objConf = objectConfig(objno).config; + DB_COM_OBJ(serial.println("objConf: 0x", objConf, HEX, 2);); From 17c6ad734e2cd9681944df3466e4007fd261db0f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 13:05:50 +0100 Subject: [PATCH 336/359] Add some `setObjectFlags` debug logging --- sblib/src/eib/com_objects.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sblib/src/eib/com_objects.cpp b/sblib/src/eib/com_objects.cpp index 2f5b7dc4..59b4a07d 100644 --- a/sblib/src/eib/com_objects.cpp +++ b/sblib/src/eib/com_objects.cpp @@ -308,6 +308,11 @@ void ComObjects::_objectWrite(int objno, unsigned int value, int flags) } //addObjectFlags(objno, flags); + d( + serial.print("ComObjects::_objectWrite call setObjectFlags(", objno, DEC, 3); + serial.print(", 0x", flags, HEX, 2); + serial.println(")"); + ) setObjectFlags(objno, flags); //clear any pending ram com object flags and set new flags } From 6e9dc873049fc3a29443b6c7bbbecd66998a966f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 13:07:35 +0100 Subject: [PATCH 337/359] Move some ComObject debug logging to new header --- sblib/inc/sblib/eib/com_objects.h | 1 + sblib/inc/sblib/eib/com_objects_debug.h | 33 +++++ sblib/src/eib/com_object_debug.cpp | 162 ++++++++++++++++++++++++ sblib/src/eib/com_objects.cpp | 10 -- 4 files changed, 196 insertions(+), 10 deletions(-) create mode 100644 sblib/inc/sblib/eib/com_objects_debug.h create mode 100644 sblib/src/eib/com_object_debug.cpp diff --git a/sblib/inc/sblib/eib/com_objects.h b/sblib/inc/sblib/eib/com_objects.h index fbbfe9c0..00ca483c 100644 --- a/sblib/inc/sblib/eib/com_objects.h +++ b/sblib/inc/sblib/eib/com_objects.h @@ -36,6 +36,7 @@ #include #include #include +#include class BcuBase; diff --git a/sblib/inc/sblib/eib/com_objects_debug.h b/sblib/inc/sblib/eib/com_objects_debug.h new file mode 100644 index 00000000..fbbeb636 --- /dev/null +++ b/sblib/inc/sblib/eib/com_objects_debug.h @@ -0,0 +1,33 @@ +/** + * com_objects_debug.h - KNX Communication objects debugging helper. + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + + +#ifndef SBLIB_EIB_COM_OBJECTS_DEBUG_H_ +#define SBLIB_EIB_COM_OBJECTS_DEBUG_H_ + +#include +#include + +#if defined(DUMP_COM_OBJ) +# include +#endif + +#ifdef DUMP_COM_OBJ +# define d(x) x +#else +# define d(x) +#endif + + +void printComObjectConfig(uint8_t config); +void printComObjectType(uint8_t type); + +#endif /* SBLIB_EIB_COM_OBJECTS_DEBUG_H_ */ diff --git a/sblib/src/eib/com_object_debug.cpp b/sblib/src/eib/com_object_debug.cpp new file mode 100644 index 00000000..5a940420 --- /dev/null +++ b/sblib/src/eib/com_object_debug.cpp @@ -0,0 +1,162 @@ +/** + * com_objects_debug.cpp - KNX Communication objects debugging helper. + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include + +void printComObjectConfig(uint8_t config) +{ + DB_COM_OBJ( + // C - communicate + if ((config & 0b00000100) == (0b00000100)) + { + serial.print("c"); + } + else + { + serial.print(" "); + } + + // R - read + if ((config & 0b00001000) == (0b00001000)) + { + serial.print("r"); + } + else + { + serial.print(" "); + } + + // W - write + if ((config & 0b00010000) == (0b00010000)) + { + serial.print("w"); + } + else + { + serial.print(" "); + } + + // T - transfer + if ((config & 0b01000000) == (0b01000000)) + { + serial.print("t"); + } + else + { + serial.print(" "); + } + + // U - update + if ((config & 0b10000000) == (0b10000000)) + { + serial.print("u"); + } + else + { + serial.print(" "); + } + + // Segment Selector Type + serial.print(" s"); + if ((config & 0b00100000) == (0b00100000)) + { + serial.print("1"); // segment = 0x100 + } + else + { + serial.print("0"); // segment = 0x00 + } + + serial.print(" "); + switch (config & 0b00000011) + { + case 0b11: + serial.print("low "); + break; + case 0b01: + serial.print("high"); + break; + case 0b10: + serial.print("alm "); + break; + case 0b00: + serial.print("sys "); + break; + default: + // this should never happen + serial.print("unknown"); + break; + } + ); +} + +void printComObjectType(uint8_t type) +{ + DB_COM_OBJ( + if ((type >= BIT_1) && (type <= BIT_7)) + { + serial.print(type + 1, DEC, 1); + serial.print(" bit"); + if (type > 0) + { + serial.print("s"); // bits, if more then one + } + } + else + { + switch(type) + { + case BYTE_1: + serial.print("1 byte"); + break; + + case BYTE_2: + serial.print("2 bytes"); + break; + + case BYTE_3: + serial.print("3 bytes"); + break; + + case BYTE_4: + serial.print("4 bytes"); + break; + + case DATA_6: + serial.print("6 bytes"); + break; + + case DATA_8: + serial.print("8 bytes"); + break; + + case DATA_10: + serial.print("10 bytes"); + break; + + case MAXDATA: + serial.print("14 bytes"); + break; + + case VARDATA: + serial.print("1-14 bytes"); + break; + + default: + serial.print("unknown (0x", type, HEX, 2); + serial.print(")"); + break; + } + } + + ); +} diff --git a/sblib/src/eib/com_objects.cpp b/sblib/src/eib/com_objects.cpp index 59b4a07d..1c8a3369 100644 --- a/sblib/src/eib/com_objects.cpp +++ b/sblib/src/eib/com_objects.cpp @@ -187,16 +187,6 @@ #include #include -#if defined(DUMP_COM_OBJ) -# include -#endif - -#ifdef DUMP_COM_OBJ -# define d(x) x -#else -# define d(x) -#endif - /** The COMFLAG_UPDATE flag, moved to the high nibble */ #define COMFLAG_UPDATE_HIGH (COMFLAG_UPDATE << 4) From 9aaa6306092326564f00b87abc56c1bf5ab116fe Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 13:11:00 +0100 Subject: [PATCH 338/359] Add BCU ComObjectConfig table debug logging --- sblib/inc/sblib/eib/com_objectsBCU2.h | 1 + sblib/src/eib/bcu2.cpp | 1 + sblib/src/eib/com_objectsBCU2.cpp | 55 +++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/sblib/inc/sblib/eib/com_objectsBCU2.h b/sblib/inc/sblib/eib/com_objectsBCU2.h index c47d7858..b1ee271d 100644 --- a/sblib/inc/sblib/eib/com_objectsBCU2.h +++ b/sblib/inc/sblib/eib/com_objectsBCU2.h @@ -44,6 +44,7 @@ class ComObjectsBCU2 : public ComObjectsBCU1 ComObjectsBCU2(BcuDefault* bcuInstance) : ComObjectsBCU1(bcuInstance) {} ~ComObjectsBCU2() = default; + void printObjectConfigTable(); protected: virtual byte* objectValuePtr(int objno) override; virtual byte* objectConfigTable() override; diff --git a/sblib/src/eib/bcu2.cpp b/sblib/src/eib/bcu2.cpp index a67e9e7e..439c6154 100644 --- a/sblib/src/eib/bcu2.cpp +++ b/sblib/src/eib/bcu2.cpp @@ -74,6 +74,7 @@ inline void BCU2::begin(int manufacturer, int deviceType, int version, word read userEeprom->orderInfo()[userEeprom->orderInfoSize() - 1] = lowByte(SBLIB_VERSION); BcuDefault::_begin(); + static_cast(this->comObjects)->printObjectConfigTable(); } void BCU2::begin(int manufacturer, int deviceType, int version) diff --git a/sblib/src/eib/com_objectsBCU2.cpp b/sblib/src/eib/com_objectsBCU2.cpp index e8c650a0..9bde3d31 100644 --- a/sblib/src/eib/com_objectsBCU2.cpp +++ b/sblib/src/eib/com_objectsBCU2.cpp @@ -61,3 +61,58 @@ const ComConfig& ComObjectsBCU2::objectConfig(int objno) { return objectConfigBCU2(objno)->baseConfig; } + +void ComObjectsBCU2::printObjectConfigTable() +{ +#ifdef DUMP_COM_OBJ + byte * addr = (byte* ) & ((BCU2*)bcu)->userEeprom->commsTabAddr(); + uint16_t comObjTableAddr = makeWord(*(addr + 1), * addr); + serial.println("ObjectConfigTable:"); + serial.println(" address : 0x", (unsigned int)comObjTableAddr, HEX, 4); + if (comObjTableAddr == 0) + { + serial.println("invalid address!"); + return; + } + byte* currentTablePosition = ((BcuDefault*)bcu)->userMemoryPtr(comObjTableAddr); + byte currentSize = *currentTablePosition; + serial.println(" #com objects : ", (unsigned int)currentSize, DEC, 3); + currentTablePosition++; // 1 byte #com objects + uint16_t ramFlagsTablePointer; + if (le_ptr == LITTLE_ENDIAN) + { + ramFlagsTablePointer = makeWord(*(currentTablePosition + 1), *currentTablePosition); + } + else + { + ramFlagsTablePointer = makeWord(*currentTablePosition, *(currentTablePosition + 1)); + } + currentTablePosition += 2; // 2 bytes RAM-Flags-Pointer + serial.println(" RAM-Flags-Ptr: 0x", (unsigned int)ramFlagsTablePointer, HEX, 4); + for (uint8_t i = 0; i < currentSize; i++) + { + uint16_t data; + if (le_ptr == LITTLE_ENDIAN) + { + data = makeWord(*(currentTablePosition + 1), *currentTablePosition); + } + else + { + data = makeWord(*currentTablePosition, *(currentTablePosition + 1)); + } + currentTablePosition += 2; // 2 bytes data pointer + uint8_t config = *currentTablePosition; + currentTablePosition++; // 1 byte config + uint8_t type = *currentTablePosition; + currentTablePosition++; // 1 byte type + serial.print("#", i, DEC, 3); + serial.print(": data 0x", data, HEX, 4); + serial.print(" config (0x", config, HEX, 2); + serial.print("): "); + printComObjectConfig(config); + serial.print(" type: "); + printComObjectType(type); + serial.println(); + } +#endif +} From d3e9c70325746837fdb86111b5d3a01ffb2ee0ae Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 17:04:12 +0100 Subject: [PATCH 339/359] Fix check `Update Enable` flag on `APCI_GROUP_VALUE_RESPONSE_PDU` The 7th bit of the Config Octet shall always be 1 for Group Object Table - Realisation Type 1 (KNX Spec. 3.0 3/5/1 4.18.3.1.2.1). The 7th bit of the Config Octet of The Group Object Table - Realisation Type 2 is used as Update Enable flag (KNX Spec. 3.0 3/5/1 4.18.4.1). --- sblib/src/eib/com_objectsBCU1.cpp | 38 ++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/sblib/src/eib/com_objectsBCU1.cpp b/sblib/src/eib/com_objectsBCU1.cpp index c0d2c176..20415724 100644 --- a/sblib/src/eib/com_objectsBCU1.cpp +++ b/sblib/src/eib/com_objectsBCU1.cpp @@ -115,20 +115,36 @@ void ComObjectsBCU1::processGroupTelegram(uint16_t addr, int apci, byte* tel, in uint8_t objConf = objectConfig(objno).config; DB_COM_OBJ(serial.println("objConf: 0x", objConf, HEX, 2);); - - - if (apci == APCI_GROUP_VALUE_WRITE_PDU || apci == APCI_GROUP_VALUE_RESPONSE_PDU) + if ((objConf & COMCONF_COMM) != COMCONF_COMM) { - // Check if communication and write are enabled - if ((objConf & COMCONF_WRITE_COMM) == COMCONF_WRITE_COMM) - processGroupWriteTelegram(objno, tel); // set update flag and update value of object + // communication is disabled, just continue and check remaining associations + continue; } - else if (apci == APCI_GROUP_VALUE_READ_PDU) + + switch (apci) { - // Check if communication and read are enabled - if ((objConf & COMCONF_READ_COMM) == COMCONF_READ_COMM) - // we received read-request from bus - so send response back and search for more associations - sendGroupWriteTelegram(objno, addr, true); // send write to the bus and update all associated local objects + case APCI_GROUP_VALUE_WRITE_PDU: + // Check if write is enabled + if ((objConf & COMCONF_WRITE) == COMCONF_WRITE) + processGroupWriteTelegram(objno, tel); // set update flag and update value of object + break; + + case APCI_GROUP_VALUE_RESPONSE_PDU: + // Check if update is enabled + if ((objConf & COMCONF_UPDATE) == COMCONF_UPDATE) + processGroupWriteTelegram(objno, tel); // set update flag and update value of object + break; + + case APCI_GROUP_VALUE_READ_PDU: + // Check if read is enabled + if ((objConf & COMCONF_READ) == COMCONF_READ) + // we received read-request from bus - so send response back and search for more associations + sendGroupWriteTelegram(objno, addr, true); // send write to the bus and update all associated local objects + break; + + default: + // this should never happen + IF_DEBUG(fatalError();); } } } From 9afbf47c90b565f06688e7a65f33c43e95582a49 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 21:41:22 +0100 Subject: [PATCH 340/359] Add BCU1 ComObjectConfig table debug logging --- sblib/inc/sblib/eib/com_objectsBCU1.h | 1 + sblib/src/eib/bcu1.cpp | 1 + sblib/src/eib/com_objectsBCU1.cpp | 47 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/sblib/inc/sblib/eib/com_objectsBCU1.h b/sblib/inc/sblib/eib/com_objectsBCU1.h index c91a4a90..e3bae0e3 100644 --- a/sblib/inc/sblib/eib/com_objectsBCU1.h +++ b/sblib/inc/sblib/eib/com_objectsBCU1.h @@ -49,6 +49,7 @@ class ComObjectsBCU1 : public ComObjects ComObjectsBCU1(BcuDefault* bcuInstance) : ComObjects((BcuBase*)bcuInstance) {} ~ComObjectsBCU1() = default; + void printObjectConfigTable(); virtual const ComConfig& objectConfig(int objno) override; virtual int objectSize(int objno) override; virtual byte* objectValuePtr(int objno) override; diff --git a/sblib/src/eib/bcu1.cpp b/sblib/src/eib/bcu1.cpp index 83c3b59b..22001102 100644 --- a/sblib/src/eib/bcu1.cpp +++ b/sblib/src/eib/bcu1.cpp @@ -25,6 +25,7 @@ inline void BCU1::begin(int manufacturer, int deviceType, int version) { BcuDefault::begin(manufacturer, deviceType, version); BcuDefault::_begin(); + comObjects->printObjectConfigTable(); } bool BCU1::applicationRunning() const diff --git a/sblib/src/eib/com_objectsBCU1.cpp b/sblib/src/eib/com_objectsBCU1.cpp index 20415724..35d34ed9 100644 --- a/sblib/src/eib/com_objectsBCU1.cpp +++ b/sblib/src/eib/com_objectsBCU1.cpp @@ -183,3 +183,50 @@ inline const ComConfigBCU1* ComObjectsBCU1::objectConfigBCU1(int objno) } return (const ComConfigBCU1*) (objConfigTable + 1 + sizeof(ComConfigBCU1::DataPtrType) + objno * sizeof(ComConfigBCU1) ); } + +void ComObjectsBCU1::printObjectConfigTable() +{ +#ifdef DUMP_COM_OBJ + uint16_t comObjTableAddr = ((BcuDefault*)bcu)->userEeprom->commsTabPtr(); + comObjTableAddr += ((BcuDefault*)bcu)->userEeprom->startAddr(); // for BCU1 add 0x100 + serial.println("ObjectConfigTable:"); + serial.println(" address : 0x", (unsigned int)comObjTableAddr, HEX, 4); + if (comObjTableAddr == 0) + { + serial.println("invalid address!"); + return; + } + byte* currentTablePosition = ((BcuDefault*)bcu)->userMemoryPtr(comObjTableAddr); + byte currentSize = *currentTablePosition; + serial.println(" #com objects : ", (unsigned int)currentSize, DEC, 3); + currentTablePosition++; // 1 byte #com objects + uint16_t ramFlagsTablePointer; + ramFlagsTablePointer = *currentTablePosition; + + currentTablePosition++; // 1 byte RAM-Flags-Pointer + serial.println(" RAM-Flags-Ptr: 0x", (unsigned int)ramFlagsTablePointer, HEX, 4); + for (uint8_t i = 0; i < currentSize; i++) + { + uint8_t data; + data = *currentTablePosition; + currentTablePosition++; // 1 byte data pointer + uint8_t config = *currentTablePosition; + if ((config & COMCONF_VALUE_TYPE) == COMCONF_VALUE_TYPE) + { + data += 0x100; // Segment selector KNX Spec. 3.0 3/5/1 4.18.3.1.2.1 + } + + currentTablePosition++; // 1 byte config + uint8_t type = *currentTablePosition; + currentTablePosition++; // 1 byte type + serial.print("#", i, DEC, 3); + serial.print(": data 0x", data, HEX, 4); + serial.print(" config (0x", config, HEX, 2); + serial.print("): "); + printComObjectConfig(config); + serial.print(" type: "); + printComObjectType(type); + serial.println(); + } +#endif +} From 087a99bd76618c94a1afe5ff9fd5756871146164 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Mon, 17 Nov 2025 22:11:20 +0100 Subject: [PATCH 341/359] [Test] Add `Memory` and `UserEeprom` unit tests --- test/lib-test-cases/lib-test-cases.cmake | 4 + test/lib-test-cases/src/knx/test_memory.cpp | 129 ++++++++++++++++++ test/lib-test-cases/src/knx/test_memory.h | 37 +++++ .../src/knx/test_user_eeprom.cpp | 62 +++++++++ .../lib-test-cases/src/knx/test_user_eeprom.h | 51 +++++++ 5 files changed, 283 insertions(+) create mode 100644 test/lib-test-cases/src/knx/test_memory.cpp create mode 100644 test/lib-test-cases/src/knx/test_memory.h create mode 100644 test/lib-test-cases/src/knx/test_user_eeprom.cpp create mode 100644 test/lib-test-cases/src/knx/test_user_eeprom.h diff --git a/test/lib-test-cases/lib-test-cases.cmake b/test/lib-test-cases/lib-test-cases.cmake index 6d158625..410575d2 100644 --- a/test/lib-test-cases/lib-test-cases.cmake +++ b/test/lib-test-cases/lib-test-cases.cmake @@ -19,4 +19,8 @@ set(SBLIB_LIB_TEST_CASES_SRC src/test_prot_app_program.cpp src/test_prot_tlayer4.cpp src/timeout_test.cpp + src/knx/test_memory.h + src/knx/test_memory.cpp + src/knx/test_user_eeprom.h + src/knx/test_user_eeprom.cpp ) \ No newline at end of file diff --git a/test/lib-test-cases/src/knx/test_memory.cpp b/test/lib-test-cases/src/knx/test_memory.cpp new file mode 100644 index 00000000..3b84a227 --- /dev/null +++ b/test/lib-test-cases/src/knx/test_memory.cpp @@ -0,0 +1,129 @@ +/* + * Tests for the memory.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include "test_memory.h" + + +struct TestRange +{ + uint32_t start; + uint32_t size; +}; + +static std::vector testRanges = { + { 0,1 }, // lower limit + { 0, UINT32_MAX }, // upper limit size + { UINT32_MAX - 1, 1 }, // upper limit address + { 0x100, 256 }, // BCU 1 + { 0x100, 1024 }, // BCU 2 + { 0x3f00, 3072 }, // Mask 0x0701, 0x0705 + { 0x3300, 3072 } // System B +}; + +TEST_CASE("Memory Constructor", "[UserEeprom]") +{ + TestMemory* testMemory; + // Test declared ranges + for (const auto& [start, size] : testRanges) + { + testMemory = new TestMemory(start, size); + REQUIRE(testMemory->startAddr() == start); + REQUIRE(testMemory->size() == size); + REQUIRE(testMemory->endAddr() == start + size - 1); + delete testMemory; + } + + // Test some arbitrary ranges + for (uint32_t start = 0; start < 0xffff; start += 131) // 131 is some arbitrary increment + { + for (uint32_t size = 1; size < 0xffff; size += 251) // 251 is some arbitrary increment + { + testMemory = new TestMemory(start, size); + REQUIRE(testMemory->startAddr() == start); + REQUIRE(testMemory->size() == size); + REQUIRE(testMemory->endAddr() == start + size - 1); + delete testMemory; + } + } +} + +TEST_CASE("Memory normalizeAddress", "[UserEeprom]") +{ + // Test declared ranges + for (const auto& [start, size] : testRanges) + { + TestMemory testMemory(start, size); + const uint32_t maxIterations = std::min(testMemory.endAddr(), static_cast(0xffff)); + for (uint32_t i = start; i <= maxIterations; i++) + { + uint32_t addressToTest = i + start; + testMemory.normalizeAddress(&addressToTest); + REQUIRE(addressToTest == i); + } + } +} + +TEST_CASE("Memory inRange(start, end)", "[UserEeprom]") +{ + // Test declared ranges + for (const auto& [start, size] : testRanges) + { + TestMemory testMemory(start, size); + REQUIRE(testMemory.inRange(start - 1, start) == false); + REQUIRE(testMemory.inRange(start, start) == true); + REQUIRE(testMemory.inRange(start, testMemory.endAddr()) == true); + REQUIRE(testMemory.inRange(start, testMemory.endAddr() - 1) == (size > 1)); + REQUIRE(testMemory.inRange(start, testMemory.endAddr()) == true); + const uint32_t maxIterations = std::min(testMemory.endAddr(), static_cast(0xffff)); + for (uint32_t i = start; i <= maxIterations; i++) + { + REQUIRE(testMemory.inRange(i, maxIterations) == true); + } + + for (uint32_t i = 0; i <= maxIterations; i--) + { + REQUIRE(testMemory.inRange(start, testMemory.endAddr() - i) == true); + } + } +} + +TEST_CASE("Memory inRange(address)", "[UserEeprom]") +{ + // Test declared ranges + for (const auto& [start, size] : testRanges) + { + TestMemory testMemory(start, size); + REQUIRE(testMemory.inRange(start - 1) == false); + REQUIRE(testMemory.inRange(testMemory.endAddr() + 1) == false); + + const uint32_t maxIterations = std::min(testMemory.endAddr(), static_cast(0xffff)); + for (uint32_t i = start; i <= maxIterations; i++) + { + REQUIRE(testMemory.inRange(i) == true); + } + + for (uint32_t i = 0; i <= maxIterations; i--) + { + REQUIRE(testMemory.inRange(testMemory.endAddr() - i) == true); + } + } +} + +TEST_CASE("Memory overridden dummys", "[UserEeprom]") +{ + // Test declared ranges + for (const auto& [start, size] : testRanges) + { + TestMemory testMemory(start, size); + // These checks are just too check that the methods are implemented. + REQUIRE(testMemory[start] == 0); + REQUIRE(testMemory.getUInt8(start) == 0); + REQUIRE(testMemory.getUInt16(start) == 0); + } +} \ No newline at end of file diff --git a/test/lib-test-cases/src/knx/test_memory.h b/test/lib-test-cases/src/knx/test_memory.h new file mode 100644 index 00000000..93824552 --- /dev/null +++ b/test/lib-test-cases/src/knx/test_memory.h @@ -0,0 +1,37 @@ +/* + * Test class for the memory.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#ifndef TEST_SBLIB_KNX_MEMORY_H_ +#define TEST_SBLIB_KNX_MEMORY_H_ + +#include + + +class TestMemory final : public Memory +{ +public: + TestMemory() = delete; + TestMemory(const uint32_t start, const uint32_t size) : + Memory(start, size) {} + + //using Memory::startAddr; + //using Memory::endAddr; + //using Memory::size; + using Memory::normalizeAddress; + //using Memory::inRange; + + // Implement dummy abstract methods + byte& operator[](const uint32_t address) override { return dummyByte; } + [[nodiscard]] uint8_t getUInt8(uint32_t address) const override { return 0;} + [[nodiscard]] uint16_t getUInt16(uint32_t address) const override { return 0;} + +private: + uint8_t dummyByte = 0; +}; + +#endif /* TEST_SBLIB_KNX_MEMORY_H_ */ diff --git a/test/lib-test-cases/src/knx/test_user_eeprom.cpp b/test/lib-test-cases/src/knx/test_user_eeprom.cpp new file mode 100644 index 00000000..56cd60b0 --- /dev/null +++ b/test/lib-test-cases/src/knx/test_user_eeprom.cpp @@ -0,0 +1,62 @@ +/* + * Tests for the userEeprom.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "test_user_eeprom.h" + + +struct TestSize +{ + uint32_t start; + uint32_t size; + uint32_t flashSize; + uint32_t resultFlashStart; +}; + +static std::vector testSizes = { + { 0, 1, 1, 0xffff }, // lower limit + { 0x100, 256, 256, 0xff00 }, // BCU 1 + { 0x100, 1024, 1024, 0xfc00 }, // BCU 2 + { 0x3f00, 3072, 4096, 0xf000 }, // Mask 0x0701, 0x0705 + { 0x3300, 3072, 4096, 0xf000 } // System B +}; + +TEST_CASE("User EEPROM test", "[userEeprom]") +{ + // Test declared ranges + for (const auto& [start, size, flashSize, resultFlashStart] : testSizes) + { + // Test a new mcu with erased flash + IAP_Init_Flash(0xff); + auto testUserEeprom = new TestUserEeprom(start, size, flashSize); + uint8_t* validPage = testUserEeprom->findValidPage(); + REQUIRE(validPage == nullptr); + delete testUserEeprom; + + // Test a mcu with erased flash + IAP_Init_Flash(0x31); + testUserEeprom = new TestUserEeprom(start, size, flashSize); + validPage = testUserEeprom->findValidPage(); + // uint32_t i = iapFlashSize(); + // while (i > 0) + // { + // if (&FLASH[i] == validPage) + // break; + // i--; + // } + // printf("i: %x\n", i); + REQUIRE(validPage == (FLASH + iapFlashSize() - flashSize)); + REQUIRE(validPage == (&FLASH[resultFlashStart])); + delete testUserEeprom; + } +} diff --git a/test/lib-test-cases/src/knx/test_user_eeprom.h b/test/lib-test-cases/src/knx/test_user_eeprom.h new file mode 100644 index 00000000..3e3daf05 --- /dev/null +++ b/test/lib-test-cases/src/knx/test_user_eeprom.h @@ -0,0 +1,51 @@ +/* + * Test class for the userEeprom.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#ifndef TEST_SBLIB_KNX_USEREEPROM_H_ +#define TEST_SBLIB_KNX_USEREEPROM_H_ + +#include + + +class TestUserEeprom final : public UserEeprom +{ +public: + TestUserEeprom() = delete; + using UserEeprom::UserEeprom; + //TestUserEeprom(unsigned int start, unsigned int size, unsigned int flashSize); + using UserEeprom::findValidPage; + + [[nodiscard]] uint8_t& optionReg() const override { return userEepromData[0]; } + [[nodiscard]] uint8_t& manuDataH() const override { return userEepromData[1]; } + [[nodiscard]] uint8_t& manuDataL() const override { return userEepromData[2]; } + [[nodiscard]] uint8_t& manufacturerH() const override { return userEepromData[3]; } + [[nodiscard]] uint8_t& manufacturerL() const override { return userEepromData[4]; } + [[nodiscard]] uint8_t& deviceTypeH() const override { return userEepromData[5]; } + [[nodiscard]] uint8_t& deviceTypeL() const override { return userEepromData[6]; } + [[nodiscard]] uint8_t& version() const override { return userEepromData[7]; } + [[nodiscard]] uint8_t& checkLimit() const override { return userEepromData[8]; } + [[nodiscard]] uint8_t& appPeiType() const override { return userEepromData[9]; } + [[nodiscard]] uint8_t& syncRate() const override { return userEepromData[10]; } + [[nodiscard]] uint8_t& portCDDR() const override { return userEepromData[11]; } + [[nodiscard]] uint8_t& portADDR() const override { return userEepromData[12]; } + [[nodiscard]] uint8_t& runError() const override { return userEepromData[13]; } + [[nodiscard]] uint8_t& routeCnt() const override { return userEepromData[14]; } + [[nodiscard]] uint8_t& maxRetransmit() const override { return userEepromData[15]; } + [[nodiscard]] uint8_t& confDesc() const override { return userEepromData[16]; } + [[nodiscard]] uint8_t& assocTabPtr() const override { return userEepromData[17]; } + [[nodiscard]] uint8_t& commsTabPtr() const override { return userEepromData[18]; } + [[nodiscard]] uint8_t& usrInitPtr() const override { return userEepromData[19]; } + [[nodiscard]] uint8_t& usrProgPtr() const override { return userEepromData[20]; } + + [[nodiscard]] uint8_t& addrTabSize() const override { return userEepromData[21]; } + [[nodiscard]] uint8_t* addrTab() const override { return &userEepromData[22]; } +}; + + + +#endif /* TEST_SBLIB_KNX_USEREEPROM_H_ */ From cf92241fbddefb4c75fdf85c8cb1a92922cf85cf Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 02:59:02 +0100 Subject: [PATCH 342/359] [Updater] Invert link state logic for better readability --- .../selfbus/updater/devicemgnt/DeviceManagement.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index e7048f8a..00840fa2 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -423,11 +423,11 @@ public BootloaderStatistic requestBootLoaderStatistic() throws UpdaterException, return blStatistic; } - private boolean isLinkAlive() { + private boolean isLinkDead() { if (link == null) { - return false; + return true; } - return link.isOpen(); + return !link.isOpen(); } private void handleKNXException(final UPDCommand command, final KNXException e) throws @@ -468,7 +468,7 @@ public ResponseResult sendWithRetry(final UPDCommand command, final byte[] data, handleKNXException(command, e); } finally { - if (!isLinkAlive()) { + if (isLinkDead()) { maxRetry = 0; // exit while } } @@ -485,7 +485,7 @@ public ResponseResult sendWithRetry(final UPDCommand command, final byte[] data, public void checkDeviceInProgrammingMode(IndividualAddress progDeviceAddr) throws UpdaterException, InterruptedException { try { - if (!isLinkAlive()) { + if (isLinkDead()) { reconnect(); } From e55b6f38ad35d80ff829c60dff09ff354e8b84fc Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 03:00:21 +0100 Subject: [PATCH 343/359] [Updater] Show tunnel address, if available --- .../selfbus/updater/devicemgnt/DeviceManagement.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java index 00840fa2..2f760bcf 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/DeviceManagement.java @@ -16,6 +16,7 @@ import tuwien.auto.calimero.*; import tuwien.auto.calimero.link.KNXLinkClosedException; import tuwien.auto.calimero.link.KNXNetworkLink; +import tuwien.auto.calimero.link.medium.KNXMediumSettings; import tuwien.auto.calimero.mgmt.Destination; import tuwien.auto.calimero.mgmt.KNXDisconnectException; import tuwien.auto.calimero.mgmt.ManagementProcedures; @@ -24,6 +25,7 @@ import java.net.UnknownHostException; import java.time.Duration; import java.util.Arrays; +import java.util.Optional; import static org.fusesource.jansi.Ansi.*; import static org.selfbus.updater.logging.Color.*; @@ -570,7 +572,12 @@ public String getLinkInfo() { if (this.link == null) { return "No link available."; } - - return link.toString(); + String linkInfo = link.toString(); + KNXMediumSettings settings = link.getKNXMedium(); + Optional tunnelAddress = settings.assignedAddress(); + if (tunnelAddress.isPresent()) { + linkInfo += String.format(", tunnel %s", tunnelAddress.get()); + } + return linkInfo; } } From 7d5286aff00e9b80247dcdf3849ef6a6c1aba784 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 03:25:00 +0100 Subject: [PATCH 344/359] [Updater] Upgrade gradle to 9.2.1 Also fixed gradle warning: The archives configuration has been deprecated for artifact declaration. --- firmware_updater/updater/README.md | 2 +- firmware_updater/updater/build.gradle | 6 +++--- .../updater/gradle/wrapper/gradle-wrapper.jar | Bin 43705 -> 45633 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- firmware_updater/updater/gradlew | 7 ++----- firmware_updater/updater/gradlew.bat | 3 +-- 6 files changed, 8 insertions(+), 12 deletions(-) diff --git a/firmware_updater/updater/README.md b/firmware_updater/updater/README.md index 02983620..38850216 100644 --- a/firmware_updater/updater/README.md +++ b/firmware_updater/updater/README.md @@ -23,7 +23,7 @@ The Updater Wiki article can be found [here](https://selfbus.org/wiki/software/t ## Requirements * JDK 17+ -* gradle >=8.12.1 +* gradle >=9.2.1 * Selfbus device with flashed [bootloader](../bootloader) version 1.00 or higher ## Build diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index eef4e3ca..cfcff94c 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id 'application' - id 'com.gradleup.shadow' version '8.3.0' + id 'com.gradleup.shadow' version '9.2.2' id 'jvm-test-suite' } @@ -65,8 +65,8 @@ compileTestJava.options.compilerArgs = [ '-Xlint:all', // enable all warnings ] -artifacts { - archives shadowJar +tasks.assemble { + dependsOn(tasks.shadowJar) } testing { diff --git a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.jar b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.jar index 9bbc975c742b298b441bfb90dbc124400a3751b9..f8e1ee3125fe0768e9a76ee977ac089eb657005e 100644 GIT binary patch delta 37435 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 delta 35540 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%0A!3{ zp%gi03-h8;;^XKp2;(Me<1qd>50J!Lf) zy^IWBgcpzO)p{vCSGOQ4ezuZF!rXS}>{;>BxC;Nq3eP_o#H7k&Wdzxk7zxlWizryR z?#?f*RTfj$Va}*H3EtHbKPsB2O6bbR^PMieQ0O~%{SE6^pAVjkTMTCm>*Y>?GJ>Bq`4Z$WwLUtynJ z$b=f@zxvfc<4miuKFb48dgV2ra!*Wgp^3g98>hCc$TCwqX`M+4@eov7P9}b|3alIe zf)9b2Q^A(<(p~ocMmasiC;JBkA!Waf!LCHt+`RgW?w0x>D^JR_@j$H>Y=ZXk2>9X+ z>@36Tb-%l3!19phJ7^Gnq1`5i$(~?;3VUCC>~2{r`@rZq(sd5HaYP`XwdVGb4V`~IW3I==4uZ06v2J}o^>&)G4rgv zGN#CqIxS`K7u1t&I{3JwG|{}u5|)uZ=-w45%b*ocvH^rU4lK8$$!BSBqRPb1ifwC@ophfdH=X7i>@ zdrLTYv)m%texaXBCmL!HQBlFOQT}cDvRLc0`^!-m1`+yDa$9_2BGN~to7U4wt~NL3 zCDD#wCVe(Ei?#6%S@~OM<&|v<{WSxBp|GRl4m*rc1F|W|^cwwt5_#5o&ma4*N(BE` zB}oZIppwJ9I>zVa+(MO;j5IE#YW!jWEu5SPLP%vGk~%0EZK+I1EP=cZ_if1Od}Vh{ zJ^`ikdZ<6g(+GKeKPO>H*=}wKJ*oMN^+Fe-AQNmd)%V{pb{z8@`*s80AMR?vQm?uC zJ&*}1HLlX_?Cs#T+0*1IOr|o`XU;na;x_&O86LoO6m{Vht`%p@)56FqXez51FGG)< z!+i37alFZw4Zo3T99n8nM=i~H^^KjK-(|1dQKGneStX?QdFUFzp1yt;gAoP|h~1p^ zt%h2U{(!jqgarg|AsSmn*!)AW)`6~Lu?Ni*W9x}eZ^EfH(Cgh;j&BEQv%7UzUHU*E z8H7Y6qwT+v`Vz4rGMTc!1f=m@@*Flj7S|FZ^&|G{?{Eq;Pq1zY&q%V{Ad;}h{Mkjt zbUwBF^Q8k<#66d0yaVm@j4ZC0>+KRW~NIUmc=OZfg?XuTMQeSMvgOlra(r&;1-g^YQm>ym=tcFJm6gthL_zdC@&MwMm-mTtG3F=Pz8pcfM|_&yfC} zf|Oa7$G-&|1%~h}I@`teSG^nky5djv9y%%a=&EoW*>Yb2&P?~n~ zTq;yFpKr@(9gz#=s3)7K-)j%D<#ZjCW*7?@!j2cL+9}xJ>=3JdKU{Nik2NW60Mhp;uZDiZ_QdtpW>o|_trA?1NdaFBd?^B74 z^nm>l9FlS~{~FV2ru1j>T9S}1G@IRysL-l}s-3P-Mi(Pa0w(`PQrgzWA=nUctUf6X z^oxnE7sbt#J2jQpEMg`0pe-xD&dOrA887EdKY8kWbF?!Jwb=q^!^IR#4bd*sHxoE) zyE{1M%!Vj;S$!TstENmbjX^K6zk$ksm9B1^b1aT+JtVt+KZLD)U$1MKlBbMZRj&A( z8uquo7)n!9sW^#e`N(yqx)w%?@|BREQbnAjgg=Eh(gm`&=sxcG_%0jVMPPu zC@|=eAB|WzCQMg-uf>d=Xj@6~kwS=zy9Af>6Ju&nj1x1CwEGKGLh(E!*2nLI;M8anJ=gtc;8`3=T8_=|KaaC=9#|=3ysm!1jfWt z81r2rKAnX8f95|(=>CRxfv5amV)BN2dVKURF+qj;|B1AZ$!lg-}n`8^8FpmIW|dc{wmy!JM_GCKXvc>Ex&Dn?I~sM5(V zpxXZ`j5>+Qd&nCyjE+;?pbcd5?Dt*^zs0u>VyT>o@f6umGzP+LF0;Hd4NTfxCXZMhnzyCc}8V^PTC58~hxdcD+z{8%@Vr52fnVmQVXPSpuYwMyT zYWy%Bv8vn{f=O}h8{6G}u6DmR{j$LJKaGi_9m zx8*bgG;#aIlz@69HJZC#;AiD`q**$a)uSX=$`eEqo~yk1F#gV?nB~RVI)KOun^!+g z(4ju97*~J2a6R9@$Z#*iEINz}TT?x|CR%cmUpEL-AJUPkKT%7DppsaBaSvpCdN6mZ z)oG{24HtjYH|Y6|8t{I%%vWl9t$5^i!!+Lf>P<*L3_Kew6=n;TX&4=MuD@drAYIOv z|65_9$vys*vOXBW3!5OYw_$y}&tHk^gIk+w+k9V3cI#5D8z$|%Y!Zw`U|ZNVuvJ4k zw-JWL1&%X1k_>BZ^5C0vqyoRT8Dfr_LIYS*V~B5q+S^i}XP~8d66(trbKJKk^wlKp z)&^vJ0pkiE4RQO+LnoYWt-`R@>*iq?ejrrY@#_X43~KQ77bUL;Tl3mECi1g)v)Lgj z*_SDGY(LLXCCasH|O z70!c5mQxt~jN)>#P^Y|jhkoo!Y4?Sjx&PLx$UopX+V+sc%EdX_5+~**?YSEKp^C(V zVO_zrI(L|C{7aX7SxS;|f!@~bDn2ic8Uik7vBc|N+PY@rkJz`LQ51bh)tSJ(8E;l3 z45UK6=TE{#o6y?qvzNDu6xL-;A4kBD?`6gwTrrL|%<`^?cQ~Tq{DY!RxqeLk`-GK4 z1*SY_z9>7mdm}2?6Y8NhJ$TB<4^vN*yrrH^lwee5T}#I z+%`NqD>hX0!7p=$ON?dF2*WvJ#9r_+<{mBc7b;DM!S#^v78R^gdp*y6o`IqMJ&OVr z!#z>EwcnThJlWtrSi$Sx+d?`<$^A_-%uzvyq0C{IQBQjW43~f20QQ?*O(hjoHo%e% zi$rBF4F`u7+c$$8d@!vN9!+&ttwmXaA`qE`g)jXWdFmud`mclRW|2dHo3seNWE3A! z+j>q0WMkHo89EGpu1_I%{|rrI_^&?VQj`;!^(O(WKJKQh36#LZHP4hnue zSwhXmGO$lgPHUcSRklRgwPBu2qS47)dSv-=+mTR0QP}5yYsH$?;q4LeqGXcVjZo#gafX<8q1)W$@pT@HekrMA~-s8!!wVC z5>vhi+UHMmq_cWvQH8ztBPi0i)!W#9|lQJRJ%3FO2O?3s+C;WYCpda zOcy5b-Ze zO>91#8s$xV3cKvlI;`XNGg5|U6MCD_*`J7A_uFZo=t3e$^nq+Kj-rwyzk9N0w2-9Q%ENS;-Em~8P2|+2$x`Jlf4t>Y1J|4op`5U!9EI>U#d&-Psw#7 zQf_YpyDRYBKpsj&P4-1iG>~q{#j(yfKV`RMB_8S8$n(OqP0uvb@oU=U(gACQ!9_Fd zkAHSQH`L=g-V#~ub}mAu;1_3byA0Vb0$NW?S>ei;?aJYo%nCkY8l;57# zw_Rlgzm+fD-v^MN=@sS1uXMsUJ^%5S-J*RVXmdC)Flw~_@_|ZCf&T}eKw!UrK@>eh zE8TveSW!?^WK}2$+YJ~=gTw@-z(WnDr6fKz)7{(c(*4TpZVfT=L;M%U1dS&C0DqM6 zwrGsvgD*34&fIhFxp(H<_s?GetYb;w-RWtw_D1hYYe0dmZ|YW0U)8P9k6ceCZWtJT zXcO~_Jd!c-WnadI)hBBZr+%z|Z}y}Y)2bdhhA$(-9p&h5Y^+)9k2zM=e(8Iu=P?oY zrLo440>=%Alp8qA4oSsai}_mhQMG2+G}PZ}5CVl(m@12QU5^BoL*Yl=u;yM2WgZ;R$%&A4yEbIz%dWH$7CfrObX2H)OWYrjl<4=UUPe=QSY={ z^=4=P;6+`a-1x0%B_n4~1hg&7a|1VdDv+tH90|<+9rP@9J)2YxC4tGUacl{fQ7m8z zR|N7YTdVfjn}(}@xN4om3Hdo(6DT^Ayi_6glbi@FRW3SNxk#089XAS?!7LXZ4I{7x zjQ`U9^vo%4;?~IMJa=kkR?kPP;SQrDJP#F^sr<9}0`85JizR5t=Wz-51+vfiv9pT| za{R#rXXm@|Nkaie-XAhr#sr^$`7fAy^%2wM@+Zul;dc2hFK5s{;K6%fEX(jZfy@t3 zO9u$8Q2LbF0RRB90+X?n9Fyo;Ie!a$9A&jXXZA6(lkF>;lrE)ZNn5*VvI#Aawp&t~ zSKG9kv@{K6`=CsAC&|*?ov=IGHmG<7DxlX3UKK5Jy^3IbpdhxJK!hS{K}Av2t0=y` zUd7k-g$hdV|C`xOvdN~@-|esM?0nxj=R5DmH(z@7(Z`8sjWob?_l-C1T7UQYnm#Rd zNKeOW!Zoq}n);fUks3-QH8WwPLn$M!V?CoMbuFu7b5P5MVuO0@P&Sv!)`a`DWLB@Q z85#(sw4qQ!(CBMwS{+++8DKN(iunDxWD*P6K`pczuX=hQk=FH0B0YfB!+Iu*v#`Fd z;gW`Rp}2my=8Xz5liFkCGJi3Bb0VoTEjKa)jRP4ip41zUWVE3nJ=3_wdfj8cb!myT zOirfx*J+2f#-x@WXx!0vogOn~ax*zo*k@W8+!vYL?=b}v8)~m&nm4)HmejIYnN%j< zfNt*07$%5-#dgD{Q4_4SX=L?SE|V~a8>ho;@RFD2s8m67nUEuboeX zWV(RKWg5M^I%lAD0&OW>sL~P&l%jRYRKw)KnLY546vU`1qE6MemzL8Cm6p-#V3mFt zSD|2u)oeJKRx!Et>wk0bF{bKZWV%^5)b90CJ%v=NqXyW@<|JV>3Yv{OHFFR~yqL)W zhTuQJQXz^mO@#+*Yv>Y{R@0?Sl{5EcY6459VSD6+UH~FifW3~^SI}At7xhxuk?C?K zS@;r&cxj_8{uQ8gc_N)KH%=38!YjShLaiz_Q=6^Uomxgun}1&F5CocOvrV*HOX*(P zYOldw?)?qdX_@5;ZHEIi3}p-ihndi`3Uz_6j6RgqL{Me2(8{_p=a%85oDp}>PNDfL znO2?~5lfsN!Q^h+;can*g1mGUtrt6cm;zG*J(f(srBf*_9giSo03oYqWQxM5L7Uz_ zmG;tpSYJyfjejG(=|gGbNP5SRaCmqP!$Yp2gC4+Ki?;!V`d~Mkcqj*2p+O@B|BFLw z#*iH)Ys+Sc$n0F%r$HnAG$6PcJgW>gv`p8*7FuQ?mx2m*4-dhg7DT3{AdpN_%0q_; zZ1MF94IwD6&n0wIp$t~q6@m#Xvcp+ZPlbe`6*|l`*MB0DGZV?iNFs~ucN9YP=w`qM z3=}+KIAHV#%t1Y{?W(RoP;VG$2!z^G!(z8NHGEi0=JeVIg>JwJaXlN$BntYeEawbw zpf?IWZ!E^Kb$UxCqYWbl{{`CBvVs?;6(!m&26Cj7We~iHZdU2dbjwU#T22t3qEVUN z0_9Co*ng^J2O+LZw;`+BR?W5!+pNM-BBhXB+7OO=E4@uP$-mAZgl(Dr4FXsSflDn4 zPUW`ENG9!M4kCBbJ5;)Z{+-DmEDvM*?7LLDo!%{KQDg?!1(5fsbSu4=sp++XVR@_G zZ)EIn?#cBP_GP-8X-n`dPHSUtm<};RxdndiQGe-u^dH5fXxXmQ)H7P2NN)ehq$I?f zX=H>{Qx()%SciH(NcUFI2k1jZDkb|e-H&XB_M%%qYFgoq&>f+VRM3a%qo@;UYq_+Z z6uievAEyTd+$WgoUsGT9qsP=taQ-0tHKh&r>3GkWI|?3NHW`DRPtn6FJw%^ol0qSc zPJc3mUh}w?eh65|M>36y0615jKcdnYJqoRaLi?_1K2WF7XYi8JOcd9wmmU|~)X^!F zhHS1cTX65{U}xEE{aJcaKz|OI*GQL0yZ0PRz-x+X6P%)79Qy@2EmZat)0%%CU#2f2 zn-$mgAT1Wv>!m-_M}%fxva}904kdLHy~AH^^ZpH-Br#i< zgI4~4Azz`t3oHEtG8*Y_qtF+4>3@vS@Iw%S=38`%=IBVry)22I;vsgFwxm|+l-X72 zTqc^T=>=hOXM>kzR#bMgM<^7EzWiLgV@5WZY%kc0s=#xYUUzOdGS5Roh)gTUg#vpg zFYxeut}Hgrr2sNlsa!2`H8(4K-Yhv$#0q18sACKeq%ejC$53v1TUkicvwxQTl3dP9 z8KcD=jM1X1oK|JVh~dT|Ey*Mb$bB8i|7M?TF4^A)sqTqTt z$h96Q7UPCe3Zb56uo49qNac&TebA#15bGtob7VI+&*F2{b0Dq-#<}D&Nwu0>R zGZo$j8-uK#KJ4X)4T#xgLE-!> zxZpvN?wT2sOHCOPg@0?0j-#0UMFYis@OqJER+iPr^z3AxsjD1gR6aKxzdwQMT9S{Z z9o1&Rr7k#&tmS%sHaVwqiZL;*36_)EwtvxvGPs?#@{S60`26{0 zkUP^^6CcoCLB&P2Sq_n^;wSg#+fcP&B3e_-=y-5jB)C~Ss6ZyG0JY1@KQMG zCY9g7H=_lKCq!%6m$RG2xzlcf_!z%MnB`U&sZrtEkq$7qnw@q><>j~8+d^OyT#y155A0 zy7X*T8_?Sm1G*??$L%y_86_?5*H+J-rpnU*KkwsvRKA-r1-W{6Zw{?fSPVWO+d6Ct z?^{@o=Dt=he~|ALN4wm=4;ims97gPJ~ucI zTkPDmV_QetZiUCDL0b1z{HP5%4#*o4LpJtf_S)n2nhp1az4lo|f_^lX%w-dYtxMaM zSPUxc(tpy>BI34rs_!KrU?#04?aQ^K5jzBRHFnI34*h?QKktE4eu3$N*#mC*YMC)@ zWX>8)rJhzUBns5Z?v>4kwiUjp^3(h!+bgUq9Mq4Jk+pEI!_V+nRFe6tFls|Gc1Yo` z!J4TpQN}emo||nr?e5@!Hm>qi_`-BMkXNC9uNiyD2#Dq9=LAl%Nog?soR!)!MarB@2mX0=sT3O zmNrCIwi@hhUj7mPSk&g9xJdk&*Sg6;{LLqdQwP+&3zgJJ*_O0!LK%aFlsTgCFIE19 zsDH+)P}QK8PU|QW7X_zW%f{^0tYxzT2vqlL{*6HWEz^>-g364!LN+tQ5lAW0&1|H_ zKIr~||ETis`ADGoA-K=)?ulzTa|H3bq z%p1E0VK$V`zav#-le#`6qJNtJf0fUu{C^J#vx_qZ5A{kCAssv|If`CURz6w6s;aByxs00)Adw z8gXeYC6r@|g0gSgHMulTnh&><78Ga9!dC|?I3lQ0P^07%*TI3pSBQvLS}49$EPs>4 zC^9XaiWq(*(TQel%8s~>n-PQd7I;}KU7$+mOG~`eVgI^N$g;2WL&+@IR?tfqsZy1M zsbmi7h6pm-cGEAb>{T4NTv{Ovi1DO4D?a+b!H$|G?4BQ-{X8is)e3YBD@$RqhC)l6 z)N<+AAlh=f!x#2aqpB@9Wfn21HMbcG0rDrpl_$7JeEiZ5rnbw_TiHBeM@U|Y+sKz|}#T%KAw z11nc#S2idT<|Q|BEDHKGUd!mG46nsGNotom&^$<+Q49+Gu(-EZq^;s3VVTRRDYq-q zHgo~fcBaMvd=Grb14p!k2_P%`Pt@vwW@WH|X;{S|4Clx#3MJG! zIq+H|5)Q(6wXqGk4oh*&i1o|^ZDiAZ`dLLvfT57Mj z!#N9U`vq$UiY;cLYv>tHo7&OIsw*8Ve(1GmbQ_k>x9k0zja)iz-+#DCTJf^Qf|-IG zLpB$mZc&59^-Xs!)u(563m)hqa*=G6SHv7C_{Wk?hiQ{l-lbftLlpHTkvmNWu&q@PrF8` zH&0jFe+(-?3C>VNpinP;&6Se~9ZPJW=t zP|oN5^1(bkou{vcT_a~6!2Z{){RXi=zd29eY!ZOg0R6TNTJ3UPYyJ9sp1voLjsWTV z7E(YUIVVB=vU?J2=fm`)yJ-=Ce_{cP&fNsSic>rwhmo_?RFKZ(7kA#|Spa-4icL2Wi({h}W) zFNa;OCZ$^OLtU@l`|3%*t6FhhD5t$n;G&x@l? z%009|(DU(B{IC5oWOz4T{?m5AAVxi$U z_4wg{&0$w%4C~Rb+u~uh^CsaF_RFQt57BwT?eONv^I9;2s2L%BoYzNvykV3tKV|>i zD1JW7ZGU@w+%d+TIO{Z3i_n5dF5vjxke@<6-af|Nv+T~ZR$QPWLNgQC80Y7s0)MyuppU;NLXUq?#QWhX?&dJU?59=yvy_j2 zeoR~I;;`(OpQ7Hv>O$AM?xC&Y{EMg+UoX~!gJ0$Oca|glIokacEk&e*#w+-{fSx`! zCh-WV+x>F)2rop6>V|#KJhXj;J%0H#eWxxjIpIFgg5&{)b^I$&VBEJwt@BA0LVqA3 zL~Xq#L{0s;G$-0MD$T9WOO?^C(=@;Sapwm}t#_>H8X*^V)t{!_<5E?0e*_TKd1+CX z^D$Zyb=3EaNlWumU{qQ*CauKDpdQLgtFc=VjW~SLB|SdrQmn7Tij0+2J&d=Fd-77t zr-a_DGYi0W0pXK&uNsy1oSMRMJvfdQIi4oTzT&!t49sgzQ58Cdxr$f1 zmKM`;3QB#>2c0M7XQjAv%z4^+KI8nZc)F|Hi`_?w+^gKHt>;>I*m`bsZ-2I)o86u6 zK|CYw9oBQV`x@)n=N`14Z+0J(Tk$;RzSDw#r~5APlw0L4>$y|zv7Wv1fc3mizDYdM z$MoaB2HI}*HtR{Eo#ddsA+l@@R%RZ5>ApY$kINq?uPT6&TeI+oCRjsPuktRTOmmd8fhAI-!DDy+>i5h&;6fs_k8@|!vGeGl>*x? zyKME6ORBq#;D1Il7OM7F2YagPtAlp5&x#n1WygF`J7m&$+>Dq;!lcPwBjF47n!$~U zWHeFj?=d0?v%b17?28(GK8s~^H#aW|E^eZ=@g>>)J;_Lf1`@r7ZxOL(ENsP0Gj7Gg zG`h)*CrB5KFKIZgVTmtfZmPaig%Hp>>|{J>uCyYiz<&%o9&QZBjZnmF?2j9LeP+C| zHI{IUNMzn31qA|=HyE3Y#)uIMH=fl=ysr$i zH7KYOD4@_&{HD|_Xd8cq0BI7pOdZN+|dQT}UGsG!vAO3n?eV zHAl>#|L4UKHqXn@76uxMT`NAR;S8K9aO_cTQg9Yon_hT^9i;%A%?a6#RbybHV@tv@ zqY742+*wUuOcfQveh)AWWgG&Eq_7>&ZRrvV_1=7+&qioV1y}UO7kVmc?ht! z^9d>P2vo41lCF;jB7_L#`BI4v`BUi9Z~-o)V+;`hJLE-o`WS8WmaI3F}bML(Q7PjYVJrzb!=phHh^xa)?+g$n@+G-V0PYg&3{4+ zdl7@phu245n43vP8l9~nU#$&-H_%!RgG_vf_$ub|zzr0~h#d}_q-c7+JJcp8N!yLMsfYsqb@F-zrw^r7RVWJ; zRVu#8Fw(`avrWKzSV;YZ$bOXCwKHmUxrfNO2X!D^K=M?2re-;3}!yf?Ns zO1TRDNd`G~o83>D{4@6fu>m37M92&FB*6bhEklPZwTM$(yE+DP$xx zYB&x%KT{L4WPd+BKQTY13i|T&$XS7M`eUMAnLo4eU2`C1OPPJK=3mP014BnGB=ySq{BC; zimpV|O7Lqj!GJ)QO)O-!B-ks3ieTCaeCrNi-S383FfwtN~OiN+G2uEKunASds8U6y=<}Vz?tbpHBpF(fvAfkT7-K=_=i+obLTCQ$|R`2r`q;Qe9 zFa>8DiidHrUmZXz^Optu>XW4Fz&l=b1eXW=+7LebaC5P{SufYii|@c}uUf7z4)`ZX zqkoINnwP%5(0PR}1`fRH)%>bgeE?q;NPk=}uocJg*VhCdp^*4Bvi}<#5UxMKz>2&)u&X|h^+@R|eUUsG#w#i8EB*aJx3cqUSLy50<2MgZ z3BOX%tdb8$NvnfEo8B*9iIf)=No(}j6_<&3QJdD5GkAI}_VSnjI zDGs)0053?F3w>q6MSh9)5m6BE?8N(lfVZ$K;4TxMj)F3wb`&;yQlhxr)73E~CZMVyTCf{6UQd~r<5vrILJ9bb z++L-FqFs~{^XV*KX&=C3`c<-^V}IzQ!Z$4HQ=*ZAK%DAh>f4Pu-hynD3cJe0qH&2) zUt5yYJo+(H!8*FeFac#oy_pEfXioz5B|KDAia^(g{?)n15D^Ig(b3 z6D2)atd=w?`oyc^6mgNbO46&vwUS;hwo7`m7?$)Qu~*W);(4(j5HE_CB)?z0#ng3; z>qhqkv0Tz3(c;?fx>fQ_nZM0-r{tM3Kj0daJX7X}Tn|c~Df2sBk4T;=^N+cnkUUf7 zpK`q-d8W+obG;#Xrp&+XdVg2)OqqYr^?~G>GXJ5wQ1VQPcbB*;n3t4z0?gA1JU5_{ zfLGv50^m-#u?_|FtXLP5-f;+%-me>fWs%!T12GVW|5&xPuGV_G@op77bzQ0`Ma3II6cj;0@G{*_ zx6$l@WLq!9K8SDOg$Q2w06vsBTNM!*$jtot=1)l8KVIJeY+_#EvERRF+`CN~+)~_f zcio`v*4!Y8Ql(|4lGuxq7O`$fleEN}9cjIwL&2@>M%LYJOKqut=BA++$<$N1M{{SV z9&BziYZ^cE?XK1=*pBq-+)^B>n8>I&WVJ`e@>#4mHnuhzUW>Zd%6?zt$4SI~ zlcxhlC4TO|$3k0wD%~}7M%K!ZiRsf{m&+`_EmNcWDpuKnz~ahZga7dAl|W%-^~!;R z$uf$lI4EIk3?ryIC}TXYW(0;0`IS)TrpP}tglbN4Rm~aBg2TZCuXEfjpuhoC)~>H# zFtz@S>Dn`9pMU{c7+4fO0Z>Z^2t=Mc0&4*P0OtV!08mSlwUii>vw$yu33waFb$&wt z1h|3@lA>hju-BAmfjCGV5h+8q93HYw5uy}QM_|d8m%xHt3D{+J7m{e#O4`V2j<#tM zr-_uta^2Q+TPKZL38bS$>J__n)1+zBq-Wa3ZrY|-n%;+_{BHn|APLH8qfZ}ZXXee! zoA>_rzc+m4JDRw#Hi1R(`_BX|7?J@w}DM zF>dQQU2}9yj%!XlJ+7xuIfcB_n#gK7M~}5mjK%ZXMBLy#M!UMUrMK^dti7wUK3mA; zFyM@9@onhp=9ppXx^0+a7(K1q4$i{(u8tiYyW$!Bbn6oV5`vU}5vyRQ_4|#SE@+)) zk9CgOS|+D=p0Txw3El1-FdbLR<^1FowCbdGTInq0Mc>(;G; z#%f-$?9kmw=}g1wDm#OQM0@K7K=BR+dhUV`*uu!cl&ah;|OXFw^!{Y2X_bQ zcDjSDpb83BAM2-9I7B~dIIbfN_E3;EQ=3AY=q^DmQncV2xz0W-mjm8_VaHElK@EC- z!ktWFouH=5iBgisaA1U@3bj)VqB)H4VK|{N+2-(JHfiJCYX>+!y8B2Fm({k0cWxASSs+u_ov64=P?sTYo z&rYDDXH?fxvxb>b^|M;q%}uJ?X5}V30@O1vluQ19_ER5Rk+tl+2Akd;UJQt1HEy_A zDoA_jeuet!0YO{7M+Et4K+vY}8zNGM)1X58C@IM67?0@^Gy_2zq62KcgNW)S%~!UX z1LIg~{{L&cVH^pxv&RS87h5Dqhv+b?!UT{rMg#O##tHOouVIW{%W|QnHnAUyjkuZ( zR@l6M%}>V^I?kADpKlXW%QH2&OfWTY{0N_PLeRc9Mi3vb*?iSmEU7hC;l7%nHAo*u zcCtc$edXLFXlD(Sys;Aj`;iBG;@fw21qcpYFGU6DtN zH*Xmdk{4fK0AKi6FGJC#f0@j_)KD&L`tcGuKP_k_ zu+uZ@Sh<3$bA}GmGrYql`YBOYe}rLwZKP!xrdrur0ib3zAR%*So7rZjP$|`v$!nA9 zxOQ4sM|Is)T`iB$29KOE-0_Y!v(GZKhMia4am~e#u5PJbJTk5!5Jn35E$W1AVWB&z zA{r<8tP)wo%Vg0}o(EZ}Ts5eMgW$E9nUDxFyhPP(s8$YB7)%~lUan?sD~~9DckP11 zEa%9&uY)hvUwxUwb}pf|IT$VPqb9AAiAuw>G+8N86Ovlm%$~Fhhg1!#<%uJPW4P+L z>rOa{&N2gbFd3Fh-nnA8lL@IrHd6K33HFYag|7^pP;EZ&_CU5|tx*P)T5w<-hNeoB7VAth{E$^ zzh&!tb9x@TA^<6 zWYl=|`BSI?aM#~0G0T^KK!+74^cJ#Nj`srvw<<6EzM$Kx-86sp4;1hc2-blI9c0tmCMY}Qn=5b(4Vqv{|sKKb)cXA9B?~> z#9fzsZ29S1Tr62*LHahw(?8R{AQudS8<=zg^lz2qD}8im+_uhWqYUr=fMT#sIo${8 zzZfe2N&j7)tPfNL^8Z2}6)v8;x|<$fDzHr5?L0g@AOmYTwm%3~HQmw+c~!W5LEVM> z2|z;BF)jd7U&jQ0%D8~=0et;cR2&d~)H=6#Rr*B(V9$6xY#V}Z4=>PWem5wViJ&4B zv3xeU=0-BSSJgLq4Ssb;S7t=xC1%@8T#c5w$= z0*}ik;4@vwq3Am7=yuN-b_|MEpaRpI;Cvp9%i(}%s}RtlP5ojEwsLfL7&QhevV-Ns zj0eq<1@D5yAlgMl5n&O9X|Vqp%RY4oNyRFF7sWtO#6?E~bm~N|z&YikXC=I0E*8Z$ zv7PtWfjy*uGFqlA5fnR1Q=q1`;U!~U>|&X_;mk34hKqYAO9h_TjRFso_sn|qdUDA33j5IN=@U7M#9u zTvV5J{l0zdjRWGKB8J3Uz+|(f(HYHAjk#NQ1jL9!uha9;i4YYO5J$mewtTo9vVtPT zxqXvBInY?m4YD)~h~q$Ax!_EwZpqbZI3OP3;=4xaULDboazx{;=E*zl0g)CIxiwU0 zS+taYYlIHHMHZAe8xkWHvSjw;0&`NOTN%Xcr-ivm9Bz1h6ny%66)ZjF=M6S}>=v4~EuG0F; z50<8 zuJ7@5d0V_2pQVkF7Vq{{!dIm33#3Ft_}G2)yjM)!d^I{4d6C{M=mM$U&yqhi=!uOq z^+sms!NF^^FO?LLY1%(UAAuAQ;Js8WHnK=;BI0?Gj@F^p*@W>;sZ=u3l$xf8pzH;I z3P)vOmA?n#aMPBi8^%0|sj#w@`5rIzhQ!tSbr|=tr zz3XA)gH(s7qlZqzSnr3GpT_7Etp6(f@@<&&Cgd6@O_{P$>oL!s`$Ftx@?LJr&QNaX z8kwntH#$vkYg|R22_$?WFI((Ps;mBgX=;jxe4dv2B0W9@Ytx5X>gz7C*}oPKd5d(e zNI!)2=dpg8p7eD2T72>A&r(Oc#kZr8Zl0T=_oWh8{A0N9vXFPx)*^lID7MGYhmW53 z!69FY@je$)Lq+<@3s5PVD$*r5``M(QjgmT^@OmO6-sp%gHc}rSY5JLvw`8Gz=TflG z&)tw(+<*mIXdUgu%{CxCbK8#JowN2@0SO=M^#R!H6?`{v`CUe5FJ?SwyCTwGaWuck zZrbd*cS97n*}$HSL^o`QV`u2{Me=!GI9~_dUxVbO7s|jzu~fEkS2;SKy+&74sr^v1 zSfo!g?rt#d&g0|P1t9ae)DZ7~4AaMp^qVvE1qqxlUZ9nHsoy&~b@Pi;bSxIXMqg&h zucX*B)AZGlZ<_wNNMB2M8@&ts^)Xsm@z<+UH@_KAm7Vk&{!iU}$6y2}y>=s3q`$h% zKQ|De3gWd_T4=Rw*ODsRR%(-Nn7U+pH|>$_UfL(yBps0LFddieaXJBi>k?^{mF+lL zvMtd2WXr!S_d)uoY)gJo;16IEvvuH(Z&YlEF~4MtgVERw{mtdnP$YGQLX5QNiKcH( z)87Fhz);ga;3ro8{wMqZN=5qDvS|E7)4xm6|Cyb+fwKtysRw&ATYU!+B2TOXK$*G3 zl~^PtLwPV-6rR$Fz;;o8z>*(s7WJjAq^m9+Eguv+(JTTuX-2FlipGi#>xbCfU@qZd zcZ!5pBz#h2ErNo*n((t*0g$hCrXHnm|i`@X6!d0j(RK8a`Hw z2l5S1eVl@8los!kPhF(7@ijcCcL%PBB!<=~MKK)m$2=`T0Eu_#R=NXIH=h{{`4iqL za>{Mu8oi!s7Kf(A;TzGAKje#F5l5QETXFpg?7)M8D4Qw*a~?Z-8SK4tke9LDVAp2x zFf0l}5RJ{^1U}<`@`|I)B2%(-WLk{fsNVS{3NYNyg}nR)ue=tyK_MEWlVVgDvV8=; z&C^-g=a&0t>2a|ceQr0P|8{y#_POQ$^YjVX=a&1Qq|36;E%!Nkxz8>4U!u>;KDXTe zI(~qWgw0KJDS&EAzCZPW9FjGEvA{wpmQp~lptydug=H;9(okb!NK8l? zHP&F{-*kJ}F6>9y4~#K#AzXzT#l#<8fEQ&vLyM5mhMnx}&YAZ)?@Z3jpTEC;16YG8 zaC~(1rus>5N^76|mcF4|yZVZ51zyK-W$XmL;RP+?ct|eEh==&9(Oh4zSZhyM8&=Qw-Nbb{5VfUI;UW39;}eCBZ*%mJ!ic>%UR`~> zS~Xg9sDB=X5J)$IB(&&-h`VEK=D09=41jZa}_^O(<+( z@dU-oVCobs4fZRXVC6E#mw-{_oB9V(O9u$-3H${-0ssIWldzOjf5Si!#%E3c#9Bq4 zK-p6(O+zb|P(={Ilb|T{zS&HZZ8w{+o7RKa2k|XD2_Ad^A4;5v9-M{w_q=X}6rk(Ww~N);x^iv)>V)F>R%WhPu8Gn7lW${nB1 zg?2dLWg6t73{<@%f1XT6a(qfz8~x4CS6UNrnFvN?(WJ^CT4hqAYqXBuA|4G-hEb5< znm_x%7<3+rm6dp{G%`3UY#OFkBpSmQoM5x6G zZPijL*Z>uQZW67A|R9w^IzUkPhic=6I zm%(-`|RxlHTyT__;TIpHtPB288^%``Bpy}I=`(B1HzbS#S^Q*EAx z4u+7Zxc(*~GMtIG28o~(XLX!G7eiM=)yPxBISPB#v`zndJ?z~G&ZAdH4=ynDG-o(t zf4fzG(U*c(G`yvvwG>!)eOpH#E;0lxhZh*mH;kJ6>$aB=Q(^iUP8ycui3r|Rf%`B( z*o|DLxmTuAG{kibs-%KzVslaWt>u!4${j*dfuna=Gjl z-rPoCZgwb{OKc%p!#g#+w~fKv?Jbb;@C$svFq?dVj~E_foIb8G#ZpC_6H8jHE-TcOH8ok& z$_(dqrZ8$S41ZRskt|hN>C9-<3~{+a6@$%*btze+^`y*m6tmfMDYJxHJ?HS1hN9qv zQKiW=4w)*+Dr35=N;rGYbOYDI`P}@%d@fmL=i)~n5CZ;!*3e7rxvVe(QB9Xpl1|GC zOI_%+UT1phon<#QwIWLmy|rgAcnAbf={Zd)RFzVD#eY*)GQH4GKq$+GsmsL%*AWQp zwp1!JQ~UXy6{OnZ8+c#>;oX0k3MSuir|u0ks{}^drwUb?S;`g~H3HuEa^1?rJxd$F z6)!aX?5$j5TEiqjb_k4}Q$;RQlWnyn+Se6~9ueqYl~vhXBhVX*9|$l4qkizhP29?h z{QB1J_J7HKVLN~Fa_`l)55@)X!;Jyxg9!qIPO13*3?JMUK(K;$1qfK)JpqO+PZS^s zA@1E5APmFYdq7~wVCL49(uHDIYsWX`g8{Bj5Ez!O>a7Bd#Nuwn95&p5ney!kDT`Tjequm6|t7FBi0~PR z6Wig3nn38_y7nWD7huBpkHix@;%Pja_}Um_SjHe0FufTsH zh<`!cP7sD3`~&nSW}7hU#OEMs$3tlO0)2^Z5cy(<=ON{WM;!d2D?aJqX?J|m!85M- zqJuBFYuyW`Uiz6>ia_{?WJyb4dc@CbIt!Pnra3m$dw zXRz*u+l|G0iQgXR{R2=-2MAKixJd5;00RI}29vRr9FsMkH-Gzg6GasMhCUKPcr-0< z&=`fbY0~hJS_-JNfL8jVDPW3#+hme1-R#EQO zr8BZ3nX;ya(|=lm)4a|VE*YE_hb1E%ALj89Bbzn?ZAnQncqoubh0{_dEDIY$EiLQg z8#a-y-kQfJvx-6!#;_D#PeZPzWR-JWR#P-P%5{T$(R^$3#^%;=f{zAHxWO1aQ7x;- z`7T-E3;|6~MN+zyPdxV3$Vxo7itK3kKSCU^`H(lUU)HoE~>D^3peJ9 zuVr3%Fn>>ctzhsL$Kk>%3X+e@hH2QY2fert_j|A!e&RsUq(mV+F9KNTAuA6u%-6Y; z*g-H|+p8-Kbq$SO1^T0=sPGmq)?lpw)Ds}Z%|1!zs_F2%C=bXp;zc8Z!f_KrYf;WN zN3vy|dpuNG0LQOs)}xS$X>lXZJO(XFg%Lw}jj7!kuOW-bVAN;}iCM3;4a0JyP`}o{*?zz8TXrym$&hgQj z12KArYd6i?<3&1Ovo%~DGAvs;OlR5g+l0bxPkdYrEVN?ZTk01}-;e1@er|}f|r@K$2{%}g8Tje|b zX@-EZnaZfTe{w9_pRL@lnb0BdP3|KZC6I2xM*}-L@okCg>+|siC&#)tUK8JylN{pH zDt4t7$(`}O@W*eezOr|UPZfB-xzHgPxx;@~pr@Rl0<053d}L7P^Ux-lBPM5a0wZ;u zyHYF%hN%t7nsAPpTW^4yYmzxjkz(`o@oIu_!z+2JaD$t+l#O{xRPj3b0%sa|$oe7N z|2j)zd)Jk6rh2cN$=6>JI6F+qLFCIP2f1sqltoi2fx3=*dzthQT&};TZQffaZvIcM zBvDRuVcN8x6#22F19^~JzM15_EzmvxB4uAYo7R+>b+i!w!E4}B$kB|Bn0BW7U9;pJ zzm;cCmYp73&uc$79`Vu6a0YoLUsUB6c+aAq{p+PxONS#uhv$Ay6b{uT_+110eAryY zo{!hBr+ZjM%T$@-=Q*izvg1Q1&Fw|4WBpaJhj0gb+D%Fni(*duGud~?Xl7m@JyKuP z!bnleKIoLuRcpO1DXOF_uU^L|I`bwWy_aO%ltvtOCpD$2UC+#Xm6cn1by9{*_vvlR z`{3%c_1qKUmO6VTo%3im-=9_IzW3wtI1QipTl|LI)0xiBdG9;+A0D)6$sD4;R#jH} z8JJRMgRX=(oJcy}@H@dZeEo}f7mqAtb=srZjOL)&XB2MmeXp(O)2?cB=T=Plc#1C7 zYwJ{U)s-JAJ6V+IjS}UMDKw>jUo=x^Q&w*GjvudQ)0^(R%}?XZQj#ZQouoylcilA@ zQnM&=8PxbII9Mi0UWnJV=1)rHHYOFg(@KqxIlbB6Pxet)7+y0zmVWJ7f=7Z1d#c}q zcXfv4M8#zrD`a^(&euYtEh}k#FGeWM#oy|EdFJ~G!t($@fef)Rp~!;&ucW(o^kbrN za*}YcQ(b5WqHl8S$h9d7aS{BUCt*d+c|HECGQFNniZ1nny50ys3*MYvsv*(d=*6@( zrFBK!4RYfFrs7>*HX*~)krCaU$n@`QnR4+Qh+DNOAh%Y?eM2*Mh%-svH02$FyZqAU z;yqkQN6_^X@~?+}T`k3mk=ClA9qP>MVBgNWJm%-9Ku_RU=}WKCQPTeTQOhaMsBiSP zB}9=4q?$u7Rc;-Qay+QUe1O|3c-?eMaLXLN7-C?CLU9nvh+(+Q_&L$ehjZ74@{r%Wq;&fY;hqF~zb#3xfeTHN#kN;qL@Jzpeo?KBQ zh>aXE ztCt71#V~MOy%Fr;E2xI=ucSmE1L11K5KkWBYml2hGfcU{ zI#lR;f)r?7r;3QJQ58p=Ly#4Q9Z*_i4Hqhfm*>D*W5A=zV$p)jTVQe6CsI3Bv3;W$ zRM*r-RhM!SAqcDbgO(Wn6UkjiHUYm7%?BDw2)I7oWo43JzQh0)2eBc2f^G9tJq&DP zpoG?phJGRIIJ%LJGR3rqVJQB?4Wh~d`~{lW1SCq8aBL&Au*cXKRauHT`FvL}HZQ73 zB56+Q@KR{O=OA+n7%#Q`0`=MlUzI80a66D6G(l*Y4lK48|KFaxY6;59sH$RfYveg3 zrV&~;NVzK5Ty6_5*(5^{2?J@`0$#ZnyyVaeK_(c$iwv5plt7pRx&u_jE~LojV5_45 zi3$Svwo)1#*lmLL%yP>{bBySOBPzN8f2c5p7hHrsnFt{XSQv1p>ms-bUh=cBYBIUu_QRVaFzKF D0|+Ag diff --git a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties index d4081da4..23449a2b 100644 --- a/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties +++ b/firmware_updater/updater/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/firmware_updater/updater/gradlew b/firmware_updater/updater/gradlew index faf93008..adff685a 100755 --- a/firmware_updater/updater/gradlew +++ b/firmware_updater/updater/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/firmware_updater/updater/gradlew.bat b/firmware_updater/updater/gradlew.bat index 9d21a218..c4bdd3ab 100644 --- a/firmware_updater/updater/gradlew.bat +++ b/firmware_updater/updater/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell From cb1f3efd2007a3c85fad2c124be6457cebac1ede Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 05:27:04 +0100 Subject: [PATCH 345/359] [Updater] Upgrade to `net.harawata:appdirs:1.5.0` --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index cfcff94c..36032f73 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -118,7 +118,7 @@ dependencies { implementation 'io.calimero:calimero-usb:2.6' // find specific directories under linux and windows - implementation 'net.harawata:appdirs:1.3.0' + implementation 'net.harawata:appdirs:1.5.0' // For search in byte array implementation 'com.google.guava:guava:33.4.0-jre' From 2263650b2ffb06987efcf905aec87d3317093f72 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 05:27:47 +0100 Subject: [PATCH 346/359] [Updater] Upgrade to `slf4j-jdk-platform-logging:2.0.17` --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 36032f73..195e25e1 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -130,7 +130,7 @@ dependencies { implementation 'ch.qos.logback:logback-classic:1.5.19' // Redirect System.Logger (used by calimero >=3.0) to slf4J (logback) - implementation 'org.slf4j:slf4j-jdk-platform-logging:2.0.16' + implementation 'org.slf4j:slf4j-jdk-platform-logging:2.0.17' // Console ansi color support implementation 'org.jline:jline-terminal-jansi:3.28.0' From 5d3da80da11575beea1a158d791b2258b555161f Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 05:28:43 +0100 Subject: [PATCH 347/359] [Updater] Upgrade to com.google.guava:guava:33.5.0-jre --- firmware_updater/updater/build.gradle | 2 +- .../selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 195e25e1..bf8a6940 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -121,7 +121,7 @@ dependencies { implementation 'net.harawata:appdirs:1.5.0' // For search in byte array - implementation 'com.google.guava:guava:33.4.0-jre' + implementation 'com.google.guava:guava:33.5.0-jre' // command line option implementation 'commons-cli:commons-cli:1.9.0' diff --git a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java index 6a34c610..28e91f82 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java +++ b/firmware_updater/updater/src/org/selfbus/updater/devicemgnt/ReflectKNXnetIPTunnel.java @@ -3,8 +3,6 @@ import com.google.common.collect.Lists; import tuwien.auto.calimero.knxnetip.KNXnetIPTunnel; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.lang.reflect.Field; import java.util.List; @@ -66,8 +64,8 @@ private Field getField(Object obj, String fieldName) { return null; } - private static Iterable getFieldsUpTo(@Nonnull Class startClass, - @Nullable Class exclusiveParent) { + private static Iterable getFieldsUpTo(Class startClass, + Class exclusiveParent) { List currentClassFields = Lists.newArrayList(startClass.getDeclaredFields()); Class parentClass = startClass.getSuperclass(); From 419952196dd071241c2bc2fb67f8f971682f122d Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 05:47:33 +0100 Subject: [PATCH 348/359] [Updater] Upgrade to jackson-databind:2.20.1 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index bf8a6940..f77a69cb 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -139,7 +139,7 @@ dependencies { implementation files('libs/forms_rt.jar') // GUI settings - implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.20.1' // Intel hex parser // commit 9dec823 of 2023/02/27 From 47ee87191caa1607637fde75a8d33e7f6492db1c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 05:48:24 +0100 Subject: [PATCH 349/359] [Updater] Upgrade to logback-classic:1.5.21 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index f77a69cb..b36c691a 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -127,7 +127,7 @@ dependencies { implementation 'commons-cli:commons-cli:1.9.0' // console and file logging - implementation 'ch.qos.logback:logback-classic:1.5.19' + implementation 'ch.qos.logback:logback-classic:1.5.21' // Redirect System.Logger (used by calimero >=3.0) to slf4J (logback) implementation 'org.slf4j:slf4j-jdk-platform-logging:2.0.17' From 82efb5e536eb53718cb2a325f095afa8622fa5d8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 05:56:11 +0100 Subject: [PATCH 350/359] [Updater] Upgrade to jline-terminal-jansi:3.30.6 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index b36c691a..17433f38 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -133,7 +133,7 @@ dependencies { implementation 'org.slf4j:slf4j-jdk-platform-logging:2.0.17' // Console ansi color support - implementation 'org.jline:jline-terminal-jansi:3.28.0' + implementation 'org.jline:jline-terminal-jansi:3.30.6' // GUI implementation files('libs/forms_rt.jar') From 46d456a60d7088e27fd1592b9ae5f7f57c68899e Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 13:14:55 +0100 Subject: [PATCH 351/359] [Updater] Upgrade commons-cli:1.11.0 --- firmware_updater/updater/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 17433f38..d6c3aa26 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -124,7 +124,7 @@ dependencies { implementation 'com.google.guava:guava:33.5.0-jre' // command line option - implementation 'commons-cli:commons-cli:1.9.0' + implementation 'commons-cli:commons-cli:1.11.0' // console and file logging implementation 'ch.qos.logback:logback-classic:1.5.21' From dd702d23252a324ceb952b8c9b18f85cc2faa3e2 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 13:15:58 +0100 Subject: [PATCH 352/359] [Updater] Fix commons-cli `.build()` deprecation warning --- .../src/org/selfbus/updater/CliOptions.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index f1805b3a..3c74293d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -203,129 +203,129 @@ private CliOptions() { .numberOfArgs(2) .required(false) .type(Number.class) - .desc("dump a flash range in intel(R) hex to the serial port of the MCU. Works only with DEBUG version of the bootloader.").build(); + .desc("dump a flash range in intel(R) hex to the serial port of the MCU. Works only with DEBUG version of the bootloader.").get(); Option fileName = Option.builder(OPT_SHORT_FILENAME).longOpt(OPT_LONG_FILENAME) .argName("filename") .numberOfArgs(1) .required(false) .type(String.class) - .desc("Filename of hex file to program").build(); + .desc("Filename of hex file to program").get(); Option localhost = Option.builder(OPT_SHORT_LOCALHOST).longOpt(OPT_LONG_LOCALHOST) .argName("localhost") .numberOfArgs(1) .required(false) .type(String.class) - .desc("local IP/host name").build(); + .desc("local IP/host name").get(); Option localport = Option.builder(OPT_SHORT_LOCALPORT).longOpt(OPT_LONG_LOCALPORT) .argName("localport") .numberOfArgs(1) .required(false) .type(Number.class) - .desc("local UDP port (default system assigned)").build(); + .desc("local UDP port (default system assigned)").get(); Option port = Option.builder(OPT_SHORT_PORT).longOpt(OPT_LONG_PORT) .argName("port") .numberOfArgs(1) .required(false) .type(Number.class) - .desc(String.format("UDP port on (default %d)", KNXnetIPConnection.DEFAULT_PORT)).build(); + .desc(String.format("UDP port on (default %d)", KNXnetIPConnection.DEFAULT_PORT)).get(); Option ft12 = Option.builder(OPT_SHORT_FT12).longOpt(OPT_LONG_FT12) .argName("COM-port") .numberOfArgs(1) .required(false) - .desc("use FT1.2 serial communication").build(); + .desc("use FT1.2 serial communication").get(); Option tpuart = Option.builder(OPT_SHORT_TPUART).longOpt(OPT_LONG_TPUART) .argName("COM-port") .numberOfArgs(1) .required(false) - .desc("use TPUART serial communication (experimental, needs serialcom or rxtx library in java.library.path)").build(); + .desc("use TPUART serial communication (experimental, needs serialcom or rxtx library in java.library.path)").get(); Option usbInterface = Option.builder(null).longOpt(OPT_LONG_USB) .argName("vendorId:productId") .numberOfArgs(1) .required(false) - .desc("use USB-Interface. Specify VendorID and ProductID e.g. 147B:5120 for the Selfbus USB-Interface (experimental)").build(); + .desc("use USB-Interface. Specify VendorID and ProductID e.g. 147B:5120 for the Selfbus USB-Interface (experimental)").get(); Option medium = Option.builder(OPT_SHORT_MEDIUM).longOpt(OPT_LONG_MEDIUM) .argName("tp1|rf") .numberOfArgs(1) .required(false) .type(TPSettings.class) - .desc(String.format("KNX medium [tp1|rf] (default %s)", getMedium())).build(); ///\todo not all implemented missing [tp0|p110|p132] + .desc(String.format("KNX medium [tp1|rf] (default %s)", getMedium())).get(); ///\todo not all implemented missing [tp0|p110|p132] Option optProgDevice = Option.builder(OPT_SHORT_PROG_DEVICE).longOpt(OPT_LONG_PROG_DEVICE) .argName("x.x.x") .numberOfArgs(1) .required(false) .type(IndividualAddress.class) - .desc(String.format("KNX device address in bootloader mode (default %s)", getProgDevicePhysicalAddress().toString())).build(); + .desc(String.format("KNX device address in bootloader mode (default %s)", getProgDevicePhysicalAddress().toString())).get(); Option device = Option.builder(OPT_SHORT_DEVICE).longOpt(OPT_LONG_DEVICE) .argName("x.x.x") .numberOfArgs(1) .required(false) .type(IndividualAddress.class) - .desc("KNX device address in normal operating mode (default none)").build(); + .desc("KNX device address in normal operating mode (default none)").get(); Option ownPhysicalAddress = Option.builder(OPT_SHORT_OWN_ADDRESS).longOpt(OPT_LONG_OWN_ADDRESS) .argName("x.x.x") .numberOfArgs(1) .required(false) .type(IndividualAddress.class) .desc(String.format("own physical KNX tunnel address (default %s). Required for some IP interfaces that also use their own address as the tunnel address, e.g. Loxone Miniserver Gen 1.", - getOwnPhysicalAddress().toString())).build(); + getOwnPhysicalAddress().toString())).get(); Option uid = Option.builder(OPT_SHORT_UID).longOpt(OPT_LONG_UID) .argName("uid") .numberOfArgs(1) .required(false) - .desc(String.format("send UID to unlock (default: request UID to unlock). Only the first %d bytes of UID are used", UPDProtocol.UID_LENGTH_USED)).build(); + .desc(String.format("send UID to unlock (default: request UID to unlock). Only the first %d bytes of UID are used", UPDProtocol.UID_LENGTH_USED)).get(); Option delay = Option.builder(null).longOpt(OPT_LONG_DELAY) .argName("ms") .numberOfArgs(1) .required(false) .type(Number.class) - .desc(String.format("delay telegrams during data transmission to reduce bus load, valid 0-500ms, default %d", Updater.DELAY_MIN)).build(); + .desc(String.format("delay telegrams during data transmission to reduce bus load, valid 0-500ms, default %d", Updater.DELAY_MIN)).get(); Option reconnect = Option.builder(null).longOpt(OPT_LONG_RECONNECT) .argName("ms") .numberOfArgs(1) .required(false) .type(Number.class) .desc(String.format("pause between a KNX connection reconnect, valid %d - %dms, default %d", RECONNECT_MIN_MS, - RECONNECT_MAX_MS, RECONNECT_MIN_MS)).build(); + RECONNECT_MAX_MS, RECONNECT_MIN_MS)).get(); Option reconnectSeqNumber = Option.builder(null).longOpt(OPT_LONG_RECONNECT_SEQ_NUMBER) .argName("#sequence") .numberOfArgs(1) .required(false) .type(Number.class) .desc(String.format("Reconnect KNX IP tunnel on sequence number, valid %d - %d, default %d. May help with some IP-Interfaces e.g. for Loxone Miniserver Gen 1. set to 245", - RECONNECT_MIN_SEQ_NUMBER, RECONNECT_MAX_SEQ_NUMBER, getReconnectSeqNumber())).build(); + RECONNECT_MIN_SEQ_NUMBER, RECONNECT_MAX_SEQ_NUMBER, getReconnectSeqNumber())).get(); Option logLevel = Option.builder(OPT_SHORT_LOGLEVEL).longOpt(OPT_LONG_LOGLEVEL) .argName("TRACE|DEBUG|INFO") .numberOfArgs(1) .required(false) .type(String.class) - .desc(String.format("Logfile logging level [TRACE|DEBUG|INFO] (default %s)", defaultLogLevel.toString())).build(); + .desc(String.format("Logfile logging level [TRACE|DEBUG|INFO] (default %s)", defaultLogLevel.toString())).get(); Option userId = Option.builder(null).longOpt(OPT_LONG_USER_ID) .argName("id") .numberOfArgs(1) .required(false) .type(Number.class) - .desc(String.format("KNX IP Secure tunneling user identifier (1..127) (default %d)", getKnxSecureUserId())).build(); + .desc(String.format("KNX IP Secure tunneling user identifier (1..127) (default %d)", getKnxSecureUserId())).get(); Option userPasswd = Option.builder(null).longOpt(OPT_LONG_USER_PASSWORD) .argName("password") .numberOfArgs(1) .required(false) .type(Number.class) - .desc("KNX IP Secure tunneling user password (Commissioning password/Inbetriebnahmepasswort), quotation marks (\") in password may not work").build(); + .desc("KNX IP Secure tunneling user password (Commissioning password/Inbetriebnahmepasswort), quotation marks (\") in password may not work").get(); Option devicePasswd = Option.builder(null).longOpt(OPT_LONG_DEVICE_PASSWORD) .argName("password") .numberOfArgs(1) .required(false) .type(Number.class) - .desc("KNX IP Secure device authentication code (Authentication Code/Authentifizierungscode) quotation marks(\") in password may not work").build(); + .desc("KNX IP Secure device authentication code (Authentication Code/Authentifizierungscode) quotation marks(\") in password may not work").get(); Option knxPriority = Option.builder(null).longOpt(OPT_LONG_PRIORITY) .argName("SYSTEM|URGENT|NORMAL|LOW") .numberOfArgs(1) .required(false) .type(String.class) - .desc(String.format("KNX telegram priority (default %s)", getPriority().toString().toUpperCase())).build(); + .desc(String.format("KNX telegram priority (default %s)", getPriority().toString().toUpperCase())).get(); Option blockSizeOption = Option.builder(OPT_SHORT_BLOCKSIZE).longOpt(OPT_LONG_BLOCKSIZE) .argName("256|512|1024") @@ -333,7 +333,7 @@ private CliOptions() { .numberOfArgs(1) .required(false) .type(Number.class) - .desc(String.format("Block size to program (default %d bytes)", getBlockSize())).build(); + .desc(String.format("Block size to program (default %d bytes)", getBlockSize())).get(); Option logStatistic = new Option(null, OPT_LONG_LOGSTATISTIC, false, "show more statistic data"); From e6141237b86ef0a63642f3cabc71973aaa989c3c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 14:26:17 +0100 Subject: [PATCH 353/359] [Updater] Add todo for `HelpFormatter` deprecation warning --- firmware_updater/updater/build.gradle | 2 +- .../src/org/selfbus/updater/CliOptions.java | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index d6c3aa26..59a6489b 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -124,7 +124,7 @@ dependencies { implementation 'com.google.guava:guava:33.5.0-jre' // command line option - implementation 'commons-cli:commons-cli:1.11.0' + implementation 'commons-cli:commons-cli:1.11.0' // todo On update check todo in CliOptions::helpToString // console and file logging implementation 'ch.qos.logback:logback-classic:1.5.21' diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index 3c74293d..f719b3a1 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -128,7 +128,6 @@ public class CliOptions { private final Options cliOptions = new Options(); // define parser CommandLine cmdLine; - final HelpFormatter helper = new HelpFormatter(); private String helpHeader = ""; private String helpFooter = ""; @@ -737,6 +736,22 @@ public String reconstructCommandLine() { public String helpToString() { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); + +// todo commons-cli 1.11.0 does not yet support setWidth or Printwriter/StringWriter +// import org.apache.commons.cli.help.HelpFormatter +// final HelpFormatter helper = org.apache.commons.cli.help.HelpFormatter.builder() +// //.setWidth(PRINT_WIDTH) +// .setShowSince(false) +// .setComparator((optionA, optionB) -> 0) // do not sort options +// .get(); +// try { +// // printHelp has no pw (PrintWriter) since 1.10.0 +// helper.printHelp(pw, helpApplicationName + " ", +// helpHeader + ":", cliOptions, helpFooter, false); +// } catch (IOException e) { +// throw new RuntimeException(e); +// } + final HelpFormatter helper = new org.apache.commons.cli.HelpFormatter(); helper.setWidth(PRINT_WIDTH); helper.setOptionComparator(null); helper.printHelp(pw, helper.getWidth(), helpApplicationName + " ", From 0700e49e6ccac76edb97e704ab40e0e8ccc63aa8 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 14:35:42 +0100 Subject: [PATCH 354/359] Add dependabot for Updater --- .github/dependabot.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..e17ef26d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "gradle" + directory: "/firmware_updater/updater" + schedule: + interval: "weekly" + day: "thursday" + time: "12:53" + ignore: + # Exclude java-intelhex-parser + - dependency-name: "com.github.j123b567:java-intelhex-parser" + versions: [">= 0"] # Prevent updating due to its specific pinned commit \ No newline at end of file From cb110d83b15d5310b3045ece4bf518c18b37f65c Mon Sep 17 00:00:00 2001 From: Darthyson Date: Tue, 18 Nov 2025 14:36:03 +0100 Subject: [PATCH 355/359] [Updater] Bump version to 1.27 --- firmware_updater/updater/build.gradle | 2 +- firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware_updater/updater/build.gradle b/firmware_updater/updater/build.gradle index 59a6489b..1b139699 100644 --- a/firmware_updater/updater/build.gradle +++ b/firmware_updater/updater/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.selfbus' -version = '1.26' ///\todo Change also in ToolInfo.java (versionMajor, versionMinor) +version = '1.27' ///\todo Change also in ToolInfo.java (versionMajor, versionMinor) description = 'Selfbus Updater, a tool to update the firmware of Selfbus devices via the KNX Bus.' java { diff --git a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java index 23af5477..dae5d2cb 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/ToolInfo.java @@ -14,7 +14,7 @@ public final class ToolInfo { private static final long versionMajor = 1; ///\todo change also in build.gradle - private static final long versionMinor = 26; + private static final long versionMinor = 27; private static final long minMajorVersionBootloader = 1; ///\todo change also in ../README.md private static final long minMinorVersionBootloader = 0; From 9cfc565aebe0f7c3563d5d8b782af440d77624e3 Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 30 Nov 2025 12:26:34 +0100 Subject: [PATCH 356/359] [Updater] Sanitize cliOption `--DUMPFLASH` start and end address --- .../src/org/selfbus/updater/CliOptions.java | 38 ++++++++++++++++--- .../updater/src/org/selfbus/updater/Mcu.java | 7 ++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java index f719b3a1..4c1b3460 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java +++ b/firmware_updater/updater/src/org/selfbus/updater/CliOptions.java @@ -30,6 +30,8 @@ import java.util.Objects; import static org.fusesource.jansi.Ansi.*; +import static org.selfbus.updater.Mcu.FLASH_END_ADDRESS; +import static org.selfbus.updater.Mcu.FLASH_START_ADDRESS; import static org.selfbus.updater.logging.Color.*; /** @@ -438,8 +440,8 @@ private void parse(final String[] args) throws ParseException, KNXFormatExceptio if (cmdLine.hasOption(OPT_LONG_DUMPFLASH)) { String[] optArgs = cmdLine.getOptionValues(OPT_LONG_DUMPFLASH); - setDumpFlashStartAddress(Long.decode(optArgs[0])); - setDumpFlashEndAddress(Long.decode(optArgs[1])); + setDumpFlashStartAddress(optArgs[0]); + setDumpFlashEndAddress(optArgs[1]); } setFlashingFullModeIsSet(cmdLine.hasOption(OPT_LONG_FULL)); @@ -570,7 +572,7 @@ private void validateCliParameters() throws CliInvalidException { ansi().fgBright(WARN), OPT_LONG_NAT, OPT_LONG_TUNNEL_V1, ansi().reset())); } - // nat not allowed with tunneling v2 + // nat isn't allowed with tunneling v2 if (getNatIsSet() && (getTunnelingV2isSet())) { throw new CliInvalidException(String.format("%sOption --%s can not be used together with --%s%s", ansi().fgBright(WARN), OPT_LONG_NAT, OPT_LONG_TUNNEL_V2, ansi().reset())); @@ -1035,6 +1037,18 @@ private void setDumpFlashStartAddress(long dumpFlashStartAddress) { logger.debug("dumpFlashStartAddress={}", getDumpFlashStartAddress()); } + private void setDumpFlashStartAddress(String dumpFlashStartAddress) { + try { + setDumpFlashStartAddress(Long.decode(dumpFlashStartAddress)); + } + catch (NumberFormatException e) { + setDumpFlashStartAddress(FLASH_START_ADDRESS); + logger.warn("{}option --{} start address {} is invalid => set to default 0x{}{}", + ansi().fgBright(WARN), OPT_LONG_DUMPFLASH, dumpFlashStartAddress, + String.format("%08X",getDumpFlashStartAddress()), ansi().reset()); + } + } + public long getDumpFlashEndAddress() { return dumpFlashEndAddress; } @@ -1044,6 +1058,18 @@ private void setDumpFlashEndAddress(long dumpFlashEndAddress) { logger.debug("dumpFlashEndAddress={}", getDumpFlashEndAddress()); } + private void setDumpFlashEndAddress(String dumpFlashEndAddress) { + try { + setDumpFlashEndAddress(Long.decode(dumpFlashEndAddress)); + } + catch (NumberFormatException e) { + setDumpFlashEndAddress(FLASH_END_ADDRESS); + logger.warn("{}option --{} end address {} is invalid => set to default 0x{}{}", + ansi().fgBright(WARN), OPT_LONG_DUMPFLASH, dumpFlashEndAddress, + String.format("%08X", getDumpFlashEndAddress()), ansi().reset()); + } + } + public boolean getHelpIsSet() { if (cmdLine == null) return false; @@ -1092,7 +1118,7 @@ private void setKnxSecureUserId(int knxSecureUserId) { } if (knxSecureUserId >= 1 && knxSecureUserId <= 127) { - logger.debug("{}=***", OPT_LONG_USER_ID); // log only that it´s, but not the actual value + logger.debug("{}=***", OPT_LONG_USER_ID); // log only that it's, but not the actual value this.knxSecureUserId = knxSecureUserId; } else { @@ -1124,7 +1150,7 @@ public String getKnxSecureUserPassword() { private void setKnxSecureUserPassword(String knxSecureUserPassword) { this.knxSecureUserPassword = knxSecureUserPassword; if (!nonNullString(this.knxSecureUserPassword).isBlank()) - logger.debug("{}=****", OPT_LONG_USER_PASSWORD); // log only that it´s, but not the actual value + logger.debug("{}=****", OPT_LONG_USER_PASSWORD); // log only that it's, but not the actual value else logger.debug("{}=", OPT_LONG_USER_PASSWORD); } @@ -1136,7 +1162,7 @@ public String getKnxSecureDevicePassword() { private void setKnxSecureDevicePassword(String knxSecureDevicePassword) { this.knxSecureDevicePassword = knxSecureDevicePassword; if (!nonNullString(this.knxSecureDevicePassword).isBlank()) - logger.debug("{}=*****", OPT_LONG_DEVICE_PASSWORD); // log only that it´s, but not the actual value + logger.debug("{}=*****", OPT_LONG_DEVICE_PASSWORD); // log only that it's, but not the actual value else logger.debug("{}=", OPT_LONG_DEVICE_PASSWORD); } diff --git a/firmware_updater/updater/src/org/selfbus/updater/Mcu.java b/firmware_updater/updater/src/org/selfbus/updater/Mcu.java index 25929f1d..94a298d1 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Mcu.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Mcu.java @@ -12,6 +12,13 @@ public final class Mcu { public static final int MAX_PAYLOAD = MAX_ASDU_LENGTH - 1; /** Selfbus ARM controller flash page size */ public static final int FLASH_PAGE_SIZE = 256; + /** Selfbus ARM controller flash start address */ + public static final int FLASH_START_ADDRESS = 0; + /** Selfbus ARM controller flash size in bytes*/ + public static final int FLASH_SIZE_BYTES = 0x10000; + /** Selfbus ARM controller flash end address */ + public static final int FLASH_END_ADDRESS = FLASH_START_ADDRESS + FLASH_SIZE_BYTES - 1; + /** Size in bytes to flash with UPD_PROGRAM */ public static final int UPD_PROGRAM_SIZE = 1024; /** Vector table end of the mcu */ From a6aa764220c0d1ae31e4f0859e6c3b4d9fd207cf Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 30 Nov 2025 15:25:45 +0100 Subject: [PATCH 357/359] [Updater] Fix missing override annotation --- .../updater/src/org/selfbus/updater/SBManagementClientImpl.java | 1 + firmware_updater/updater/src/org/selfbus/updater/Updater.java | 2 ++ .../src/org/selfbus/updater/bootloader/BootloaderIdentity.java | 1 + .../updater/src/org/selfbus/updater/progress/ProgressInfo.java | 1 + 4 files changed, 5 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index cac50fd7..25c60ee8 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -41,6 +41,7 @@ private class SBLinkListener implements NetworkLinkListener this.linkLogger = (Logger) LoggerFactory.getLogger(SBLinkListener.class.getName() + " " + link.getName()); } + @Override public void indication(final FrameEvent e) { CEMILData cemilData = getCEMILData(e, linkLogger); diff --git a/firmware_updater/updater/src/org/selfbus/updater/Updater.java b/firmware_updater/updater/src/org/selfbus/updater/Updater.java index 4211ce27..511ecfeb 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/Updater.java +++ b/firmware_updater/updater/src/org/selfbus/updater/Updater.java @@ -145,6 +145,7 @@ void unregister() { } } + @Override public void run() { try { t.interrupt(); @@ -182,6 +183,7 @@ public void run() { * * @see java.lang.Runnable#run() */ + @Override public void run() { try { final String hexFileName = cliOptions.getFileName(); diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java index 801510d4..9fcb5290 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderIdentity.java @@ -47,6 +47,7 @@ public static BootloaderIdentity fromArray(byte[] parse) { return new BootloaderIdentity(vMajor, vMinor, versionSBLibMajor, versionSBLibMinor, features, applicationFirstAddress); } + @Override public String toString() { return String.format("v%s, sbLib v%s, Features: 0x%04X, App-start: 0x%04X", getVersion(), getVersionSBLib(), features(), applicationFirstAddress()); diff --git a/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java b/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java index 16ff263d..7da717d3 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java +++ b/firmware_updater/updater/src/org/selfbus/updater/progress/ProgressInfo.java @@ -71,6 +71,7 @@ public String getHeader() { return " Done Speed Avg Min Max Time"; } + @Override public String toString() { return String.format("%5.1f%% %s %s %5.1f %5.1f %tM:% Date: Sun, 30 Nov 2025 15:26:22 +0100 Subject: [PATCH 358/359] [Updater] Add `SBManagementClientImpl.toString()` --- .../selfbus/updater/SBManagementClientImpl.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java index 25c60ee8..d7788780 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java +++ b/firmware_updater/updater/src/org/selfbus/updater/SBManagementClientImpl.java @@ -227,4 +227,21 @@ protected byte[] sendWait(final Destination d, final Priority p, final byte[] ap } return received; } + + @Override + public String toString() { + String status; + if (isOpen()) { + status = "is open"; + } + else { + status = "closed"; + } + + if (link == null) { + return String.format("%s, link==null, %s", status, super.toString()); + } + + return String.format("%s, link %s, %s", status, link, super.toString()); + } } From 83e388e17981906a3cce6856b64be5844ff770df Mon Sep 17 00:00:00 2001 From: Darthyson Date: Sun, 30 Nov 2025 15:45:03 +0100 Subject: [PATCH 359/359] [Updater] Fix more missing override annotation --- firmware_updater/updater/src/org/selfbus/updater/BinImage.java | 1 + .../src/org/selfbus/updater/bootloader/BootDescriptor.java | 1 + .../src/org/selfbus/updater/bootloader/BootloaderStatistic.java | 1 + 3 files changed, 3 insertions(+) diff --git a/firmware_updater/updater/src/org/selfbus/updater/BinImage.java b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java index 79703a3f..38bbc898 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/BinImage.java +++ b/firmware_updater/updater/src/org/selfbus/updater/BinImage.java @@ -142,6 +142,7 @@ public final void writeToBinFile(String fileName) { } } + @Override public final String toString() { return String.format("0x%04X-0x%04X, %05d byte(s), crc32 0x%08X, APP_VERSION pointer: 0x%04X", startAddress(), endAddress(), binData.length, crc32(), getAppVersionAddress()); diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java index 15f09b67..0448fd1d 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootDescriptor.java @@ -83,6 +83,7 @@ private short long2short(long a) { return (short) (a & 0x0000FFFF); } + @Override public String toString() { String res; if (valid()) { diff --git a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java index 33e2f007..6eae2064 100644 --- a/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java +++ b/firmware_updater/updater/src/org/selfbus/updater/bootloader/BootloaderStatistic.java @@ -33,6 +33,7 @@ private static String toColoredThreshold(int value, int threshold) { return String.format("%s%2d%s", ansiColor, value, ansi().reset()); } + @Override public String toString() { String result = getDisconnectCountColored(); result += " " + getRepeatedT_ACKcountColored();