From 732cfaa450a4c11efe3bee1a707f87e7402dfa0c Mon Sep 17 00:00:00 2001 From: Roman Makeev <57789105+makeevrserg@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:21:10 +0000 Subject: [PATCH] Update Infrared signal selection (#1) ## Background: This PR introduces new version of infrared signal selection with fixed several issues of previous version ## Changes: - Database normalization fix - Configuration generator for categories - Configuration generator for devices - Fix brands sorting - Fix exception on non-existing brands - Fix duplicate signals - Improve parser speed - Fix device configuration sha hash generation --- .gitignore | 3 +- Dockerfile | 2 +- IRDB | 2 +- README.md | 15 +- gradle.properties | 2 +- local.properties | 8 + .../backend/db/signal/table/BrandTable.kt | 8 - .../db/signal/table/CategoryMetaTable.kt | 11 - .../backend/db/signal/table/IfrFileTable.kt | 9 - .../db/signal/table/SignalOrderTable.kt | 18 -- .../backend/db/signal/table/UiPresetTable.kt | 10 - .../ifrmvp/backend/envkonfig/EnvKonfig.kt | 47 ++- .../ifrmvp/backend/envkonfig/KSystem.kt | 4 + .../backend/envkonfig/model/DBConnection.kt | 7 + modules/core/build.gradle.kts | 2 - .../core/di/factory/DatabaseFactory.kt | 11 +- .../{api-status => database}/build.gradle.kts | 4 +- .../ifrmvp/backend/db/signal/dao/TableDao.kt | 18 ++ .../backend/db/signal/dao/TableDaoImpl.kt | 78 +++++ .../backend/db/signal/di/SignalApiModule.kt | 6 + .../di/factory/SignalDatabaseFactory.kt | 10 +- .../db/signal/exception/TableDaoException.kt | 8 + .../backend/db/signal/table/BrandTable.kt | 24 ++ .../db/signal/table/CategoryMetaTable.kt | 36 +++ .../backend/db/signal/table/CategoryTable.kt | 8 +- .../db/signal/table/InfraredFileTable.kt | 23 ++ .../signal/table/InfraredFileToSignalTable.kt | 8 + .../backend/db/signal/table/SignalKeyTable.kt | 15 + .../backend/db/signal/table/SignalTable.kt | 27 +- .../backend/db/signal/table/UiPresetTable.kt | 19 ++ modules/infrared/build.gradle.kts | 13 + .../bridge/dao/api/model/FlipperKeyContent.kt | 0 .../editor/encoding/ByteArrayEncoder.kt | 9 + .../infrared/editor/encoding/JvmEncoder.kt | 20 ++ .../infrared/editor/model/InfraredRemote.kt | 5 + .../editor/util/InfraredRemoteEncoder.kt | 60 ++++ .../editor/viewmodel/InfraredKeyParser.kt | 0 .../kenerator/configuration/build.gradle.kts | 43 +++ .../ifrmvp/generator/config/Main.kt | 54 ++++ .../api/AllCategoryConfigGenerator.kt | 104 +++++++ .../config/category/api/DeviceKeyExt.kt | 200 ++++++++++++ .../api/DefaultDeviceConfigGenerator.kt | 28 ++ .../device/api/DeviceConfigGenerator.kt | 20 ++ .../device/api/DeviceKeyNamesProvider.kt | 14 + .../api/any/AnyDeviceKeyNamesProvider.kt | 191 ++++++++++++ modules/kenerator/paths/build.gradle.kts | 14 + .../ifrmvp/parser/util/ParserPathResolver.kt | 50 +-- .../ifrmvp/parser/ParserPathResolverTest.kt | 0 .../sql}/build.gradle.kts | 9 +- .../com/flipperdevices/ifrmvp/parser/Main.kt | 1 + .../ifrmvp/parser/di/ParserModule.kt | 8 +- .../ifrmvp/parser/model/RawIfrRemote.kt | 0 .../parser/presentation/FillerController.kt | 207 +++++++++++++ .../ui}/build.gradle.kts | 10 +- .../ifrmvp/parser/UiGenerator.kt | 0 .../ifrmvp/parser/UiGeneratorImpl.kt | 6 +- .../build.gradle.kts | 3 + .../ifrmvp/backend/model/BrandModel.kt | 2 +- .../ifrmvp/backend/model/BrandsResponse.kt | 0 .../backend/model/CategoriesRequestBody.kt | 0 .../backend/model/CategoriesResponse.kt | 0 .../backend/model/CategoryConfiguration.kt | 25 ++ .../ifrmvp/backend/model/CategoryManifest.kt | 0 .../ifrmvp/backend/model/CategoryMeta.kt | 0 .../ifrmvp/backend/model/CategoryType.kt | 12 + .../ifrmvp/backend/model/DeviceCategory.kt | 4 +- .../backend/model/DeviceConfiguration.kt | 11 + .../ifrmvp/backend/model/DeviceKey.kt | 116 +++++++ .../backend/model/ErrorResponseModel.kt | 0 .../ifrmvp/backend/model/ErrorType.kt | 10 + .../backend/model/IfrFileContentResponse.kt | 0 .../ifrmvp/backend/model/IfrFileModel.kt | 6 +- .../ifrmvp/backend/model/SignalModel.kt | 32 ++ .../backend/model/SignalRequestModel.kt | 4 - .../ifrmvp/backend/model/SignalResponse.kt | 17 +- .../backend/model/SignalResponseModel.kt | 0 .../ifrmvp/backend/model/UiPresetModel.kt | 14 + .../flipperdevices/ifrmvp/model/IfrButton.kt | 0 .../ifrmvp/model/IfrKeyIdentifier.kt | 30 +- .../flipperdevices/ifrmvp/model/PageLayout.kt | 0 .../ifrmvp/model/PagesLayout.kt | 0 .../ifrmvp/model/RawIfrButton.kt | 0 .../model/buttondata/Base64ImageButtonData.kt | 0 .../ifrmvp/model/buttondata/ButtonData.kt | 0 .../model/buttondata/ChannelButtonData.kt | 0 .../ifrmvp/model/buttondata/IconButtonData.kt | 2 +- .../model/buttondata/NavigationButtonData.kt | 0 .../model/buttondata/SingleKeyButtonData.kt | 0 .../ifrmvp/model/buttondata/TextButtonData.kt | 2 +- .../model/buttondata/UnknownButtonData.kt | 0 .../model/buttondata/VolumeButtonData.kt | 0 .../model/serialization/ButtonDataDecoder.kt | 0 .../model/serialization/ButtonDataEncoder.kt | 0 .../serialization/ButtonDataSerializer.kt | 0 .../serialization/ButtonDataSerializerTest.kt | 33 ++ .../ifrmvp/parser/api/SignalTableApi.kt | 50 --- .../ifrmvp/parser/api/SignalTableApiImpl.kt | 221 ------------- .../ifrmvp/parser/model/OrderModel.kt | 15 - .../parser/presentation/FillerController.kt | 27 -- .../presentation/filler/BrandsFiller.kt | 45 --- .../presentation/filler/CategoriesFiller.kt | 33 -- .../presentation/filler/IfrSignalFiller.kt | 67 ---- .../presentation/filler/IrFilesFiller.kt | 65 ---- .../parser/presentation/filler/MetaFiller.kt | 20 -- .../parser/presentation/filler/OrderFiller.kt | 48 --- .../presentation/filler/UiPresetFiller.kt | 26 -- .../ifrmvp/parser/util/IfrRemoteExt.kt | 24 -- .../ifrmvp/backend/model/ErrorType.kt | 5 - .../ifrmvp/backend/model/SignalModel.kt | 34 -- modules/shared-ui-model/build.gradle.kts | 24 -- settings.gradle.kts | 20 +- web-api/build.gradle.kts | 12 +- .../ifrmvp/backend/Application.kt | 3 +- .../ifrmvp/backend/di/RootModule.kt | 8 + .../ifrmvp/backend/plugins/StatusPages.kt | 13 + .../ifrmvp/backend/plugins/Swagger.kt | 3 +- .../route/brands/data/BrandsRepositoryImpl.kt | 13 +- .../backend/route/brands/di/BrandsModule.kt | 3 +- .../presentation/BrandsRouteRegistry.kt | 5 +- .../data/CategoriesRepositoryImpl.kt | 1 + .../route/configgen/di/ConfigGenModule.kt | 22 ++ .../presentation/ConfigGenRouteRegistry.kt | 53 ++++ .../route/key/data/KeyRouteRepository.kt | 48 --- .../route/key/data/KeyRouteRepositoryImpl.kt | 24 ++ .../ifrmvp/backend/route/key/di/KeyModule.kt | 4 +- .../route/key/presentation/KeySwagger.kt | 6 +- .../route/signal/data/CounterRepository.kt | 12 - .../signal/data/DefaultSignalRepository.kt | 82 ----- .../data/InstantCategoryConfigRepository.kt | 18 ++ .../route/signal/data/IrFileRepository.kt | 45 --- .../signal/data/SignalByOrderRepository.kt | 63 ---- .../backend/route/signal/di/SignalModule.kt | 11 +- .../route/signal/mapping/SignalModelMapper.kt | 26 -- .../presentation/SignalRouteRegistry.kt | 290 ++++++++++++++---- .../backend/route/ui/data/UiFileRepository.kt | 8 + .../route/ui/data/UiFileRepositoryImpl.kt | 46 +++ .../ifrmvp/backend/route/ui/di/UiModule.kt | 8 +- .../route/ui/presentation/UiRouteRegistry.kt | 68 +--- 138 files changed, 2163 insertions(+), 1288 deletions(-) create mode 100644 local.properties delete mode 100644 modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/BrandTable.kt delete mode 100644 modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryMetaTable.kt delete mode 100644 modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/IfrFileTable.kt delete mode 100644 modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalOrderTable.kt delete mode 100644 modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/UiPresetTable.kt rename modules/{api-status => database}/build.gradle.kts (74%) create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/dao/TableDao.kt create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/dao/TableDaoImpl.kt rename modules/{api-status => database}/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/SignalApiModule.kt (67%) rename modules/{api-status => database}/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/factory/SignalDatabaseFactory.kt (81%) create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/exception/TableDaoException.kt create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/BrandTable.kt create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryMetaTable.kt rename modules/{api-status => database}/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryTable.kt (51%) create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/InfraredFileTable.kt create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/InfraredFileToSignalTable.kt create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalKeyTable.kt rename modules/{api-status => database}/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalTable.kt (55%) create mode 100644 modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/UiPresetTable.kt create mode 100644 modules/infrared/build.gradle.kts rename modules/{parser => infrared}/src/main/kotlin/com/flipperdevices/bridge/dao/api/model/FlipperKeyContent.kt (100%) create mode 100644 modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/encoding/ByteArrayEncoder.kt create mode 100644 modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/encoding/JvmEncoder.kt rename modules/{parser => infrared}/src/main/kotlin/com/flipperdevices/infrared/editor/model/InfraredRemote.kt (89%) create mode 100644 modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/util/InfraredRemoteEncoder.kt rename modules/{parser => infrared}/src/main/kotlin/com/flipperdevices/infrared/editor/viewmodel/InfraredKeyParser.kt (100%) create mode 100644 modules/kenerator/configuration/build.gradle.kts create mode 100644 modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/Main.kt create mode 100644 modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/category/api/AllCategoryConfigGenerator.kt create mode 100644 modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/category/api/DeviceKeyExt.kt create mode 100644 modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DefaultDeviceConfigGenerator.kt create mode 100644 modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DeviceConfigGenerator.kt create mode 100644 modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DeviceKeyNamesProvider.kt create mode 100644 modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/any/AnyDeviceKeyNamesProvider.kt create mode 100644 modules/kenerator/paths/build.gradle.kts rename modules/{parser => kenerator/paths}/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/ParserPathResolver.kt (79%) rename modules/{parser => kenerator/paths}/src/test/kotlin/com/flipperdevices/ifrmvp/parser/ParserPathResolverTest.kt (100%) rename modules/{parser => kenerator/sql}/build.gradle.kts (82%) rename modules/{parser => kenerator/sql}/src/main/kotlin/com/flipperdevices/ifrmvp/parser/Main.kt (99%) rename modules/{parser => kenerator/sql}/src/main/kotlin/com/flipperdevices/ifrmvp/parser/di/ParserModule.kt (55%) rename modules/{parser => kenerator/sql}/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/RawIfrRemote.kt (100%) create mode 100644 modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/FillerController.kt rename modules/{ui-generator => kenerator/ui}/build.gradle.kts (63%) rename modules/{ui-generator => kenerator/ui}/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt (100%) rename modules/{ui-generator => kenerator/ui}/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt (92%) rename modules/{shared-backend-model => model}/build.gradle.kts (86%) rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandModel.kt (91%) rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandsResponse.kt (100%) rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesRequestBody.kt (100%) rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesResponse.kt (100%) create mode 100644 modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryConfiguration.kt rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryManifest.kt (100%) rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryMeta.kt (100%) create mode 100644 modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryType.kt rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceCategory.kt (75%) create mode 100644 modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceConfiguration.kt create mode 100644 modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceKey.kt rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorResponseModel.kt (100%) create mode 100644 modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorType.kt rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileContentResponse.kt (100%) rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileModel.kt (77%) create mode 100644 modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalModel.kt rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalRequestModel.kt (81%) rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponse.kt (59%) rename modules/{shared-backend-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponseModel.kt (100%) create mode 100644 modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/UiPresetModel.kt rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrButton.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrKeyIdentifier.kt (63%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PageLayout.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PagesLayout.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/RawIfrButton.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/Base64ImageButtonData.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ButtonData.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ChannelButtonData.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/IconButtonData.kt (87%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/NavigationButtonData.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/SingleKeyButtonData.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/TextButtonData.kt (84%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/UnknownButtonData.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/VolumeButtonData.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataDecoder.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataEncoder.kt (100%) rename modules/{shared-ui-model => model}/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializer.kt (100%) create mode 100644 modules/model/src/commonTest/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializerTest.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/api/SignalTableApi.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/api/SignalTableApiImpl.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/OrderModel.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/FillerController.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/BrandsFiller.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/CategoriesFiller.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/IfrSignalFiller.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/IrFilesFiller.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/MetaFiller.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/OrderFiller.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/UiPresetFiller.kt delete mode 100644 modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/IfrRemoteExt.kt delete mode 100644 modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorType.kt delete mode 100644 modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalModel.kt delete mode 100644 modules/shared-ui-model/build.gradle.kts create mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/configgen/di/ConfigGenModule.kt create mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/configgen/presentation/ConfigGenRouteRegistry.kt create mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepositoryImpl.kt delete mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/CounterRepository.kt delete mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/DefaultSignalRepository.kt create mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/InstantCategoryConfigRepository.kt delete mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/IrFileRepository.kt delete mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/SignalByOrderRepository.kt delete mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/mapping/SignalModelMapper.kt create mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/data/UiFileRepository.kt create mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/data/UiFileRepositoryImpl.kt diff --git a/.gitignore b/.gitignore index f789144..90f0781 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ build jars output -.env \ No newline at end of file +.env +DB_FILE.* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 697b393..282b05e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ COPY . . RUN ./gradlew :web-api:shadowJar -RUN ./gradlew :modules:parser:shadowJar +RUN ./gradlew :modules:kenerator:sql:shadowJar FROM openjdk:24-slim as parser diff --git a/IRDB b/IRDB index dd48b36..69abbb9 160000 --- a/IRDB +++ b/IRDB @@ -1 +1 @@ -Subproject commit dd48b36b24265f2c2b8719ff05697bd1a2e4271e +Subproject commit 69abbb95ca293cf1aeaee9196d2035fad34cc13e diff --git a/README.md b/README.md index 9389892..c928823 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ```bash # Run parser and fill DB -./gradlew :modules:parser:run +./gradlew :modules:kenerator:sql:run # Run server ./gradlew :web-api:run ``` @@ -15,7 +15,7 @@ Output jars will be located in [generated ./jars folder](./jars) ```bash # Shadow parser -./gradlew :modules:parser:shadowJar +./gradlew :modules:kenerator:sql:shadowJar # Shadow server ./gradlew :web-api:shadowJar ``` @@ -26,7 +26,16 @@ Output jars will be located in [generated ./jars folder](./jars) # Path to https://github.com/flipperdevices/IRDB/tree/dev/database IR_FOLDER_PATH="./IRDB/database" FBACKEND_PORT=8080 -# H2 Section +# SQLite Section +# [H2, POSTGRES] FBACKEND_DB_TYPE="H2" +# Only for H2 DB_FULL_PATH="./folder/DB_FILE" +# SQL Remote section +# Only for POSTGRES +DB_NAME=SOME_NAME +DB_HOST=192.168.0.1 +DB_PORT=1234 +DB_USER=ROOT +DB_PASSWORD=PASSWORD ``` \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d1e1967..68d5166 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ org.gradle.parallel=true makeevrserg.project.name=IfrBackend makeevrserg.project.url=https://github.com/makeevrserg/IfrSample makeevrserg.project.group=com.flipperdevices.ifrmvp.backend -makeevrserg.project.version.string=0.1.0 +makeevrserg.project.version.string=0.2.0 makeevrserg.project.description=Api for IfrSample makeevrserg.project.developers=makeevrserg|Makeev Roman|makeevrserg@gmail.com # Java diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..ceac417 --- /dev/null +++ b/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Thu May 23 11:47:58 MSK 2024 +sdk.dir=/Users/romanmakeev/Library/Android/sdk diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/BrandTable.kt b/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/BrandTable.kt deleted file mode 100644 index f6885b3..0000000 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/BrandTable.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.db.signal.table - -import org.jetbrains.exposed.dao.id.LongIdTable - -object BrandTable : LongIdTable("BRAND") { - val category = reference("category_id", CategoryTable) - val displayName = text("display_name") -} diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryMetaTable.kt b/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryMetaTable.kt deleted file mode 100644 index dd4d8ae..0000000 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryMetaTable.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.db.signal.table - -import org.jetbrains.exposed.dao.id.LongIdTable - -object CategoryMetaTable : LongIdTable("CATEGORY_META") { - val categoryId = reference("category_id", CategoryTable) - val displayName = text("display_name") - val singularDisplayName = text("singular_display_name") - val iconPngBase64 = text("icn_png_base64") - val iconSvgBase64 = text("icn_svg_base64") -} diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/IfrFileTable.kt b/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/IfrFileTable.kt deleted file mode 100644 index 94f277d..0000000 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/IfrFileTable.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.db.signal.table - -import org.jetbrains.exposed.dao.id.LongIdTable - -object IfrFileTable : LongIdTable("IFR_FILE") { - val categoryId = reference("category_id", CategoryTable) - val brandId = reference("brand_id", BrandTable) - val fileName = text("file_name") -} diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalOrderTable.kt b/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalOrderTable.kt deleted file mode 100644 index fe333f8..0000000 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalOrderTable.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.db.signal.table - -import org.jetbrains.exposed.dao.id.LongIdTable - -object SignalOrderTable : LongIdTable("SIGNAL_ORDER_TABLE") { - val categoryId = reference("category_id", CategoryTable) - val brandId = reference("brand_id", BrandTable) - val ifrFileId = reference("ifr_file_id", IfrFileTable) - val ifrSignalId = reference("ifr_signal_id", SignalTable.id) - val order = integer("order") - - val message = text("message") - - // Data - val dataType = text("data_type") - val dataIconId = text("data_icon_id").nullable() - val dataText = text("data_text").nullable() -} diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/UiPresetTable.kt b/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/UiPresetTable.kt deleted file mode 100644 index 1ae3a34..0000000 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/UiPresetTable.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.db.signal.table - -import org.jetbrains.exposed.dao.id.LongIdTable - -object UiPresetTable : LongIdTable("UI_PRESET") { - val categoryId = reference("category_id", CategoryTable) - val brandId = reference("brand_id", BrandTable) - val ifrFileId = reference("ifr_file_id", IfrFileTable) - val fileName = text("file_name") -} diff --git a/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/EnvKonfig.kt b/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/EnvKonfig.kt index 9f471a9..eade9ce 100644 --- a/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/EnvKonfig.kt +++ b/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/EnvKonfig.kt @@ -6,7 +6,7 @@ import java.net.InetAddress object EnvKonfig { internal enum class DBType { - H2 + H2, POSTGRES } val IR_DATABASE_PATH: String @@ -22,17 +22,44 @@ object EnvKonfig { ?.toIntOrNull() ?: 8080 - internal val FBACKEND_DB_TYPE: DBType - get() = KSystem.getenvOrNull("FBACKEND_DB_TYPE") - ?.let { type -> DBType.entries.firstOrNull { entry -> entry.name == type } } - ?: DBType.H2 + private object DbKonfig { + val FBACKEND_DB_TYPE: DBType + get() = KSystem.getenvOrNull("FBACKEND_DB_TYPE") + ?.let { type -> DBType.entries.firstOrNull { entry -> entry.name == type } } + ?: DBType.H2 - internal val FBACKEND_DB_NAME: String - get() = KSystem.getenvOrNull("DB_FULL_PATH") - ?: BuildKonfig.FALLBACK_DB_FULL_PATH + val FBACKEND_DB_FULL_PATH: String + get() = KSystem.getenvOrNull("DB_FULL_PATH") + ?: BuildKonfig.FALLBACK_DB_FULL_PATH + + val DB_NAME: String + get() = KSystem.requireEnv("DB_NAME") + + val DB_HOST: String + get() = KSystem.requireEnv("DB_HOST") + + val DB_PORT: String + get() = KSystem.requireEnv("DB_PORT") + + val DB_USER: String + get() = KSystem.requireEnv("DB_USER") + + val DB_PASSWORD: String + get() = KSystem.requireEnv("DB_PASSWORD") + } val signalDatabaseConnection: DBConnection - get() = when (EnvKonfig.FBACKEND_DB_TYPE) { - EnvKonfig.DBType.H2 -> DBConnection.H2(EnvKonfig.FBACKEND_DB_NAME) + get() = when (DbKonfig.FBACKEND_DB_TYPE) { + DBType.H2 -> DBConnection.H2( + path = DbKonfig.FBACKEND_DB_FULL_PATH + ) + + DBType.POSTGRES -> DBConnection.Postgres( + host = DbKonfig.DB_HOST, + port = DbKonfig.DB_PORT, + user = DbKonfig.DB_USER, + password = DbKonfig.DB_PASSWORD, + name = DbKonfig.DB_NAME + ) } } diff --git a/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/KSystem.kt b/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/KSystem.kt index 1754f23..3c97504 100644 --- a/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/KSystem.kt +++ b/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/KSystem.kt @@ -10,4 +10,8 @@ internal object KSystem { } return value } + + fun requireEnv(key: String): String { + return getenvOrNull(key) ?: error { "Environment not found: $key" } + } } diff --git a/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/model/DBConnection.kt b/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/model/DBConnection.kt index cb21335..6ebf3c8 100644 --- a/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/model/DBConnection.kt +++ b/modules/build-konfig/src/main/kotlin/com/flipperdevices/ifrmvp/backend/envkonfig/model/DBConnection.kt @@ -2,4 +2,11 @@ package com.flipperdevices.ifrmvp.backend.envkonfig.model sealed class DBConnection(val driver: String) { class H2(val path: String) : DBConnection("org.h2.Driver") + class Postgres( + val host: String, + val port: String, + val user: String, + val password: String, + val name: String + ) : DBConnection("org.postgresql.Driver") } diff --git a/modules/core/build.gradle.kts b/modules/core/build.gradle.kts index 949c2d0..03c285d 100644 --- a/modules/core/build.gradle.kts +++ b/modules/core/build.gradle.kts @@ -18,7 +18,5 @@ dependencies { implementation(libs.exposed.jdbc) implementation(libs.sql.driver.mysql) // Services - implementation("com.flipperdevices.ifrmvp.backend:shared-ui-model") - implementation(projects.modules.sharedUiModel) implementation(projects.modules.buildKonfig) } diff --git a/modules/core/src/main/kotlin/com/flipperdevices/ifrmvp/backend/core/di/factory/DatabaseFactory.kt b/modules/core/src/main/kotlin/com/flipperdevices/ifrmvp/backend/core/di/factory/DatabaseFactory.kt index 16305cb..ebbd314 100644 --- a/modules/core/src/main/kotlin/com/flipperdevices/ifrmvp/backend/core/di/factory/DatabaseFactory.kt +++ b/modules/core/src/main/kotlin/com/flipperdevices/ifrmvp/backend/core/di/factory/DatabaseFactory.kt @@ -14,10 +14,19 @@ interface DatabaseFactory { return when (dbConnection) { is DBConnection.H2 -> { Database.connect( - url = "jdbc:h2:${dbConnection.path};DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false", + url = "jdbc:h2:${dbConnection.path};DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MYSQL", driver = dbConnection.driver ) } + + is DBConnection.Postgres -> { + Database.connect( + url = "jdbc:postgresql://${dbConnection.host}:${dbConnection.port}/${dbConnection.name}", + driver = dbConnection.driver, + user = dbConnection.user, + password = dbConnection.password + ) + } }.also(configure) } } diff --git a/modules/api-status/build.gradle.kts b/modules/database/build.gradle.kts similarity index 74% rename from modules/api-status/build.gradle.kts rename to modules/database/build.gradle.kts index 545b99c..aaac012 100644 --- a/modules/api-status/build.gradle.kts +++ b/modules/database/build.gradle.kts @@ -7,9 +7,9 @@ dependencies { implementation(libs.exposed.core) implementation(libs.exposed.dao) implementation("com.h2database:h2:2.2.224") + implementation("org.postgresql:postgresql:42.7.1") // Local implementation(projects.modules.buildKonfig) - implementation(projects.modules.sharedBackendModel) - implementation(projects.modules.sharedUiModel) + implementation(projects.modules.model) implementation(projects.modules.core) } diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/dao/TableDao.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/dao/TableDao.kt new file mode 100644 index 0000000..13f8b4b --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/dao/TableDao.kt @@ -0,0 +1,18 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.dao + +import com.flipperdevices.ifrmvp.backend.model.BrandModel +import com.flipperdevices.ifrmvp.backend.model.DeviceCategory +import com.flipperdevices.ifrmvp.backend.model.IfrFileModel + +interface TableDao { + suspend fun getCategoryById(categoryId: Long): DeviceCategory + suspend fun getBrandById(brandId: Long): BrandModel + suspend fun ifrFileById(irFileId: Long): IfrFileModel + + companion object { + suspend fun TableDao.getCategoryByBrandId(brandId: Long): DeviceCategory { + val brandModel = getBrandById(brandId) + return getCategoryById(brandModel.categoryId) + } + } +} diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/dao/TableDaoImpl.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/dao/TableDaoImpl.kt new file mode 100644 index 0000000..7dc3663 --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/dao/TableDaoImpl.kt @@ -0,0 +1,78 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.dao + +import com.flipperdevices.ifrmvp.backend.db.signal.exception.TableDaoException +import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryMetaTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.InfraredFileTable +import com.flipperdevices.ifrmvp.backend.model.BrandModel +import com.flipperdevices.ifrmvp.backend.model.CategoryManifest +import com.flipperdevices.ifrmvp.backend.model.CategoryMeta +import com.flipperdevices.ifrmvp.backend.model.DeviceCategory +import com.flipperdevices.ifrmvp.backend.model.IfrFileModel +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction + +internal class TableDaoImpl(private val database: Database) : TableDao { + override suspend fun getCategoryById( + categoryId: Long + ): DeviceCategory = newSuspendedTransaction(db = database) { + CategoryTable + .selectAll() + .where { CategoryTable.id eq categoryId } + .limit(1) + .map { r -> + DeviceCategory( + id = r[CategoryTable.id].value, + folderName = r[CategoryTable.folderName], + meta = CategoryMetaTable.selectAll() + .where { CategoryMetaTable.categoryId eq categoryId } + .limit(1) + .map { + CategoryMeta( + iconPngBase64 = it[CategoryMetaTable.iconPngBase64], + iconSvgBase64 = it[CategoryMetaTable.iconSvgBase64], + manifest = CategoryManifest( + displayName = it[CategoryMetaTable.displayName], + singularDisplayName = it[CategoryMetaTable.singularDisplayName], + ) + ) + }.firstOrNull() ?: throw TableDaoException.CategoryMeta(categoryId) + ) + }.firstOrNull() ?: throw TableDaoException.CategoryNotFound(categoryId) + } + + override suspend fun getBrandById( + brandId: Long + ): BrandModel = newSuspendedTransaction(db = database) { + BrandTable + .selectAll() + .where { BrandTable.id eq brandId } + .limit(1) + .map { + BrandModel( + id = it[BrandTable.id].value, + folderName = it[BrandTable.folderName], + categoryId = it[BrandTable.categoryId].value + ) + }.firstOrNull() ?: throw TableDaoException.BrandNotFound(brandId) + } + + override suspend fun ifrFileById( + irFileId: Long + ): IfrFileModel = newSuspendedTransaction(db = database) { + InfraredFileTable + .selectAll() + .where { InfraredFileTable.id eq irFileId } + .limit(1) + .map { + IfrFileModel( + id = it[InfraredFileTable.id].value, + brandId = it[InfraredFileTable.brandId].value, + fileName = it[InfraredFileTable.fileName], + folderName = it[InfraredFileTable.folderName] + ) + }.firstOrNull() ?: throw TableDaoException.IrFileNotFound(irFileId) + } +} diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/SignalApiModule.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/SignalApiModule.kt similarity index 67% rename from modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/SignalApiModule.kt rename to modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/SignalApiModule.kt index 9ea08a0..4696598 100644 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/SignalApiModule.kt +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/SignalApiModule.kt @@ -1,11 +1,14 @@ package com.flipperdevices.ifrmvp.backend.db.signal.di +import com.flipperdevices.ifrmvp.backend.db.signal.dao.TableDao +import com.flipperdevices.ifrmvp.backend.db.signal.dao.TableDaoImpl import com.flipperdevices.ifrmvp.backend.db.signal.di.factory.SignalDatabaseFactory import com.flipperdevices.ifrmvp.backend.envkonfig.model.DBConnection import org.jetbrains.exposed.sql.Database interface SignalApiModule { val database: Database + val tableDao: TableDao class Default( signalDbConnection: DBConnection @@ -13,5 +16,8 @@ interface SignalApiModule { override val database: Database by lazy { SignalDatabaseFactory(signalDbConnection).create() } + override val tableDao: TableDao by lazy { + TableDaoImpl(database) + } } } diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/factory/SignalDatabaseFactory.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/factory/SignalDatabaseFactory.kt similarity index 81% rename from modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/factory/SignalDatabaseFactory.kt rename to modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/factory/SignalDatabaseFactory.kt index 9589fec..102a381 100644 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/factory/SignalDatabaseFactory.kt +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/di/factory/SignalDatabaseFactory.kt @@ -4,8 +4,9 @@ import com.flipperdevices.ifrmvp.backend.core.di.factory.DatabaseFactory import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryMetaTable import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.IfrFileTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalOrderTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.InfraredFileTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.InfraredFileToSignalTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalKeyTable import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalTable import com.flipperdevices.ifrmvp.backend.db.signal.table.UiPresetTable import com.flipperdevices.ifrmvp.backend.envkonfig.model.DBConnection @@ -29,9 +30,10 @@ internal class SignalDatabaseFactory( BrandTable, CategoryMetaTable, CategoryTable, - IfrFileTable, - SignalOrderTable, + InfraredFileTable, + InfraredFileToSignalTable, SignalTable, + SignalKeyTable, UiPresetTable, ) } diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/exception/TableDaoException.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/exception/TableDaoException.kt new file mode 100644 index 0000000..1bb47c1 --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/exception/TableDaoException.kt @@ -0,0 +1,8 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.exception + +sealed class TableDaoException(message: String) : Throwable(message) { + class CategoryNotFound(id: Long) : TableDaoException("Category with id $id not found!") + class CategoryMeta(id: Long) : TableDaoException("Category meta for category with id $id not found!") + class BrandNotFound(id: Long) : TableDaoException("Brand with id $id not found!") + class IrFileNotFound(id: Long) : TableDaoException("IR file with id $id not found!") +} diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/BrandTable.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/BrandTable.kt new file mode 100644 index 0000000..bc9b05f --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/BrandTable.kt @@ -0,0 +1,24 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.table + +import org.jetbrains.exposed.dao.id.LongIdTable + +/** + * Brands folder converted to tables + * + * Every brand of every category with parent category [BrandTable.categoryId] + */ +object BrandTable : LongIdTable("BRAND") { + /** + * id of category brand located in + */ + val categoryId = reference("category_id", CategoryTable) + + /** + * The name of brand folder + */ + val folderName = text("folder_name") + + init { + uniqueIndex("category_folder_index", categoryId, folderName) + } +} diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryMetaTable.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryMetaTable.kt new file mode 100644 index 0000000..569ab4a --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryMetaTable.kt @@ -0,0 +1,36 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.table + +import org.jetbrains.exposed.dao.id.LongIdTable + +/** + * Category meta is a table model for .meta folder + * + * Every category meta of every category with parent [CategoryMetaTable.categoryId] + */ +object CategoryMetaTable : LongIdTable("CATEGORY_META") { + /** + * id of parent category + */ + val categoryId = reference("category_id", CategoryTable).uniqueIndex() + + /** + * Default display name for category aka TVs, Fans + */ + val displayName = text("display_name").uniqueIndex() + + /** + * Singular display name aka TV, Fan which allows to translate it into other languages + * + */ + val singularDisplayName = text("singular_display_name").uniqueIndex() + + /** + * .meta/icon.png converted to Base64 String + */ + val iconPngBase64 = text("icn_png_base64") + + /** + * .meta/icon.svg converted to Base64 String + */ + val iconSvgBase64 = text("icn_svg_base64") +} diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryTable.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryTable.kt similarity index 51% rename from modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryTable.kt rename to modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryTable.kt index b9b1f4b..228e634 100644 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryTable.kt +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/CategoryTable.kt @@ -2,6 +2,12 @@ package com.flipperdevices.ifrmvp.backend.db.signal.table import org.jetbrains.exposed.dao.id.LongIdTable +/** + * Every category of IRDB + */ object CategoryTable : LongIdTable("CATEGORY") { - val folderName = text("folder_name") + /** + * The name of category folder inside IRDB + */ + val folderName = text("folder_name").uniqueIndex() } diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/InfraredFileTable.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/InfraredFileTable.kt new file mode 100644 index 0000000..0c5c8ed --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/InfraredFileTable.kt @@ -0,0 +1,23 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.table + +import org.jetbrains.exposed.dao.id.LongIdTable + +/** + * Every .ir file from IRDB + */ +object InfraredFileTable : LongIdTable("INFRARED_FILE") { + /** + * Reference to current brand identifier + */ + val brandId = reference("brand_id", BrandTable) + + /** + * The name of .ir file inside /CATEGORY/BRAND/MODEL/XXX.ir + */ + val fileName = text("file_name") + + /** + * The name of .IR file folder + */ + val folderName = text("folder_name") +} diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/InfraredFileToSignalTable.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/InfraredFileToSignalTable.kt new file mode 100644 index 0000000..d5a8165 --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/InfraredFileToSignalTable.kt @@ -0,0 +1,8 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.table + +import org.jetbrains.exposed.dao.id.LongIdTable + +object InfraredFileToSignalTable : LongIdTable("INFRARED_FILE_TO_SIGNAL") { + val infraredFileId = reference("infrared_file_id", InfraredFileTable) + val signalId = reference("signal_id", SignalTable) +} diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalKeyTable.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalKeyTable.kt new file mode 100644 index 0000000..67b0a8c --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalKeyTable.kt @@ -0,0 +1,15 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.table + +import com.flipperdevices.ifrmvp.backend.model.DeviceKey +import org.jetbrains.exposed.dao.id.LongIdTable + +object SignalKeyTable : LongIdTable("SIGNAL_KEY") { + val infraredFileId = reference("ir_file_id", InfraredFileTable) + val signalId = reference("signal_id", SignalTable) + + val deviceKey = enumeration("device_key") + + val type = text("type") + val remoteKeyName = text("remote_key_name").nullable() + val hash = text("hash").nullable() +} diff --git a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalTable.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalTable.kt similarity index 55% rename from modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalTable.kt rename to modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalTable.kt index 23cce1c..f4f97df 100644 --- a/modules/api-status/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalTable.kt +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/SignalTable.kt @@ -2,10 +2,16 @@ package com.flipperdevices.ifrmvp.backend.db.signal.table import org.jetbrains.exposed.dao.id.LongIdTable +/** + * Every UNIQUE signal of every .ir file + * + * There's shouldn't be the same signal with same data + * + * Exception for raw signals which can be the same with parsed + */ object SignalTable : LongIdTable("SIGNAL_TABLE") { - val categoryId = reference("category_id", CategoryTable) val brandId = reference("brand_id", BrandTable) - val ifrFileId = reference("ifr_file_id", IfrFileTable) + val name = text("name") val type = text("type") val protocol = text("protocol").nullable() @@ -15,4 +21,21 @@ object SignalTable : LongIdTable("SIGNAL_TABLE") { val dutyCycle = text("duty_cycle").nullable() val data = text("data").nullable() val hash = text("hash") + + init { + uniqueIndex( + brandId, + type, + frequency, + dutyCycle, + data + ) + uniqueIndex( + brandId, + type, + protocol, + address, + command, + ) + } } diff --git a/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/UiPresetTable.kt b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/UiPresetTable.kt new file mode 100644 index 0000000..41c11be --- /dev/null +++ b/modules/database/src/main/kotlin/com/flipperdevices/ifrmvp/backend/db/signal/table/UiPresetTable.kt @@ -0,0 +1,19 @@ +package com.flipperdevices.ifrmvp.backend.db.signal.table + +import org.jetbrains.exposed.dao.id.LongIdTable + +/** + * Every UI preset file of every device + * /CATEGORY/BRAND/MODEL/XXXX.ui.json + */ +object UiPresetTable : LongIdTable("UI_PRESET") { + /** + * Reference to .ir file instance + */ + val infraredFileId = reference("infrared_file_id", InfraredFileTable) + + /** + * The name of XXX.ui.json file + */ + val fileName = text("file_name") +} diff --git a/modules/infrared/build.gradle.kts b/modules/infrared/build.gradle.kts new file mode 100644 index 0000000..2a7da0d --- /dev/null +++ b/modules/infrared/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("org.jetbrains.kotlin.jvm") + alias(libs.plugins.kotlin.serialization) +} + +dependencies { + // Kotlin + implementation(libs.kotlin.coroutines) + implementation(libs.kotlin.serialization.json) + // Local + implementation(projects.modules.buildKonfig) + implementation(projects.modules.model) +} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/bridge/dao/api/model/FlipperKeyContent.kt b/modules/infrared/src/main/kotlin/com/flipperdevices/bridge/dao/api/model/FlipperKeyContent.kt similarity index 100% rename from modules/parser/src/main/kotlin/com/flipperdevices/bridge/dao/api/model/FlipperKeyContent.kt rename to modules/infrared/src/main/kotlin/com/flipperdevices/bridge/dao/api/model/FlipperKeyContent.kt diff --git a/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/encoding/ByteArrayEncoder.kt b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/encoding/ByteArrayEncoder.kt new file mode 100644 index 0000000..5589f64 --- /dev/null +++ b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/encoding/ByteArrayEncoder.kt @@ -0,0 +1,9 @@ +package com.flipperdevices.infrared.editor.encoding + +interface ByteArrayEncoder { + val algorithm: Algorithm + + fun encode(byteArray: ByteArray): String + + enum class Algorithm { MD5, SHA_256 } +} diff --git a/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/encoding/JvmEncoder.kt b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/encoding/JvmEncoder.kt new file mode 100644 index 0000000..18f2788 --- /dev/null +++ b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/encoding/JvmEncoder.kt @@ -0,0 +1,20 @@ +package com.flipperdevices.infrared.editor.encoding + +import java.security.MessageDigest + +class JvmEncoder(override val algorithm: ByteArrayEncoder.Algorithm) : ByteArrayEncoder { + private val ByteArrayEncoder.Algorithm.algorithmName: String + get() = when (algorithm) { + ByteArrayEncoder.Algorithm.MD5 -> "MD5" + ByteArrayEncoder.Algorithm.SHA_256 -> "SHA-256" + } + + @OptIn(ExperimentalStdlibApi::class) + override fun encode(byteArray: ByteArray): String { + val algorithmName = algorithm.algorithmName + return MessageDigest.getInstance(algorithmName) + .digest(byteArray) + .toHexString() + } + +} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/infrared/editor/model/InfraredRemote.kt b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/model/InfraredRemote.kt similarity index 89% rename from modules/parser/src/main/kotlin/com/flipperdevices/infrared/editor/model/InfraredRemote.kt rename to modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/model/InfraredRemote.kt index 2615813..3bd3910 100644 --- a/modules/parser/src/main/kotlin/com/flipperdevices/infrared/editor/model/InfraredRemote.kt +++ b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/model/InfraredRemote.kt @@ -1,9 +1,13 @@ package com.flipperdevices.infrared.editor.model +import kotlinx.serialization.Serializable + +@Serializable sealed class InfraredRemote( val name: String, val type: String ) { + @Serializable data class Parsed( val nameInternal: String, val typeInternal: String, @@ -12,6 +16,7 @@ sealed class InfraredRemote( val command: String, ) : InfraredRemote(nameInternal, typeInternal) + @Serializable data class Raw( val nameInternal: String, val typeInternal: String, diff --git a/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/util/InfraredRemoteEncoder.kt b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/util/InfraredRemoteEncoder.kt new file mode 100644 index 0000000..c336d7d --- /dev/null +++ b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/util/InfraredRemoteEncoder.kt @@ -0,0 +1,60 @@ +package com.flipperdevices.infrared.editor.util + +import com.flipperdevices.ifrmvp.backend.model.SignalModel +import com.flipperdevices.infrared.editor.model.InfraredRemote +import kotlin.jvm.internal.Ref.FloatRef +import kotlinx.coroutines.processNextEventInCurrentThread +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + +object InfraredRemoteEncoder { + private val json = Json { + isLenient = true + ignoreUnknownKeys = true + prettyPrint = false + classDiscriminator = "_discriminator" + } + + private fun InfraredRemote.toFlipperRemote(): SignalModel.FlipperRemote { + val raw = this as? InfraredRemote.Raw + val parsed = this as? InfraredRemote.Parsed + return SignalModel.FlipperRemote( + name = this.name, + type = this.type, + protocol = parsed?.protocol, + address = parsed?.address, + command = parsed?.command, + frequency = raw?.frequency, + dutyCycle = raw?.dutyCycle, + data = raw?.data + ) + } + + private fun SignalModel.FlipperRemote.toInfraredRemote(): InfraredRemote { + return runCatching { + InfraredRemote.Parsed( + nameInternal = name, + typeInternal = type, + protocol = protocol ?: error("Not parsed remote"), + address = address ?: error("Not parsed remote"), + command = command ?: error("Not parsed remote") + ) + }.getOrNull() ?: InfraredRemote.Raw( + nameInternal = name, + typeInternal = type, + frequency = frequency ?: error("Not raw remote"), + dutyCycle = dutyCycle ?: error("Not raw remote"), + data = data ?: error("Not raw remote") + ) + } + + fun encode(remote: InfraredRemote): ByteArray { + val fRemote = remote.toFlipperRemote() + return json.encodeToString(fRemote).encodeToByteArray() + } + + fun decode(byteArray: ByteArray): InfraredRemote { + val fRemote: SignalModel.FlipperRemote = json.decodeFromString(byteArray.decodeToString()) + return fRemote.toInfraredRemote() + } +} \ No newline at end of file diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/infrared/editor/viewmodel/InfraredKeyParser.kt b/modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/viewmodel/InfraredKeyParser.kt similarity index 100% rename from modules/parser/src/main/kotlin/com/flipperdevices/infrared/editor/viewmodel/InfraredKeyParser.kt rename to modules/infrared/src/main/kotlin/com/flipperdevices/infrared/editor/viewmodel/InfraredKeyParser.kt diff --git a/modules/kenerator/configuration/build.gradle.kts b/modules/kenerator/configuration/build.gradle.kts new file mode 100644 index 0000000..cb8ed05 --- /dev/null +++ b/modules/kenerator/configuration/build.gradle.kts @@ -0,0 +1,43 @@ +import ru.astrainteractive.gradleplugin.property.extension.ModelPropertyValueExt.requireProjectInfo + +plugins { + id("org.jetbrains.kotlin.jvm") + application + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.gradle.shadow) +} + +dependencies { + // Exposed + implementation(libs.exposed.core) + implementation(libs.exposed.dao) + implementation(libs.kotlin.coroutines) + implementation(libs.kotlin.serialization.json) + implementation(kotlin("test")) + // Local + implementation(projects.modules.buildKonfig) + implementation(projects.modules.database) + implementation(projects.modules.core) + implementation(projects.modules.model) + implementation(projects.modules.database) + implementation(projects.modules.infrared) + implementation(projects.modules.kenerator.sql) + implementation(projects.modules.kenerator.paths) +} + +application { + mainClass.set("com.flipperdevices.ifrmvp.generator.config.MainKt") +} + +tasks.shadowJar { + isReproducibleFileOrder = true + mergeServiceFiles() + dependsOn(configurations) + archiveClassifier.set(null as String?) + from(sourceSets.main.get().output) + from(project.configurations.runtimeClasspath) + + archiveVersion.set(requireProjectInfo.versionString) + archiveBaseName.set("${requireProjectInfo.name}-configgenerator") + File(rootDir, "jars").also(File::mkdirs).also(destinationDirectory::set) +} diff --git a/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/Main.kt b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/Main.kt new file mode 100644 index 0000000..21fffb4 --- /dev/null +++ b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/Main.kt @@ -0,0 +1,54 @@ +package com.flipperdevices.ifrmvp.generator.config + +import com.flipperdevices.ifrmvp.backend.model.CategoryType +import com.flipperdevices.ifrmvp.generator.config.category.api.AllCategoryConfigGenerator +import com.flipperdevices.ifrmvp.generator.config.device.api.DefaultDeviceConfigGenerator +import com.flipperdevices.ifrmvp.generator.config.device.api.any.AnyDeviceKeyNamesProvider +import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + +private val json = Json { + isLenient = true + ignoreUnknownKeys = true + prettyPrint = true +} + +private fun generateCategoriesConfigFiles() { + ParserPathResolver + .categories + .onEach { categoryFolder -> + val categoryType = CategoryType.entries.first { it.folderName == categoryFolder.name } + val config = AllCategoryConfigGenerator.generate(categoryType) + val configFile = ParserPathResolver.categoryMetaPath(categoryFolder.name).resolve("config.json") + if (configFile.exists()) configFile.delete() + configFile.createNewFile() + val string = json.encodeToString(config) + configFile.writeText(string) + } +} + +private fun generateDevicesConfigFiles() { + ParserPathResolver + .categories + .onEach { categopryFolder -> + ParserPathResolver.brands(categopryFolder.name) + .onEach { brandFolder -> + ParserPathResolver.brandIfrFiles( + category = categopryFolder.name, + brand = brandFolder.name + ).onEach { irFile -> + val config = DefaultDeviceConfigGenerator(AnyDeviceKeyNamesProvider) + .generate(irFile) + val configFile = irFile.parentFile.resolve("config.json") + val string = json.encodeToString(config) + configFile.writeText(string) + } + } + } +} + +fun main() { + generateCategoriesConfigFiles() + generateDevicesConfigFiles() +} diff --git a/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/category/api/AllCategoryConfigGenerator.kt b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/category/api/AllCategoryConfigGenerator.kt new file mode 100644 index 0000000..3875cbb --- /dev/null +++ b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/category/api/AllCategoryConfigGenerator.kt @@ -0,0 +1,104 @@ +package com.flipperdevices.ifrmvp.generator.config.category.api + +import com.flipperdevices.ifrmvp.backend.model.CategoryConfiguration +import com.flipperdevices.ifrmvp.backend.model.CategoryType +import com.flipperdevices.ifrmvp.backend.model.DeviceKey +import com.flipperdevices.ifrmvp.generator.config.category.api.DeviceKeyExt.getAllowedCategories +import com.flipperdevices.ifrmvp.model.buttondata.IconButtonData +import com.flipperdevices.ifrmvp.model.buttondata.TextButtonData + +object AllCategoryConfigGenerator { + /** + * Generate all categories configurations + * + * Some categories wil have the same orders. We need [DeviceKeyExt.getAllowedCategories] + * to understand for which category we need keys + */ + @Suppress("LongMethod") + fun generate(): CategoryConfiguration { + return CategoryConfiguration( + orders = DeviceKey.entries.mapIndexed { index, key -> + when (key) { + DeviceKey.PWR -> CategoryConfiguration.OrderModel( + data = IconButtonData(iconId = IconButtonData.IconType.POWER), + message = "Does %s turns on?", + key = key, + index = index, + ) + + DeviceKey.VOL_UP -> CategoryConfiguration.OrderModel( + message = "Does volume go up?", + data = TextButtonData(text = "+"), + key = key, + index = index, + ) + + DeviceKey.VOL_DOWN -> CategoryConfiguration.OrderModel( + message = "Does volume go down?", + data = TextButtonData(text = "-"), + key = key, + index = index, + ) + + DeviceKey.CH_UP -> CategoryConfiguration.OrderModel( + message = "Does channel go next?", + data = TextButtonData(text = "+"), + key = key, + index = index, + ) + + DeviceKey.CH_DOWN -> CategoryConfiguration.OrderModel( + message = "Does channel go previous?", + data = TextButtonData(text = "-"), + key = key, + index = index, + ) + + DeviceKey.FOCUS_MORE -> CategoryConfiguration.OrderModel( + message = "Does key focuses more?", + data = TextButtonData(text = "+"), + key = key, + index = index, + ) + + DeviceKey.FOCUS_LESS -> CategoryConfiguration.OrderModel( + message = "Does key focuses less?", + data = TextButtonData(text = "-"), + key = key, + index = index, + ) + + DeviceKey.ZOOM_UP -> CategoryConfiguration.OrderModel( + message = "Does key zoom more?", + data = TextButtonData(text = "+"), + key = key, + index = index, + ) + + DeviceKey.ZOOM_DOWN -> CategoryConfiguration.OrderModel( + message = "Does key zoom less?", + data = TextButtonData(text = "-"), + key = key, + index = index, + ) + + else -> CategoryConfiguration.OrderModel( + message = "Does it do something?", + data = TextButtonData(text = key.name), + key = key, + index = index, + ) + } + } + ) + } + + fun generate(categoryType: CategoryType): CategoryConfiguration { + val configuration = generate() + return CategoryConfiguration( + orders = configuration.orders.filter { + it.key.getAllowedCategories().contains(categoryType) + } + ) + } +} diff --git a/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/category/api/DeviceKeyExt.kt b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/category/api/DeviceKeyExt.kt new file mode 100644 index 0000000..d1c56d5 --- /dev/null +++ b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/category/api/DeviceKeyExt.kt @@ -0,0 +1,200 @@ +package com.flipperdevices.ifrmvp.generator.config.category.api + +import com.flipperdevices.ifrmvp.backend.model.CategoryType +import com.flipperdevices.ifrmvp.backend.model.DeviceKey + +object DeviceKeyExt { + @Suppress("LongMethod", "CyclomaticComplexMethod") + fun DeviceKey.getAllowedCategories(): List { + return when (this) { + DeviceKey.PWR -> listOf( + CategoryType.TVS, + CategoryType.AIR_PURIFIERS, + CategoryType.BOX, + CategoryType.FAN, + CategoryType.PROJECTOR + ) + + DeviceKey.VOL_UP -> listOf( + CategoryType.TVS, + CategoryType.BOX, + CategoryType.DVD, + CategoryType.PROJECTOR + ) + + DeviceKey.VOL_DOWN -> listOf( + CategoryType.TVS, + CategoryType.BOX, + CategoryType.DVD, + CategoryType.PROJECTOR + ) + + DeviceKey.CH_UP -> listOf( + CategoryType.TVS, + CategoryType.BOX + ) + + DeviceKey.CH_DOWN -> listOf( + CategoryType.TVS, + CategoryType.BOX + ) + + DeviceKey.FOCUS_MORE -> listOf( + CategoryType.PROJECTOR + ) + + DeviceKey.FOCUS_LESS -> listOf( + CategoryType.PROJECTOR + ) + + DeviceKey.ZOOM_UP -> listOf( + CategoryType.PROJECTOR + ) + + DeviceKey.ZOOM_DOWN -> listOf( + CategoryType.PROJECTOR + ) + + DeviceKey.RESET -> listOf( + CategoryType.PROJECTOR + ) + + DeviceKey.DOWN -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.BOX, + CategoryType.CAMERA, + CategoryType.DVD, + CategoryType.PROJECTOR + ) + + DeviceKey.UP -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.BOX, + CategoryType.CAMERA, + CategoryType.DVD, + CategoryType.PROJECTOR + ) + + DeviceKey.RIGHT -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.BOX, + CategoryType.CAMERA, + CategoryType.DVD, + CategoryType.PROJECTOR + ) + + DeviceKey.LEFT -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.BOX, + CategoryType.CAMERA, + CategoryType.DVD, + CategoryType.PROJECTOR + ) + + DeviceKey.NEXT -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.CAMERA, + CategoryType.DVD + ) + + DeviceKey.PREVIOUS -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.CAMERA, + CategoryType.DVD + ) + + DeviceKey.TV -> listOf( + CategoryType.A_V_RECEIVER + ) + + DeviceKey.AUX -> listOf( + CategoryType.A_V_RECEIVER + ) + + DeviceKey.HOME -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.BOX, + CategoryType.DVD + ) + + DeviceKey.BACK -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.BOX, + CategoryType.CAMERA, + CategoryType.PROJECTOR + ) + + DeviceKey.MENU -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.CAMERA, + CategoryType.DVD, + CategoryType.PROJECTOR + ) + + DeviceKey.PLAY -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.DVD + ) + + DeviceKey.MUTE -> listOf( + CategoryType.A_V_RECEIVER, + CategoryType.AIR_PURIFIERS, + CategoryType.DVD, + CategoryType.FAN, + CategoryType.PROJECTOR + ) + + DeviceKey.FAN_SPEED -> listOf( + CategoryType.AIR_PURIFIERS + ) + + DeviceKey.NEAR -> listOf( + CategoryType.CAMERA + ) + + DeviceKey.FAR -> listOf( + CategoryType.CAMERA + ) + + DeviceKey.PAUSE -> listOf( + CategoryType.DVD + ) + + DeviceKey.WIND_SPEED -> listOf( + CategoryType.FAN + ) + + DeviceKey.MODE -> listOf( + CategoryType.FAN + ) + + DeviceKey.FAN_SPEED_UP -> listOf( + CategoryType.FAN + ) + + DeviceKey.FAN_SPEED_DOWN -> listOf( + CategoryType.FAN + ) + + DeviceKey.SHAKE_WIND -> listOf( + CategoryType.FAN + ) + + DeviceKey.WIND_TYPE -> listOf( + CategoryType.FAN + ) + + DeviceKey.TEMPERATURE_UP -> listOf( + CategoryType.FAN + ) + + DeviceKey.TEMPERATURE_DOWN -> listOf( + CategoryType.FAN + ) + + DeviceKey.ENERGY_SAVE -> listOf( + CategoryType.FAN + ) + } + } +} diff --git a/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DefaultDeviceConfigGenerator.kt b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DefaultDeviceConfigGenerator.kt new file mode 100644 index 0000000..f06357d --- /dev/null +++ b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DefaultDeviceConfigGenerator.kt @@ -0,0 +1,28 @@ +package com.flipperdevices.ifrmvp.generator.config.device.api + +import com.flipperdevices.ifrmvp.backend.model.DeviceConfiguration +import com.flipperdevices.ifrmvp.generator.config.device.api.DeviceKeyNamesProvider.Companion.getKey +import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier +import com.flipperdevices.infrared.editor.encoding.ByteArrayEncoder +import com.flipperdevices.infrared.editor.encoding.JvmEncoder +import com.flipperdevices.infrared.editor.util.InfraredRemoteEncoder +import java.io.File + +class DefaultDeviceConfigGenerator(private val keyNamesProvider: DeviceKeyNamesProvider) : DeviceConfigGenerator { + override fun generate(irFile: File): DeviceConfiguration { + val remotes = DeviceConfigGenerator.parseRemotes(irFile) + val remoteNameToCount = remotes.groupBy { it.name } + val deviceKeyToInstance = remotes.mapNotNull { + val name = it.name + val deviceKey = keyNamesProvider.getKey(name) ?: return@mapNotNull null + val byteArray = InfraredRemoteEncoder.encode(it) + val hash = JvmEncoder(ByteArrayEncoder.Algorithm.SHA_256).encode(byteArray) + val identifier = when { + (remoteNameToCount[name]?.size ?: 0) > 1 -> IfrKeyIdentifier.Sha256(name = name, hash = hash) + else -> IfrKeyIdentifier.Name(name = name) + } + deviceKey to identifier + }.associate { pair -> pair } + return DeviceConfiguration(deviceKeyToInstance) + } +} diff --git a/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DeviceConfigGenerator.kt b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DeviceConfigGenerator.kt new file mode 100644 index 0000000..c5aba48 --- /dev/null +++ b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DeviceConfigGenerator.kt @@ -0,0 +1,20 @@ +package com.flipperdevices.ifrmvp.generator.config.device.api + +import com.flipperdevices.bridge.dao.api.model.FlipperFileFormat +import com.flipperdevices.ifrmvp.backend.model.DeviceConfiguration +import com.flipperdevices.infrared.editor.model.InfraredRemote +import com.flipperdevices.infrared.editor.viewmodel.InfraredKeyParser +import java.io.File + +interface DeviceConfigGenerator { + fun generate(irFile: File): DeviceConfiguration + + companion object { + fun parseRemotes(irFile: File): List { + val content = irFile.readText() + val fff = FlipperFileFormat.fromFileContent(content) + val remotes = InfraredKeyParser.mapParsedKeyToInfraredRemotes(fff) + return remotes + } + } +} diff --git a/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DeviceKeyNamesProvider.kt b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DeviceKeyNamesProvider.kt new file mode 100644 index 0000000..dcb9dec --- /dev/null +++ b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/DeviceKeyNamesProvider.kt @@ -0,0 +1,14 @@ +package com.flipperdevices.ifrmvp.generator.config.device.api + +import com.flipperdevices.ifrmvp.backend.model.DeviceKey + +interface DeviceKeyNamesProvider { + fun getKeyNames(key: DeviceKey): List + + companion object { + fun DeviceKeyNamesProvider.getKey(keyName: String): DeviceKey? { + return DeviceKey.entries + .firstOrNull { deviceKey -> getKeyNames(deviceKey).contains(keyName) } + } + } +} diff --git a/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/any/AnyDeviceKeyNamesProvider.kt b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/any/AnyDeviceKeyNamesProvider.kt new file mode 100644 index 0000000..9d6d068 --- /dev/null +++ b/modules/kenerator/configuration/src/main/kotlin/com/flipperdevices/ifrmvp/generator/config/device/api/any/AnyDeviceKeyNamesProvider.kt @@ -0,0 +1,191 @@ +package com.flipperdevices.ifrmvp.generator.config.device.api.any + +import com.flipperdevices.ifrmvp.backend.model.DeviceKey +import com.flipperdevices.ifrmvp.generator.config.device.api.DeviceKeyNamesProvider + +object AnyDeviceKeyNamesProvider : DeviceKeyNamesProvider { + @Suppress("LongMethod", "CyclomaticComplexMethod") + override fun getKeyNames(key: DeviceKey): List { + return when (key) { + DeviceKey.PWR -> listOf( + "pwr", + "power", + "power_r", + "on", + ) + + DeviceKey.VOL_DOWN -> listOf( + "vol-", + "vol-_r" + ) + + DeviceKey.VOL_UP -> listOf( + "vol+", + "vol+_r" + ) + + DeviceKey.CH_UP -> listOf( + "ch+", + "ch+_r" + ) + + DeviceKey.CH_DOWN -> listOf( + "ch-", + "ch-_r" + ) + + DeviceKey.FOCUS_MORE -> listOf( + "focus-" + ) + + DeviceKey.FOCUS_LESS -> listOf( + "focus+" + ) + + DeviceKey.ZOOM_UP -> listOf( + "zoom+", + "zoom_up", + "digital zoom+" + ) + + DeviceKey.ZOOM_DOWN -> listOf( + "zoom-", + "zoom_down", + "digital zoom-" + ) + + DeviceKey.RESET -> listOf( + "reset" + ) + + DeviceKey.DOWN -> listOf( + "down", + "down_r" + ) + + DeviceKey.UP -> listOf( + "up", + "up_r" + ) + + DeviceKey.RIGHT -> listOf( + "right", + "right_r" + ) + + DeviceKey.LEFT -> listOf( + "left", + "left_r" + ) + + DeviceKey.NEXT -> listOf( + "next", + "next_r" + ) + + DeviceKey.PREVIOUS -> listOf( + "previous", + "previous_r" + ) + + DeviceKey.TV -> listOf( + "tv", + "tv_av", + "tv_r", + "tvp3", + "livetv", + "hd tv" + ) + + DeviceKey.AUX -> listOf( + "aux", + "aux_r", + "input aux", + "input aux_r" + ) + + DeviceKey.HOME -> listOf( + "home", + "home_r" + ) + + DeviceKey.BACK -> listOf( + "back", + "back_r" + ) + + DeviceKey.MENU -> listOf( + "menu", + "menu_r", + "pop-up menu", + "shortcut menu" + ) + + DeviceKey.PLAY -> listOf( + "play", + "play_r" + ) + + DeviceKey.MUTE -> listOf( + "mute", + "mute_r" + ) + + DeviceKey.FAN_SPEED -> listOf( + "fanspeed", + "fan_speed" + ) + + DeviceKey.NEAR -> listOf( + "near", + "p_near" + ) + + DeviceKey.FAR -> listOf( + "far" + ) + + DeviceKey.PAUSE -> listOf( + "pause" + ) + + DeviceKey.WIND_SPEED -> listOf( + "wind_speed" + ) + + DeviceKey.MODE -> listOf( + "mode", + "mode_r" + ) + + DeviceKey.FAN_SPEED_UP -> listOf( + "fanspeed+" + ) + + DeviceKey.FAN_SPEED_DOWN -> listOf( + "fanspeed-" + ) + + DeviceKey.SHAKE_WIND -> listOf( + "shake_wind" + ) + + DeviceKey.WIND_TYPE -> listOf( + "wind_type" + ) + + DeviceKey.TEMPERATURE_UP -> listOf( + "temperature_up" + ) + + DeviceKey.TEMPERATURE_DOWN -> listOf( + "temperature_down" + ) + + DeviceKey.ENERGY_SAVE -> listOf( + "energy save", + "energy save_r" + ) + } + } +} diff --git a/modules/kenerator/paths/build.gradle.kts b/modules/kenerator/paths/build.gradle.kts new file mode 100644 index 0000000..5765833 --- /dev/null +++ b/modules/kenerator/paths/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("org.jetbrains.kotlin.jvm") + alias(libs.plugins.kotlin.serialization) +} + +dependencies { + implementation(libs.kotlin.serialization.json) + implementation(kotlin("test")) + // Local + implementation(projects.modules.buildKonfig) + implementation(projects.modules.core) + implementation(projects.modules.model) + implementation(projects.modules.infrared) +} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/ParserPathResolver.kt b/modules/kenerator/paths/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/ParserPathResolver.kt similarity index 79% rename from modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/ParserPathResolver.kt rename to modules/kenerator/paths/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/ParserPathResolver.kt index ab486e1..a88ad15 100644 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/ParserPathResolver.kt +++ b/modules/kenerator/paths/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/ParserPathResolver.kt @@ -1,8 +1,9 @@ package com.flipperdevices.ifrmvp.parser.util import com.flipperdevices.ifrmvp.backend.envkonfig.EnvKonfig +import com.flipperdevices.ifrmvp.backend.model.CategoryConfiguration import com.flipperdevices.ifrmvp.backend.model.CategoryMeta -import com.flipperdevices.ifrmvp.parser.model.OrderModel +import com.flipperdevices.ifrmvp.backend.model.DeviceConfiguration import kotlinx.serialization.json.Json import java.io.File import kotlin.io.encoding.Base64 @@ -38,6 +39,15 @@ object ParserPathResolver { return categoriesFolder.resolve(category) } + fun categoryConfiguration(category: String): CategoryConfiguration { + val file = categoryConfigurationFile(category = category) + return Json.decodeFromString(file.readText()) + } + + fun categoryConfigurationFile(category: String): File { + return categoryMetaPath(category).resolve("config.json") + } + /** * Get all brands of current category * @param category the name of category @@ -106,6 +116,27 @@ object ParserPathResolver { return folder.resolve("$ifrFolderName.ir") } + fun irFileConfiguration( + category: String, + brand: String, + ifrFolderName: String + ): DeviceConfiguration { + val file = irFileConfigurationFile( + category = category, + brand = brand, + ifrFolderName = ifrFolderName + ) + return Json.decodeFromString(file.readText()) + } + + fun irFileConfigurationFile( + category: String, + brand: String, + ifrFolderName: String + ): File { + return brandPath(category, brand).resolve(ifrFolderName).resolve("config.json") + } + /** * Get specific ui preset file * @param category category name of brand @@ -122,21 +153,4 @@ object ParserPathResolver { val folder = brandPath(category, brand).resolve(ifrFolderName) return folder.resolve(presetFileName) } - - /** - * Get orders of selected controller - * @param category category name of brand - * @param brand the name of brand - * @param controller the name of controller - */ - fun controllerOrders( - category: String, - brand: String, - controller: String - ): List { - val controllerFolder = brandPath(category, brand).resolve(controller) - val orders = controllerFolder.resolve("orders.json") - if (!orders.exists()) return emptyList() - return Json.decodeFromString>(orders.readText()) - } } diff --git a/modules/parser/src/test/kotlin/com/flipperdevices/ifrmvp/parser/ParserPathResolverTest.kt b/modules/kenerator/paths/src/test/kotlin/com/flipperdevices/ifrmvp/parser/ParserPathResolverTest.kt similarity index 100% rename from modules/parser/src/test/kotlin/com/flipperdevices/ifrmvp/parser/ParserPathResolverTest.kt rename to modules/kenerator/paths/src/test/kotlin/com/flipperdevices/ifrmvp/parser/ParserPathResolverTest.kt diff --git a/modules/parser/build.gradle.kts b/modules/kenerator/sql/build.gradle.kts similarity index 82% rename from modules/parser/build.gradle.kts rename to modules/kenerator/sql/build.gradle.kts index 607891c..e20a780 100644 --- a/modules/parser/build.gradle.kts +++ b/modules/kenerator/sql/build.gradle.kts @@ -16,11 +16,12 @@ dependencies { implementation(kotlin("test")) // Local implementation(projects.modules.buildKonfig) - implementation(projects.modules.apiStatus) + implementation(projects.modules.database) implementation(projects.modules.core) - implementation(projects.modules.sharedBackendModel) - implementation(projects.modules.sharedUiModel) - implementation(projects.modules.apiStatus) + implementation(projects.modules.model) + implementation(projects.modules.database) + implementation(projects.modules.infrared) + implementation(projects.modules.kenerator.paths) } application { diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/Main.kt b/modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/Main.kt similarity index 99% rename from modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/Main.kt rename to modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/Main.kt index 19580f7..dc2f566 100644 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/Main.kt +++ b/modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/Main.kt @@ -44,6 +44,7 @@ fun mainUnused() { } } +@Suppress("NestedBlockDepth") fun fixMiRemoteDatabase() { val miRemoteFolder = ParserPathResolver.irDbFolderFolder .parentFile diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/di/ParserModule.kt b/modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/di/ParserModule.kt similarity index 55% rename from modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/di/ParserModule.kt rename to modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/di/ParserModule.kt index 01aca18..089be67 100644 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/di/ParserModule.kt +++ b/modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/di/ParserModule.kt @@ -1,20 +1,14 @@ package com.flipperdevices.ifrmvp.parser.di import com.flipperdevices.ifrmvp.backend.db.signal.di.SignalApiModule -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import com.flipperdevices.ifrmvp.parser.api.SignalTableApiImpl import com.flipperdevices.ifrmvp.parser.presentation.FillerController internal interface ParserModule { - val signalTableApi: SignalTableApi val fillerController: FillerController class Default(signalApiModule: SignalApiModule) : ParserModule { - override val signalTableApi: SignalTableApi by lazy { - SignalTableApiImpl(signalApiModule.database) - } override val fillerController: FillerController by lazy { - FillerController(signalTableApi) + FillerController(signalApiModule.database) } } } diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/RawIfrRemote.kt b/modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/RawIfrRemote.kt similarity index 100% rename from modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/RawIfrRemote.kt rename to modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/RawIfrRemote.kt diff --git a/modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/FillerController.kt b/modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/FillerController.kt new file mode 100644 index 0000000..55146de --- /dev/null +++ b/modules/kenerator/sql/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/FillerController.kt @@ -0,0 +1,207 @@ +package com.flipperdevices.ifrmvp.parser.presentation + +import com.flipperdevices.bridge.dao.api.model.FlipperFileFormat +import com.flipperdevices.ifrmvp.backend.core.IoCoroutineScope +import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryMetaTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.InfraredFileTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.InfraredFileToSignalTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalKeyTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.UiPresetTable +import com.flipperdevices.ifrmvp.backend.model.SignalModel +import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier +import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver +import com.flipperdevices.infrared.editor.encoding.ByteArrayEncoder +import com.flipperdevices.infrared.editor.encoding.JvmEncoder +import com.flipperdevices.infrared.editor.model.InfraredRemote +import com.flipperdevices.infrared.editor.util.InfraredRemoteEncoder +import com.flipperdevices.infrared.editor.viewmodel.InfraredKeyParser +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.JoinType +import org.jetbrains.exposed.sql.andWhere +import org.jetbrains.exposed.sql.batchInsert +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.transactions.transaction +import java.io.File + +internal class FillerController(private val database: Database) : CoroutineScope by IoCoroutineScope() { + + fun fillDatabase() = launch { + transaction(database) { + // insert all categories + CategoryTable.batchInsert(ParserPathResolver.categories) { categoriesFolder -> + this[CategoryTable.folderName] = categoriesFolder.name + } + // Insert all brands + ParserPathResolver.categories + .map(File::nameWithoutExtension) + .forEach { categoryFolder -> + val brands = ParserPathResolver.brands(categoryFolder) + val categoryId = CategoryTable.select(CategoryTable.id) + .where { CategoryTable.folderName eq categoryFolder } + .map { it[CategoryTable.id] } + .first() + BrandTable.batchInsert(brands) { + this[BrandTable.categoryId] = categoryId + this[BrandTable.folderName] = it.name + } + // Category meta + val categoryMeta = ParserPathResolver.categoryMeta(categoryFolder) + CategoryMetaTable.insert { + it[CategoryMetaTable.categoryId] = categoryId + it[CategoryMetaTable.displayName] = categoryMeta.manifest.displayName + it[CategoryMetaTable.singularDisplayName] = categoryMeta.manifest.singularDisplayName + it[CategoryMetaTable.iconPngBase64] = categoryMeta.iconPngBase64 + it[CategoryMetaTable.iconSvgBase64] = categoryMeta.iconSvgBase64 + } + } + // Infrared files + ParserPathResolver.categories + .map(File::nameWithoutExtension) + .forEach { categoryFolder -> + val categoryId = CategoryTable.select(CategoryTable.id) + .where { CategoryTable.folderName eq categoryFolder } + .map { it[CategoryTable.id] } + .first() + val brands = ParserPathResolver.brands(categoryFolder) + brands.forEach { brand -> + val irFiles = ParserPathResolver.brandIfrFiles( + category = categoryFolder, + brand = brand.name + ) + val brandId = BrandTable.select(BrandTable.id) + .where { BrandTable.folderName eq brand.name } + .andWhere { BrandTable.categoryId eq categoryId } + .map { it[BrandTable.id] } + .first() + InfraredFileTable.batchInsert(irFiles) { + this[InfraredFileTable.brandId] = brandId + this[InfraredFileTable.fileName] = it.name + this[InfraredFileTable.folderName] = it.parentFile.name + } + // Presets + val uiFiles = irFiles.mapNotNull { irFile -> + irFile.parentFile + .resolve("ui.json") + .takeIf(File::exists) + } + UiPresetTable.batchInsert(uiFiles) { + this[UiPresetTable.fileName] = it.name + this[UiPresetTable.infraredFileId] = InfraredFileTable + .select(InfraredFileTable.id) + .where { InfraredFileTable.brandId eq brandId } + .andWhere { InfraredFileTable.folderName eq it.parentFile.name } + } + // Signals + irFiles.forEach { irFile -> + val signals = irFile.readText() + .let(FlipperFileFormat::fromFileContent) + .let(InfraredKeyParser::mapParsedKeyToInfraredRemotes) + SignalTable.batchInsert(signals, ignore = true) { remote -> + val parsedRemote = remote as? InfraredRemote.Parsed + val rawRemote = remote as? InfraredRemote.Raw + this[SignalTable.brandId] = brandId + this[SignalTable.name] = remote.name + this[SignalTable.type] = remote.type + this[SignalTable.protocol] = parsedRemote?.protocol + this[SignalTable.address] = parsedRemote?.address + this[SignalTable.command] = parsedRemote?.command + this[SignalTable.frequency] = rawRemote?.frequency + this[SignalTable.dutyCycle] = rawRemote?.dutyCycle + this[SignalTable.data] = rawRemote?.data + val byteArray = InfraredRemoteEncoder.encode(remote) + val hash = JvmEncoder(ByteArrayEncoder.Algorithm.SHA_256).encode(byteArray) + this[SignalTable.hash] = hash + } + // ManyToMany file to signal references + val irFileId = InfraredFileTable + .select(InfraredFileTable.id) + .where { InfraredFileTable.brandId eq brandId } + .andWhere { InfraredFileTable.folderName eq irFile.parentFile.name } + .map { it[InfraredFileTable.id] } + .first() + + val signalIds = signals.map { + val parsedRemote = it as? InfraredRemote.Parsed + val rawRemote = it as? InfraredRemote.Raw + SignalTable + .select(SignalTable.id) + .where { SignalTable.brandId eq brandId } + .andWhere { SignalTable.name eq it.name } + .andWhere { SignalTable.type eq it.type } + .andWhere { SignalTable.protocol eq parsedRemote?.protocol } + .andWhere { SignalTable.address eq parsedRemote?.address } + .andWhere { SignalTable.command eq parsedRemote?.command } + .andWhere { SignalTable.frequency eq rawRemote?.frequency } + .andWhere { SignalTable.dutyCycle eq rawRemote?.dutyCycle } + .andWhere { SignalTable.data eq rawRemote?.data } + .map { it[SignalTable.id] } + .first() + } + InfraredFileToSignalTable.batchInsert(signalIds) { + this[InfraredFileToSignalTable.infraredFileId] = irFileId + this[InfraredFileToSignalTable.signalId] = it + } + // Key Table + val irFileConfiguration = ParserPathResolver.irFileConfiguration( + category = categoryFolder, + brand = brand.name, + ifrFolderName = irFile.parentFile.name + ) + SignalKeyTable.batchInsert(irFileConfiguration.keyMap.entries) { (baseKey, keyIdentifier) -> + this[SignalKeyTable.infraredFileId] = irFileId + this[SignalKeyTable.deviceKey] = baseKey + when (keyIdentifier) { + IfrKeyIdentifier.Empty -> { + this[SignalKeyTable.type] = IfrKeyIdentifier.Empty.TYPE + } + + is IfrKeyIdentifier.Name -> { + this[SignalKeyTable.remoteKeyName] = keyIdentifier.name + this[SignalKeyTable.type] = IfrKeyIdentifier.Name.TYPE + } + + is IfrKeyIdentifier.Sha256 -> { + this[SignalKeyTable.remoteKeyName] = keyIdentifier.name + this[SignalKeyTable.type] = IfrKeyIdentifier.Sha256.TYPE + this[SignalKeyTable.hash] = keyIdentifier.hash + } + } + + this[SignalKeyTable.signalId] = SignalTable + .join( + otherTable = InfraredFileToSignalTable, + joinType = JoinType.LEFT, + onColumn = SignalTable.id, + otherColumn = InfraredFileToSignalTable.signalId + ) + .select(SignalTable.id) + .where { SignalTable.brandId eq brandId } + .andWhere { InfraredFileToSignalTable.infraredFileId eq irFileId } + .apply { + when (keyIdentifier) { + IfrKeyIdentifier.Empty -> this + is IfrKeyIdentifier.Name -> { + andWhere { SignalTable.name eq keyIdentifier.name } + } + + is IfrKeyIdentifier.Sha256 -> { + andWhere { SignalTable.name eq keyIdentifier.name } + .andWhere { SignalTable.hash eq keyIdentifier.hash } + } + } + } + .map { it[SignalTable.id] } + .also { assert(it.size == 1) } + .first() + } + } + } + } + } + } +} diff --git a/modules/ui-generator/build.gradle.kts b/modules/kenerator/ui/build.gradle.kts similarity index 63% rename from modules/ui-generator/build.gradle.kts rename to modules/kenerator/ui/build.gradle.kts index 50e7d86..028c605 100644 --- a/modules/ui-generator/build.gradle.kts +++ b/modules/kenerator/ui/build.gradle.kts @@ -12,10 +12,10 @@ dependencies { implementation(kotlin("test")) // Local implementation(projects.modules.buildKonfig) - implementation(projects.modules.apiStatus) + implementation(projects.modules.database) implementation(projects.modules.core) - implementation(projects.modules.sharedBackendModel) - implementation(projects.modules.sharedUiModel) - implementation(projects.modules.apiStatus) - implementation(projects.modules.parser) + implementation(projects.modules.model) + implementation(projects.modules.database) + implementation(projects.modules.infrared) + implementation(projects.modules.kenerator.configuration) } diff --git a/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt b/modules/kenerator/ui/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt similarity index 100% rename from modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt rename to modules/kenerator/ui/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt diff --git a/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt b/modules/kenerator/ui/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt similarity index 92% rename from modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt rename to modules/kenerator/ui/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt index c783107..7eb699d 100644 --- a/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt +++ b/modules/kenerator/ui/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt @@ -14,7 +14,11 @@ class UiGeneratorImpl : UiGenerator { val signals = remoteContent .let(FlipperFileFormat.Companion::fromFileContent) .let(InfraredKeyParser::mapParsedKeyToInfraredRemotes) - val chunks = signals.chunked(MAX_ROWS * MAX_COLUMNS) { it } + val windowSize = MAX_ROWS * MAX_COLUMNS + val chunks = when { + signals.size > windowSize -> signals.windowed(windowSize) { it } + else -> listOf(signals) + } return PagesLayout( pages = chunks.map { signals -> var x = -1 diff --git a/modules/shared-backend-model/build.gradle.kts b/modules/model/build.gradle.kts similarity index 86% rename from modules/shared-backend-model/build.gradle.kts rename to modules/model/build.gradle.kts index d8fe487..0076fff 100644 --- a/modules/shared-backend-model/build.gradle.kts +++ b/modules/model/build.gradle.kts @@ -19,6 +19,9 @@ kotlin { // Serialization implementation(libs.kotlin.serialization.json) } + commonTest.dependencies { + implementation(kotlin("test")) + } } } diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandModel.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandModel.kt similarity index 91% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandModel.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandModel.kt index fa3a36c..17cdbaf 100644 --- a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandModel.kt +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandModel.kt @@ -9,7 +9,7 @@ class BrandModel( @SerialName("id") val id: Long, @SerialName("name") - val name: String, + val folderName: String, @SerialName("category_id") val categoryId: Long, ) diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandsResponse.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandsResponse.kt similarity index 100% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandsResponse.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/BrandsResponse.kt diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesRequestBody.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesRequestBody.kt similarity index 100% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesRequestBody.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesRequestBody.kt diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesResponse.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesResponse.kt similarity index 100% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesResponse.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoriesResponse.kt diff --git a/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryConfiguration.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryConfiguration.kt new file mode 100644 index 0000000..687e1dd --- /dev/null +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryConfiguration.kt @@ -0,0 +1,25 @@ +package com.flipperdevices.ifrmvp.backend.model + +import com.flipperdevices.ifrmvp.model.buttondata.ButtonData +import com.flipperdevices.ifrmvp.model.serialization.ButtonDataSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +class CategoryConfiguration( + @SerialName("orders") + val orders: List +) { + @Serializable + data class OrderModel( + @SerialName("message") + val message: String, + @SerialName("index") + val index: Int, + @SerialName("key") + val key: DeviceKey, + @Serializable(ButtonDataSerializer::class) + @SerialName("data") + val data: ButtonData + ) +} diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryManifest.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryManifest.kt similarity index 100% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryManifest.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryManifest.kt diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryMeta.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryMeta.kt similarity index 100% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryMeta.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryMeta.kt diff --git a/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryType.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryType.kt new file mode 100644 index 0000000..3815711 --- /dev/null +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/CategoryType.kt @@ -0,0 +1,12 @@ +package com.flipperdevices.ifrmvp.backend.model + +enum class CategoryType(val folderName: String) { + A_V_RECEIVER("A_V_receiver"), + AIR_PURIFIERS("Air_Purifiers"), + BOX("Box"), + CAMERA("Camera"), + DVD("DVD"), + FAN("Fan"), + PROJECTOR("Projector"), + TVS("TVs") +} diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceCategory.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceCategory.kt similarity index 75% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceCategory.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceCategory.kt index a9b8d8d..287405b 100644 --- a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceCategory.kt +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceCategory.kt @@ -9,5 +9,7 @@ class DeviceCategory( @SerialName("id") val id: Long, @SerialName("meta") - val meta: CategoryMeta + val meta: CategoryMeta, + @SerialName("folder_name") + val folderName: String ) diff --git a/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceConfiguration.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceConfiguration.kt new file mode 100644 index 0000000..3718126 --- /dev/null +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceConfiguration.kt @@ -0,0 +1,11 @@ +package com.flipperdevices.ifrmvp.backend.model + +import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +class DeviceConfiguration( + @SerialName("key_map") + val keyMap: Map +) \ No newline at end of file diff --git a/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceKey.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceKey.kt new file mode 100644 index 0000000..84703a5 --- /dev/null +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/DeviceKey.kt @@ -0,0 +1,116 @@ +package com.flipperdevices.ifrmvp.backend.model + +/** + * [DeviceKey] is default set of pre-defined keys for remote controls + * + * The keys have comments in which devices they are present + */ +enum class DeviceKey { + // TV, Air_Purifiers, Box, DVD, Fan, Projector + PWR, + + // TV, Box, Camera, DVD, Projector + VOL_UP, + + // TV, Box, Camera, DVD, Projector + VOL_DOWN, + + // TV, Box + CH_UP, + + // TV, Box + CH_DOWN, + + // Projector + FOCUS_MORE, + + // Projector + FOCUS_LESS, + + // Projector + ZOOM_UP, + + // Projector + ZOOM_DOWN, + + // Projector + RESET, + + // A_V_receiver, Box, Camera, DVD, Projector + DOWN, + + // A_V_receiver, Box, Camera, DVD, Projector + UP, + + // A_V_receiver, Box, Camera, DVD, Projector + RIGHT, + + // A_V_receiver, Box, Camera, DVD, Projector + LEFT, + + // A_V_receiver, Camera, DVD + NEXT, + + // A_V_receiver, Camera, DVD + PREVIOUS, + + // A_V_receiver + TV, + + // A_V_receiver + AUX, + + // A_V_receiver, Box, DVD + HOME, + + // A_V_receiver, Box, Camera, Projector + BACK, + + // A_V_receiver, Camera, DVD, Projector + MENU, + + // A_V_receiver, DVD + PLAY, + + // A_V_receiver, Air_Purifiers, DVD, Fan, Projector + MUTE, + + // Air_Purifiers + FAN_SPEED, + + // Camera + NEAR, + + // Camera + FAR, + + // DVD + PAUSE, + + // Fan + WIND_SPEED, + + // Fan + MODE, + + // Fan + FAN_SPEED_UP, + + // Fan + FAN_SPEED_DOWN, + + // Fan + SHAKE_WIND, + + // Fan + WIND_TYPE, + + // Fan + TEMPERATURE_UP, + + // Fan + TEMPERATURE_DOWN, + + // Fan + ENERGY_SAVE +} diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorResponseModel.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorResponseModel.kt similarity index 100% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorResponseModel.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorResponseModel.kt diff --git a/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorType.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorType.kt new file mode 100644 index 0000000..488cb57 --- /dev/null +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorType.kt @@ -0,0 +1,10 @@ +package com.flipperdevices.ifrmvp.backend.model + +enum class ErrorType { + UNHANDLED, + CATEGORY_NOT_FOUND, + CATEGORY_META_NOT_FOUND, + BRAND_NOT_FOUND, + IR_FILE_NOT_FOUND, + NO_SIGNAL +} diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileContentResponse.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileContentResponse.kt similarity index 100% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileContentResponse.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileContentResponse.kt diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileModel.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileModel.kt similarity index 77% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileModel.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileModel.kt index 80cbd89..80210c5 100644 --- a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileModel.kt +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/IfrFileModel.kt @@ -7,10 +7,10 @@ import kotlinx.serialization.Serializable data class IfrFileModel( @SerialName("id") val id: Long, - @SerialName("category_id") - val categoryId: Long, @SerialName("brand_id") val brandId: Long, @SerialName("file_name") - val fileName: String + val fileName: String, + @SerialName("folder_name") + val folderName: String ) diff --git a/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalModel.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalModel.kt new file mode 100644 index 0000000..8ec2834 --- /dev/null +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalModel.kt @@ -0,0 +1,32 @@ +package com.flipperdevices.ifrmvp.backend.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class SignalModel( + @SerialName("id") + val id: Long, + @SerialName("remote") + val remote: FlipperRemote, +) { + @Serializable + data class FlipperRemote( + @SerialName("name") + val name: String, + @SerialName("type") + val type: String, + @SerialName("protocol") + val protocol: String? = null, + @SerialName("address") + val address: String? = null, + @SerialName("command") + val command: String? = null, + @SerialName("frequency") + val frequency: String? = null, + @SerialName("duty_cycle") + val dutyCycle: String? = null, + @SerialName("data") + val data: String? = null, + ) +} diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalRequestModel.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalRequestModel.kt similarity index 81% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalRequestModel.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalRequestModel.kt index 53dfdd5..f41c5ce 100644 --- a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalRequestModel.kt +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalRequestModel.kt @@ -9,8 +9,6 @@ data class SignalRequestModel( val successResults: List = emptyList(), @SerialName("failed_results") val failedResults: List = emptyList(), - @SerialName("category_id") - val categoryId: Long, @SerialName("brand_id") val brandId: Long ) { @@ -18,7 +16,5 @@ data class SignalRequestModel( data class SignalResultData( @SerialName("signal_id") val signalId: Long, - @SerialName("ifr_file_id") - val ifrFileId: Long ) } diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponse.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponse.kt similarity index 59% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponse.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponse.kt index 4325ca4..e38a0f7 100644 --- a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponse.kt +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponse.kt @@ -1,5 +1,7 @@ package com.flipperdevices.ifrmvp.backend.model +import com.flipperdevices.ifrmvp.model.buttondata.ButtonData +import com.flipperdevices.ifrmvp.model.serialization.ButtonDataSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -12,15 +14,6 @@ data class SignalResponse( @SerialName("category_name") val categoryName: String, @SerialName("data") - val data: Data, -) { - @Serializable - data class Data( - @SerialName("type") - val type: String, - @SerialName("icon_id") - val iconId: String? = null, - @SerialName("text") - val text: String? = null - ) -} + @Serializable(ButtonDataSerializer::class) + val data: ButtonData, +) diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponseModel.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponseModel.kt similarity index 100% rename from modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponseModel.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalResponseModel.kt diff --git a/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/UiPresetModel.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/UiPresetModel.kt new file mode 100644 index 0000000..f2fc8a1 --- /dev/null +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/UiPresetModel.kt @@ -0,0 +1,14 @@ +package com.flipperdevices.ifrmvp.backend.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UiPresetModel( + @SerialName("id") + val id: Long, + @SerialName("infrared_file_id") + val infraredFileId: Long, + @SerialName("file_name") + val fileName: String +) diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrButton.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrButton.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrButton.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrButton.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrKeyIdentifier.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrKeyIdentifier.kt similarity index 63% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrKeyIdentifier.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrKeyIdentifier.kt index c140726..357c129 100644 --- a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrKeyIdentifier.kt +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/IfrKeyIdentifier.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable * [IfrKeyIdentifier] is used to define the remote controller key * * For example, the .ir file contains multiple buttons. We need to use the ON/OFF buttons. - * Then we define [IfrKeyIdentifier.Sha256] where [IfrKeyIdentifier.Sha256.sha256String] + * Then we define [IfrKeyIdentifier.Sha256] where [IfrKeyIdentifier.Sha256.hash] * is the hashcode of its raw data. * It's required, so we can find exact signal by its identifier * @@ -18,19 +18,33 @@ sealed interface IfrKeyIdentifier { /** * SHA-256 of raw data */ - @SerialName("SHA_256") + @SerialName(Sha256.TYPE) @Serializable - class Sha256( + data class Sha256( @SerialName("key_name") val name: String, @SerialName("sha_256_string") - val sha256String: String - ) : IfrKeyIdentifier + val hash: String + ) : IfrKeyIdentifier { + companion object { + const val TYPE = "SHA_256" + } + } - @SerialName("NAME") + @SerialName(Name.TYPE) @Serializable - class Name( + data class Name( @SerialName("key_name") val name: String, - ) : IfrKeyIdentifier + ) : IfrKeyIdentifier { + companion object { + const val TYPE = "NAME" + } + } + + @SerialName(Empty.TYPE) + @Serializable + data object Empty : IfrKeyIdentifier { + const val TYPE = "EMPTY" + } } diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PageLayout.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PageLayout.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PageLayout.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PageLayout.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PagesLayout.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PagesLayout.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PagesLayout.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/PagesLayout.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/RawIfrButton.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/RawIfrButton.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/RawIfrButton.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/RawIfrButton.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/Base64ImageButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/Base64ImageButtonData.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/Base64ImageButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/Base64ImageButtonData.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ButtonData.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ButtonData.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ChannelButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ChannelButtonData.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ChannelButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/ChannelButtonData.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/IconButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/IconButtonData.kt similarity index 87% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/IconButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/IconButtonData.kt index 9dab385..0c8373f 100644 --- a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/IconButtonData.kt +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/IconButtonData.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable @Serializable data class IconButtonData( @SerialName("key_id") - override val keyIdentifier: IfrKeyIdentifier, + override val keyIdentifier: IfrKeyIdentifier = IfrKeyIdentifier.Empty, @SerialName("icon_id") val iconId: IconType ) : SingleKeyButtonData { diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/NavigationButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/NavigationButtonData.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/NavigationButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/NavigationButtonData.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/SingleKeyButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/SingleKeyButtonData.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/SingleKeyButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/SingleKeyButtonData.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/TextButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/TextButtonData.kt similarity index 84% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/TextButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/TextButtonData.kt index 8d140d5..6f2bd9e 100644 --- a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/TextButtonData.kt +++ b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/TextButtonData.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable @Serializable data class TextButtonData( @SerialName("key_id") - override val keyIdentifier: IfrKeyIdentifier, + override val keyIdentifier: IfrKeyIdentifier = IfrKeyIdentifier.Empty, @SerialName("text") val text: String ) : SingleKeyButtonData { diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/UnknownButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/UnknownButtonData.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/UnknownButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/UnknownButtonData.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/VolumeButtonData.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/VolumeButtonData.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/VolumeButtonData.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/buttondata/VolumeButtonData.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataDecoder.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataDecoder.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataDecoder.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataDecoder.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataEncoder.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataEncoder.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataEncoder.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataEncoder.kt diff --git a/modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializer.kt b/modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializer.kt similarity index 100% rename from modules/shared-ui-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializer.kt rename to modules/model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializer.kt diff --git a/modules/model/src/commonTest/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializerTest.kt b/modules/model/src/commonTest/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializerTest.kt new file mode 100644 index 0000000..1b29dc6 --- /dev/null +++ b/modules/model/src/commonTest/kotlin/com/flipperdevices/ifrmvp/model/serialization/ButtonDataSerializerTest.kt @@ -0,0 +1,33 @@ +package com.flipperdevices.ifrmvp.model.serialization + +import com.flipperdevices.ifrmvp.model.IfrButton +import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier +import com.flipperdevices.ifrmvp.model.buttondata.IconButtonData +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals + +class ButtonDataSerializerTest { + private val json = Json { + isLenient = true + ignoreUnknownKeys = true + } + + private inline fun assertSerializable(value: T) { + val string = json.encodeToString(value) + val decodedValue = json.decodeFromString(string) + assertEquals(value, decodedValue) + } + + @Test + fun test() { + IfrButton( + data = IconButtonData( + keyIdentifier = IfrKeyIdentifier.Name("Hello"), + iconId = IconButtonData.IconType.POWER + ), + position = IfrButton.Position(0, 0) + ).run(::assertSerializable) + } +} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/api/SignalTableApi.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/api/SignalTableApi.kt deleted file mode 100644 index 84ea5b5..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/api/SignalTableApi.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.api - -import com.flipperdevices.ifrmvp.backend.model.CategoryMeta -import com.flipperdevices.ifrmvp.parser.model.OrderModel -import com.flipperdevices.ifrmvp.parser.model.RawIfrRemote - -internal interface SignalTableApi { - suspend fun addCategory( - categoryFolderName: String, - ): Long - - suspend fun addBrand( - categoryId: Long, - displayName: String - ): Long - - suspend fun addIrFile( - fileName: String, - categoryId: Long, - brandId: Long - ): Long - - suspend fun addUiPreset( - categoryId: Long, - brandId: Long, - irFileId: Long, - fileName: String - ) - - suspend fun addSignal( - categoryId: Long, - brandId: Long, - irFileId: Long, - remote: RawIfrRemote - ): Long - - suspend fun addCategoryMeta( - categoryId: Long, - meta: CategoryMeta - ) - - suspend fun addOrderModel( - orderModel: OrderModel, - ifrSignalId: Long, - categoryId: Long, - brandId: Long, - ifrFileId: Long, - order: Int - ) -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/api/SignalTableApiImpl.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/api/SignalTableApiImpl.kt deleted file mode 100644 index 71b47a3..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/api/SignalTableApiImpl.kt +++ /dev/null @@ -1,221 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.api - -import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryMetaTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.IfrFileTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalOrderTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.UiPresetTable -import com.flipperdevices.ifrmvp.backend.model.CategoryMeta -import com.flipperdevices.ifrmvp.model.buttondata.IconButtonData -import com.flipperdevices.ifrmvp.model.buttondata.TextButtonData -import com.flipperdevices.ifrmvp.parser.model.OrderModel -import com.flipperdevices.ifrmvp.parser.model.RawIfrRemote -import com.flipperdevices.ifrmvp.parser.util.IfrRemoteExt.md5 -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.insertAndGetId -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction - -internal class SignalTableApiImpl( - private val database: Database -) : SignalTableApi { - - private fun checkCategoryExists(id: Long) { - val isExists = CategoryTable.selectAll() - .where { CategoryTable.id eq id } - .count() != 0L - check(isExists) { "Category does not exists" } - } - - private fun checkBrandExists(id: Long) { - val isExists = BrandTable.selectAll() - .where { BrandTable.id eq id } - .count() != 0L - check(isExists) { "Brand does not exists" } - } - - private fun checkIrFileExists(id: Long) { - val isExists = IfrFileTable.selectAll() - .where { IfrFileTable.id eq id } - .count() != 0L - check(isExists) { "IrFile does not exists" } - } - - override suspend fun addCategory( - categoryFolderName: String, - ): Long = transaction(database) { - CategoryTable.selectAll() - .where { CategoryTable.folderName eq categoryFolderName } - .map { it[CategoryTable.id] } - .firstOrNull() - ?.value - ?.let { existingCategoryId -> return@transaction existingCategoryId } - CategoryTable.insertAndGetId { statement -> - statement[CategoryTable.folderName] = categoryFolderName - }.value - } - - override suspend fun addBrand( - categoryId: Long, - displayName: String - ): Long = transaction(database) { - checkCategoryExists(categoryId) - BrandTable.selectAll() - .where { BrandTable.displayName eq displayName } - .andWhere { BrandTable.category eq categoryId } - .map { it[BrandTable.id] } - .firstOrNull() - ?.value - ?.let { existingBrandId -> return@transaction existingBrandId } - - BrandTable.insertAndGetId { statement -> - statement[BrandTable.category] = categoryId - statement[BrandTable.displayName] = displayName - }.value - } - - override suspend fun addIrFile( - fileName: String, - categoryId: Long, - brandId: Long - ): Long = transaction(database) { - checkCategoryExists(categoryId) - checkBrandExists(brandId) - IfrFileTable.selectAll() - .where { IfrFileTable.fileName eq fileName } - .andWhere { IfrFileTable.categoryId eq categoryId } - .andWhere { IfrFileTable.brandId eq brandId } - .map { it[IfrFileTable.id] } - .firstOrNull() - ?.value - ?.let { existingIrfFileId -> return@transaction existingIrfFileId } - - IfrFileTable.insertAndGetId { statement -> - statement[IfrFileTable.categoryId] = categoryId - statement[IfrFileTable.brandId] = brandId - statement[IfrFileTable.fileName] = fileName - }.value - } - - override suspend fun addUiPreset( - categoryId: Long, - brandId: Long, - irFileId: Long, - fileName: String - ): Unit = transaction(database) { - checkCategoryExists(categoryId) - checkBrandExists(brandId) - checkIrFileExists(irFileId) - - UiPresetTable.selectAll() - .where { UiPresetTable.ifrFileId eq irFileId } - .andWhere { UiPresetTable.categoryId eq categoryId } - .andWhere { UiPresetTable.brandId eq brandId } - .andWhere { UiPresetTable.fileName eq fileName } - .map { it[UiPresetTable.id] } - .firstOrNull() - ?.value - ?.let { _ -> return@transaction } - - UiPresetTable.insert { statement -> - statement[UiPresetTable.categoryId] = categoryId - statement[UiPresetTable.brandId] = brandId - statement[UiPresetTable.ifrFileId] = irFileId - statement[UiPresetTable.fileName] = fileName - } - } - - override suspend fun addSignal( - categoryId: Long, - brandId: Long, - irFileId: Long, - remote: RawIfrRemote - ): Long = transaction(database) { - checkCategoryExists(categoryId) - checkBrandExists(brandId) - checkIrFileExists(irFileId) - - SignalTable.selectAll() - .where { SignalTable.name eq remote.name } - .andWhere { SignalTable.type eq remote.type } - .andWhere { SignalTable.protocol eq remote.protocol } - .andWhere { SignalTable.address eq remote.address } - .andWhere { SignalTable.command eq remote.command } - .andWhere { SignalTable.frequency eq remote.frequency } - .andWhere { SignalTable.dutyCycle eq remote.dutyCycle } - .andWhere { SignalTable.data eq remote.data } - .andWhere { SignalTable.categoryId eq categoryId } - .andWhere { SignalTable.ifrFileId eq irFileId } - .andWhere { SignalTable.brandId eq brandId } - .map { it[SignalTable.id] } - .firstOrNull() - ?.value - ?.let { existingIrfSignalId -> return@transaction existingIrfSignalId } - - SignalTable.insertAndGetId { statement -> - statement[SignalTable.categoryId] = categoryId - statement[SignalTable.brandId] = brandId - statement[SignalTable.ifrFileId] = irFileId - statement[SignalTable.name] = remote.name - statement[SignalTable.type] = remote.type - statement[SignalTable.protocol] = remote.protocol - statement[SignalTable.address] = remote.address - statement[SignalTable.command] = remote.command - statement[SignalTable.frequency] = remote.frequency - statement[SignalTable.dutyCycle] = remote.dutyCycle - statement[SignalTable.data] = remote.data - statement[SignalTable.hash] = remote.md5 - }.value - } - - override suspend fun addCategoryMeta( - categoryId: Long, - meta: CategoryMeta - ): Unit = transaction(database) { - checkCategoryExists(categoryId) - CategoryMetaTable.selectAll() - .where { CategoryMetaTable.categoryId eq categoryId } - .map { it[CategoryMetaTable.id] } - .firstOrNull() - ?.value - ?.let { _ -> return@transaction Unit } - CategoryMetaTable.insert { statement -> - statement[CategoryMetaTable.categoryId] = categoryId - statement[CategoryMetaTable.displayName] = meta.manifest.displayName - statement[CategoryMetaTable.singularDisplayName] = meta.manifest.singularDisplayName - statement[CategoryMetaTable.iconPngBase64] = meta.iconPngBase64 - statement[CategoryMetaTable.iconSvgBase64] = meta.iconSvgBase64 - } - } - - override suspend fun addOrderModel( - orderModel: OrderModel, - ifrSignalId: Long, - categoryId: Long, - brandId: Long, - ifrFileId: Long, - order: Int - ): Unit = transaction(database) { - SignalOrderTable.insert { statement -> - statement[SignalOrderTable.categoryId] = categoryId - statement[SignalOrderTable.brandId] = brandId - statement[SignalOrderTable.ifrFileId] = ifrFileId - statement[SignalOrderTable.ifrSignalId] = ifrSignalId - statement[SignalOrderTable.message] = orderModel.message - statement[SignalOrderTable.dataType] = orderModel.data.type.name - statement[SignalOrderTable.order] = order - statement[SignalOrderTable.dataIconId] = when (orderModel.data) { - is IconButtonData -> orderModel.data.iconId.name - else -> null - } - statement[SignalOrderTable.dataText] = when (orderModel.data) { - is TextButtonData -> orderModel.data.text - else -> null - } - } - } -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/OrderModel.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/OrderModel.kt deleted file mode 100644 index 6242c9a..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/model/OrderModel.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.model - -import com.flipperdevices.ifrmvp.model.buttondata.ButtonData -import com.flipperdevices.ifrmvp.model.serialization.ButtonDataSerializer -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class OrderModel( - @SerialName("data") - @Serializable(with = ButtonDataSerializer::class) - val data: ButtonData, - @Serializable - val message: String -) diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/FillerController.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/FillerController.kt deleted file mode 100644 index 9633988..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/FillerController.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.presentation - -import com.flipperdevices.ifrmvp.backend.core.IoCoroutineScope -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import com.flipperdevices.ifrmvp.parser.presentation.filler.BrandsFiller -import com.flipperdevices.ifrmvp.parser.presentation.filler.CategoriesFiller -import com.flipperdevices.ifrmvp.parser.presentation.filler.IfrSignalFiller -import com.flipperdevices.ifrmvp.parser.presentation.filler.IrFilesFiller -import com.flipperdevices.ifrmvp.parser.presentation.filler.MetaFiller -import com.flipperdevices.ifrmvp.parser.presentation.filler.OrderFiller -import com.flipperdevices.ifrmvp.parser.presentation.filler.UiPresetFiller -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -internal class FillerController( - signalTableApi: SignalTableApi -) : CoroutineScope by IoCoroutineScope() { - private val orderFiller = OrderFiller(signalTableApi) - private val ifrSignalFiller = IfrSignalFiller(signalTableApi, orderFiller) - private val uiFileFiller = UiPresetFiller(signalTableApi) - private val irFilesFiller = IrFilesFiller(signalTableApi, ifrSignalFiller, uiFileFiller) - private val brandsFiller = BrandsFiller(signalTableApi, irFilesFiller) - private val metaFilter = MetaFiller(signalTableApi) - private val categoriesFiller = CategoriesFiller(signalTableApi, metaFilter, brandsFiller) - - fun fillDatabase() = launch { categoriesFiller.fill() } -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/BrandsFiller.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/BrandsFiller.kt deleted file mode 100644 index 1420222..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/BrandsFiller.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.presentation.filler - -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope -import java.io.File - -internal class BrandsFiller( - private val signalTableApi: SignalTableApi, - private val irFilesFiller: IrFilesFiller -) { - - suspend fun full(model: Model) = coroutineScope { - with(model) { - brands.map { brandFile -> - async { - val brandName = brandFile.name - val brandId = signalTableApi.addBrand( - categoryId = categoryId, - displayName = brandName - ) - irFilesFiller.fill( - model = IrFilesFiller.Model( - categoryId = categoryId, - brandId = brandId, - categoryName = categoryName, - brandName = brandName, - irFiles = ParserPathResolver.brandIfrFiles( - category = categoryName, - brand = brandName - ) - ) - ) - } - }.awaitAll() - } - } - data class Model( - val brands: List, - val categoryId: Long, - val categoryName: String - ) -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/CategoriesFiller.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/CategoriesFiller.kt deleted file mode 100644 index 2442351..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/CategoriesFiller.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.presentation.filler - -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver -import kotlinx.coroutines.coroutineScope - -internal class CategoriesFiller( - private val signalTableApi: SignalTableApi, - private val metaFiller: MetaFiller, - private val brandsFiller: BrandsFiller -) { - suspend fun fill() = coroutineScope { - ParserPathResolver.categories.map { categoryFile -> - val categoryName = categoryFile.name - val categoryId = signalTableApi.addCategory( - categoryFolderName = categoryName, - ) - metaFiller.fill( - model = MetaFiller.Model( - categoryId = categoryId, - categoryName = categoryName - ) - ) - brandsFiller.full( - model = BrandsFiller.Model( - brands = ParserPathResolver.brands(categoryName), - categoryId = categoryId, - categoryName = categoryName - ) - ) - } - } -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/IfrSignalFiller.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/IfrSignalFiller.kt deleted file mode 100644 index ebe18c1..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/IfrSignalFiller.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.presentation.filler - -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import com.flipperdevices.ifrmvp.parser.model.RawIfrRemote -import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver -import com.flipperdevices.infrared.editor.model.InfraredRemote -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope - -internal class IfrSignalFiller( - private val signalTableApi: SignalTableApi, - private val orderFiller: OrderFiller -) { - - suspend fun fill(model: Model) = coroutineScope { - with(model) { - val orders = ParserPathResolver.controllerOrders( - category = categoryName, - brand = brandName, - controller = ifrFolderName - ) - remotes.map { remote -> - val parsed = remote as? InfraredRemote.Parsed - val raw = remote as? InfraredRemote.Raw - async { - val rawRemote = RawIfrRemote( - name = remote.name, - type = remote.type, - protocol = parsed?.protocol, - address = parsed?.address, - command = parsed?.command, - frequency = raw?.frequency, - dutyCycle = raw?.dutyCycle, - data = raw?.data - ) - val ifrSignalId = signalTableApi.addSignal( - categoryId = categoryId, - brandId = brandId, - irFileId = ifrFileId, - remote = rawRemote, - ) - orderFiller.fill( - model = OrderFiller.Model( - orderModels = orders, - remote = rawRemote, - ifrSignalId = ifrSignalId, - categoryId = categoryId, - brandId = brandId, - ifrFileId = ifrFileId - ) - ) - } - }.awaitAll() - } - } - - data class Model( - val ifrFileId: Long, - val categoryId: Long, - val brandId: Long, - val remotes: List, - val categoryName: String, - val brandName: String, - val ifrFolderName: String - ) -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/IrFilesFiller.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/IrFilesFiller.kt deleted file mode 100644 index bc27afa..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/IrFilesFiller.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.presentation.filler - -import com.flipperdevices.bridge.dao.api.model.FlipperFileFormat -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import com.flipperdevices.infrared.editor.viewmodel.InfraredKeyParser -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope -import java.io.File - -internal class IrFilesFiller( - private val signalTableApi: SignalTableApi, - private val ifrSignalFiller: IfrSignalFiller, - private val uiFileFiller: UiPresetFiller -) { - suspend fun fill(model: Model) = coroutineScope { - with(model) { - irFiles.map { irFile -> - - async { - val irFileId = signalTableApi.addIrFile( - fileName = irFile.name, - categoryId = categoryId, - brandId = brandId - ) - irFile.parentFile.listFiles() - .orEmpty() - .filter { file -> file.name.endsWith(".ui.json") } - .onEach { file -> - uiFileFiller.fill( - UiPresetFiller.Model( - categoryId = categoryId, - brandId = brandId, - ifrFileId = irFileId, - uiFileName = file.name - ) - ) - } - val signals = irFile.readText() - .let(FlipperFileFormat::fromFileContent) - .let(InfraredKeyParser::mapParsedKeyToInfraredRemotes) - ifrSignalFiller.fill( - model = IfrSignalFiller.Model( - ifrFileId = irFileId, - categoryId = categoryId, - brandId = brandId, - remotes = signals, - categoryName = categoryName, - brandName = brandName, - ifrFolderName = irFile.parentFile.name - ) - ) - } - }.awaitAll() - } - } - - data class Model( - val categoryId: Long, - val brandId: Long, - val irFiles: List, - val categoryName: String, - val brandName: String - ) -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/MetaFiller.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/MetaFiller.kt deleted file mode 100644 index fb10ebe..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/MetaFiller.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.presentation.filler - -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver - -internal class MetaFiller(private val signalTableApi: SignalTableApi) { - - suspend fun fill(model: Model) = with(model) { - signalTableApi.addCategoryMeta( - categoryId = categoryId, - meta = ParserPathResolver.categoryMeta(category = categoryName) - ) - } - - data class Model( - - val categoryId: Long, - val categoryName: String - ) -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/OrderFiller.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/OrderFiller.kt deleted file mode 100644 index f59eacb..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/OrderFiller.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.presentation.filler - -import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier -import com.flipperdevices.ifrmvp.model.buttondata.SingleKeyButtonData -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import com.flipperdevices.ifrmvp.parser.model.OrderModel -import com.flipperdevices.ifrmvp.parser.model.RawIfrRemote - -internal class OrderFiller(private val signalTableApi: SignalTableApi) { - - private fun isEquals(rawIfrRemote: RawIfrRemote, orderModel: OrderModel): Boolean { - val keyIdentifier = when (orderModel.data) { - is SingleKeyButtonData -> orderModel.data.keyIdentifier - else -> error("Type ${orderModel.data::class} is not supported") - } - return when (keyIdentifier) { - is IfrKeyIdentifier.Name -> { - rawIfrRemote.name == keyIdentifier.name - } - - is IfrKeyIdentifier.Sha256 -> error("Comparison by SHA not supported") - } - } - - suspend fun fill(model: Model) = with(model) { - orderModels - .filter { orderModel -> isEquals(model.remote, orderModel) } - .forEachIndexed { i, orderModel -> - signalTableApi.addOrderModel( - orderModel = orderModel, - ifrSignalId = ifrSignalId, - categoryId = categoryId, - brandId = brandId, - ifrFileId = ifrFileId, - order = i - ) - } - } - - data class Model( - val orderModels: List, - val remote: RawIfrRemote, - val ifrSignalId: Long, - val categoryId: Long, - val brandId: Long, - val ifrFileId: Long - ) -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/UiPresetFiller.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/UiPresetFiller.kt deleted file mode 100644 index fc8debd..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/presentation/filler/UiPresetFiller.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.presentation.filler - -import com.flipperdevices.ifrmvp.parser.api.SignalTableApi -import kotlinx.coroutines.coroutineScope - -internal class UiPresetFiller( - private val signalTableApi: SignalTableApi, -) { - suspend fun fill(model: Model) = coroutineScope { - with(model) { - signalTableApi.addUiPreset( - categoryId = categoryId, - brandId = brandId, - irFileId = ifrFileId, - fileName = uiFileName - ) - } - } - - data class Model( - val categoryId: Long, - val brandId: Long, - val ifrFileId: Long, - val uiFileName: String - ) -} diff --git a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/IfrRemoteExt.kt b/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/IfrRemoteExt.kt deleted file mode 100644 index b8c6e21..0000000 --- a/modules/parser/src/main/kotlin/com/flipperdevices/ifrmvp/parser/util/IfrRemoteExt.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.flipperdevices.ifrmvp.parser.util - -import com.flipperdevices.ifrmvp.parser.model.RawIfrRemote -import java.security.MessageDigest - -internal object IfrRemoteExt { - @OptIn(ExperimentalStdlibApi::class) - val RawIfrRemote.md5: String - get() { - val dataByteArray = listOfNotNull( - type.toByteArray(), - protocol?.toByteArray(), - address?.toByteArray(), - command?.toByteArray(), - frequency?.toByteArray(), - dutyCycle?.toByteArray(), - data?.toByteArray() - ).flatMap(ByteArray::asList).toByteArray() - - return MessageDigest.getInstance("MD5") - .digest(dataByteArray) - .toHexString() - } -} diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorType.kt b/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorType.kt deleted file mode 100644 index 9ec261a..0000000 --- a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/ErrorType.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.model - -enum class ErrorType { - UNHANDLED, CATEGORY_NOT_FOUND, BRAND_NOT_FOUND, NO_SIGNAL -} diff --git a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalModel.kt b/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalModel.kt deleted file mode 100644 index 6c14c6d..0000000 --- a/modules/shared-backend-model/src/commonMain/kotlin/com/flipperdevices/ifrmvp/backend/model/SignalModel.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class SignalModel( - @SerialName("id") - val id: Long, - @SerialName("ifr_file_id") - val irFileId: Long, - @SerialName("brand_id") - val brandId: Long, - @SerialName("category_id") - val categoryId: Long, - @SerialName("name") - val name: String, - @SerialName("type") - val type: String, - @SerialName("protocol") - val protocol: String? = null, - @SerialName("address") - val address: String? = null, - @SerialName("command") - val command: String? = null, - @SerialName("frequency") - val frequency: String? = null, - @SerialName("duty_cycle") - val dutyCycle: String? = null, - @SerialName("data") - val data: String? = null, - @SerialName("hash") - val hash: String -) diff --git a/modules/shared-ui-model/build.gradle.kts b/modules/shared-ui-model/build.gradle.kts deleted file mode 100644 index 9922e22..0000000 --- a/modules/shared-ui-model/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -import ru.astrainteractive.gradleplugin.property.extension.ModelPropertyValueExt.hierarchyGroup - -plugins { - alias(libs.plugins.multiplatform) - alias(libs.plugins.kotlin.serialization) - alias(libs.plugins.gradle.android.library) - alias(libs.plugins.klibs.gradle.android.core) -} - -kotlin { - jvm() - androidTarget() - iosX64() - iosArm64() - iosSimulatorArm64() - - sourceSets { - commonMain.dependencies { - implementation(libs.kotlin.serialization.json) - } - } -} - -android.namespace = hierarchyGroup diff --git a/settings.gradle.kts b/settings.gradle.kts index 5908112..733bf5b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,13 +19,17 @@ dependencyResolutionManagement { enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") rootProject.name = "backend" -// Services -include(":modules:shared-backend-model") -include(":modules:shared-ui-model") -include(":modules:core") -include(":modules:api-status") +// database +include(":modules:database") +// core include(":modules:build-konfig") -include(":modules:parser") -include(":modules:ui-generator") -// Master +include(":modules:core") +include(":modules:model") +include(":modules:infrared") +// generators +include(":modules:kenerator:configuration") +include(":modules:kenerator:sql") +include(":modules:kenerator:ui") +include(":modules:kenerator:paths") +// application include("web-api") diff --git a/web-api/build.gradle.kts b/web-api/build.gradle.kts index 4b9c5a3..7514b0b 100644 --- a/web-api/build.gradle.kts +++ b/web-api/build.gradle.kts @@ -39,12 +39,14 @@ dependencies { implementation(libs.exposed.dao) // Services implementation(projects.modules.buildKonfig) - implementation(projects.modules.sharedUiModel) - implementation(projects.modules.sharedBackendModel) + implementation(projects.modules.model) implementation(projects.modules.core) - implementation(projects.modules.apiStatus) - implementation(projects.modules.parser) - implementation(projects.modules.uiGenerator) + implementation(projects.modules.database) + implementation(projects.modules.infrared) + implementation(projects.modules.kenerator.ui) + implementation(projects.modules.kenerator.sql) + implementation(projects.modules.kenerator.configuration) + implementation(projects.modules.kenerator.paths) } application { diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/Application.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/Application.kt index ba4a472..f97ff96 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/Application.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/Application.kt @@ -22,7 +22,8 @@ internal fun Application.module(rootModule: RootModule, logger: Logger) { rootModule.brandsModule.registry, rootModule.signalModule.registry, rootModule.keyModule.registry, - rootModule.uiModule.registry + rootModule.uiModule.registry, + rootModule.configGenModule.registry ).forEach { routeRegistry -> routeRegistry.register(this) } } logger.info("Started!") diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/di/RootModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/di/RootModule.kt index e6570c0..e13b1d9 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/di/RootModule.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/di/RootModule.kt @@ -5,6 +5,7 @@ import com.flipperdevices.ifrmvp.backend.db.signal.di.SignalApiModule import com.flipperdevices.ifrmvp.backend.envkonfig.EnvKonfig import com.flipperdevices.ifrmvp.backend.route.brands.di.BrandsModule import com.flipperdevices.ifrmvp.backend.route.categories.di.CategoriesModule +import com.flipperdevices.ifrmvp.backend.route.configgen.di.ConfigGenModule import com.flipperdevices.ifrmvp.backend.route.key.di.KeyModule import com.flipperdevices.ifrmvp.backend.route.signal.di.SignalModule import com.flipperdevices.ifrmvp.backend.route.ui.di.UiModule @@ -20,6 +21,7 @@ interface RootModule { val signalModule: SignalModule val keyModule: KeyModule val uiModule: UiModule + val configGenModule: ConfigGenModule class Default : RootModule { override val coreModule by lazy { @@ -50,5 +52,11 @@ interface RootModule { keyModule = keyModule ) } + override val configGenModule: ConfigGenModule by lazy { + ConfigGenModule.Default( + keyModule = keyModule, + apiModule = signalApiModule + ) + } } } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/plugins/StatusPages.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/plugins/StatusPages.kt index 3938482..cc71d0d 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/plugins/StatusPages.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/plugins/StatusPages.kt @@ -1,6 +1,7 @@ package com.flipperdevices.ifrmvp.backend.plugins import com.flipperdevices.ifrmvp.backend.buildkonfig.BuildKonfig +import com.flipperdevices.ifrmvp.backend.db.signal.exception.TableDaoException import com.flipperdevices.ifrmvp.backend.model.ErrorResponseModel import com.flipperdevices.ifrmvp.backend.model.ErrorType import io.ktor.http.HttpStatusCode @@ -13,6 +14,18 @@ import io.ktor.util.logging.KtorSimpleLogger fun Application.configureStatusPages() { val logger = KtorSimpleLogger(BuildKonfig.GROUP) install(StatusPages) { + exception { call, e -> + val errorType = when (e) { + is TableDaoException.BrandNotFound -> ErrorType.BRAND_NOT_FOUND + is TableDaoException.CategoryMeta -> ErrorType.CATEGORY_META_NOT_FOUND + is TableDaoException.CategoryNotFound -> ErrorType.CATEGORY_NOT_FOUND + is TableDaoException.IrFileNotFound -> ErrorType.IR_FILE_NOT_FOUND + } + call.respond( + status = HttpStatusCode.InternalServerError, + message = ErrorResponseModel(errorType), + ) + } unhandled { call -> logger.error("Unhandled exception: ${call.response}; ${call.request}") call.respond( diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/plugins/Swagger.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/plugins/Swagger.kt index 9f849b3..83564d6 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/plugins/Swagger.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/plugins/Swagger.kt @@ -35,7 +35,8 @@ fun Application.configureSwagger() { generator = { type -> type // process type using kotlinx-serialization instead of reflection - // requires additional dependency "io.github.smiley4:schema-kenerator-kotlinx-serialization:" + // requires additional dependency + // "io.github.smiley4:schema-kenerator-kotlinx-serialization:" // see https://github.com/SMILEY4/schema-kenerator for more information .processKotlinxSerialization() .generateSwaggerSchema() diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/data/BrandsRepositoryImpl.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/data/BrandsRepositoryImpl.kt index 61a63c5..b3a398a 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/data/BrandsRepositoryImpl.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/data/BrandsRepositoryImpl.kt @@ -1,21 +1,26 @@ package com.flipperdevices.ifrmvp.backend.route.brands.data +import com.flipperdevices.ifrmvp.backend.db.signal.dao.TableDao import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable import com.flipperdevices.ifrmvp.backend.model.BrandModel import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction -internal class BrandsRepositoryImpl(private val database: Database) : BrandsRepository { +internal class BrandsRepositoryImpl( + private val database: Database, + private val tableDao: TableDao +) : BrandsRepository { override suspend fun getBrands(categoryId: Long): List { + check(tableDao.getCategoryById(categoryId).id == categoryId) return transaction(database) { BrandTable.selectAll() - .where { BrandTable.category eq categoryId } + .where { BrandTable.categoryId eq categoryId } .map { BrandModel( id = it[BrandTable.id].value, - name = it[BrandTable.displayName], - categoryId = it[BrandTable.category].value, + folderName = it[BrandTable.folderName], + categoryId = it[BrandTable.categoryId].value, ) } } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/di/BrandsModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/di/BrandsModule.kt index ad25080..372bee7 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/di/BrandsModule.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/di/BrandsModule.kt @@ -12,7 +12,8 @@ interface BrandsModule { override val registry: RouteRegistry by lazy { BrandsRouteRegistry( brandsRepository = BrandsRepositoryImpl( - database = signalApiModule.database + database = signalApiModule.database, + tableDao = signalApiModule.tableDao ) ) } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/presentation/BrandsRouteRegistry.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/presentation/BrandsRouteRegistry.kt index 226942b..87f97ed 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/presentation/BrandsRouteRegistry.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/brands/presentation/BrandsRouteRegistry.kt @@ -6,7 +6,6 @@ import com.flipperdevices.ifrmvp.backend.route.brands.data.BrandsRepository import io.github.smiley4.ktorswaggerui.dsl.routing.get import io.ktor.server.response.respond import io.ktor.server.routing.Routing -import io.ktor.server.routing.get internal class BrandsRouteRegistry( private val brandsRepository: BrandsRepository @@ -20,10 +19,10 @@ internal class BrandsRouteRegistry( val categoryId = context.request .queryParameters["category_id"] ?.toLongOrNull() - ?: 0 + ?: -1 val brandsResponse = BrandsResponse( brands = brandsRepository.getBrands(categoryId) - .sortedBy { brandModel -> brandModel.name.first() } + .sortedBy { brandModel -> brandModel.folderName.lowercase() } ) context.respond(brandsResponse) } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/categories/data/CategoriesRepositoryImpl.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/categories/data/CategoriesRepositoryImpl.kt index a5ef21b..8d3dbdc 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/categories/data/CategoriesRepositoryImpl.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/categories/data/CategoriesRepositoryImpl.kt @@ -17,6 +17,7 @@ internal class CategoriesRepositoryImpl(private val database: Database) : Catego val categoryId = resultRow[CategoryTable.id].value DeviceCategory( id = categoryId, + folderName = resultRow[CategoryTable.folderName], meta = CategoryMetaTable.selectAll() .where { CategoryMetaTable.id eq categoryId } .map { metaResultRow -> diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/configgen/di/ConfigGenModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/configgen/di/ConfigGenModule.kt new file mode 100644 index 0000000..1441d8f --- /dev/null +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/configgen/di/ConfigGenModule.kt @@ -0,0 +1,22 @@ +package com.flipperdevices.ifrmvp.backend.route.configgen.di + +import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry +import com.flipperdevices.ifrmvp.backend.db.signal.di.SignalApiModule +import com.flipperdevices.ifrmvp.backend.route.configgen.presentation.ConfigGenRouteRegistry +import com.flipperdevices.ifrmvp.backend.route.key.di.KeyModule + +interface ConfigGenModule { + val registry: RouteRegistry + + class Default( + keyModule: KeyModule, + apiModule: SignalApiModule + ) : ConfigGenModule { + override val registry: RouteRegistry by lazy { + ConfigGenRouteRegistry( + keyRouteRepository = keyModule.keyRouteRepository, + tableDao = apiModule.tableDao + ) + } + } +} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/configgen/presentation/ConfigGenRouteRegistry.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/configgen/presentation/ConfigGenRouteRegistry.kt new file mode 100644 index 0000000..6aa0a0a --- /dev/null +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/configgen/presentation/ConfigGenRouteRegistry.kt @@ -0,0 +1,53 @@ +package com.flipperdevices.ifrmvp.backend.route.configgen.presentation + +import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry +import com.flipperdevices.ifrmvp.backend.db.signal.dao.TableDao +import com.flipperdevices.ifrmvp.backend.model.CategoryType +import com.flipperdevices.ifrmvp.backend.route.key.data.KeyRouteRepository +import com.flipperdevices.ifrmvp.backend.route.key.presentation.KeySwagger +import com.flipperdevices.ifrmvp.generator.config.category.api.AllCategoryConfigGenerator +import com.flipperdevices.ifrmvp.generator.config.device.api.DefaultDeviceConfigGenerator +import com.flipperdevices.ifrmvp.generator.config.device.api.any.AnyDeviceKeyNamesProvider +import io.github.smiley4.ktorswaggerui.dsl.routing.get +import io.ktor.server.response.respond +import io.ktor.server.routing.Routing + +internal class ConfigGenRouteRegistry( + private val keyRouteRepository: KeyRouteRepository, + private val tableDao: TableDao +) : RouteRegistry { + private fun Routing.deviceConfigRoute() { + get( + path = "configuration/device", + builder = { with(KeySwagger) { createSwaggerDefinition() } }, + body = { + val ifrFileId = context.parameters["ifr_file_id"]?.toLongOrNull() ?: -1 + val file = keyRouteRepository.getIfrFile(ifrFileId) + if (!file.exists()) error("Ifr file doesn't exists! ${file.absolutePath}") + context.respond(DefaultDeviceConfigGenerator(AnyDeviceKeyNamesProvider).generate(file)) + } + ) + } + + private fun Routing.categoryConfigRoute() { + get( + path = "configuration/category", + builder = { with(KeySwagger) { createSwaggerDefinition() } }, + body = { + val categoryId = context.parameters["category_id"]?.toLongOrNull() ?: -1 + val category = tableDao.getCategoryById(categoryId) + val categoryType = CategoryType + .entries + .firstOrNull { entry -> entry.folderName == category.folderName } + ?: error("Category with fodlerName ${category.folderName} not found!") + val configuration = AllCategoryConfigGenerator.generate(categoryType) + context.respond(configuration) + } + ) + } + + override fun register(routing: Routing) { + routing.deviceConfigRoute() + routing.categoryConfigRoute() + } +} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepository.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepository.kt index f76e656..bf94362 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepository.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepository.kt @@ -1,55 +1,7 @@ package com.flipperdevices.ifrmvp.backend.route.key.data -import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.IfrFileTable -import com.flipperdevices.ifrmvp.backend.model.IfrFileModel -import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction import java.io.File interface KeyRouteRepository { suspend fun getIfrFile(ifrFileId: Long): File } - -class KeyRouteRepositoryImpl(private val database: Database) : KeyRouteRepository { - override suspend fun getIfrFile(ifrFileId: Long): File { - val ifrFileModel = transaction(database) { - IfrFileTable.selectAll() - .where { IfrFileTable.id eq ifrFileId } - .map { - IfrFileModel( - id = it[IfrFileTable.id].value, - categoryId = it[IfrFileTable.categoryId].value, - brandId = it[IfrFileTable.brandId].value, - fileName = it[IfrFileTable.fileName] - ) - } - .firstOrNull() - ?: error("Ir file with id $ifrFileId not found!") - } - val categoryFolderName = transaction(database) { - CategoryTable.select(CategoryTable.folderName) - .where { CategoryTable.id eq ifrFileModel.categoryId } - .map { it[CategoryTable.folderName] } - .firstOrNull() - ?: error("Category with id ${ifrFileModel.categoryId} not found!") - } - val brandFolderName = transaction(database) { - BrandTable.select(BrandTable.displayName) - .where { BrandTable.id eq ifrFileModel.brandId } - .map { it[BrandTable.displayName] } - .firstOrNull() - ?: error("Brand with id ${ifrFileModel.brandId} not found!") - } - println("Category :$categoryFolderName brand: $brandFolderName ifr: ${ifrFileModel.fileName}") - val file = ParserPathResolver.ifrFile( - category = categoryFolderName, - brand = brandFolderName, - ifrFolderName = ifrFileModel.fileName.replaceFirst(".ir", "") // todo - ) - return file - } -} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepositoryImpl.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepositoryImpl.kt new file mode 100644 index 0000000..3e244d1 --- /dev/null +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepositoryImpl.kt @@ -0,0 +1,24 @@ +package com.flipperdevices.ifrmvp.backend.route.key.data + +import com.flipperdevices.ifrmvp.backend.db.signal.dao.TableDao +import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver +import java.io.File + +class KeyRouteRepositoryImpl( + private val tableDao: TableDao +) : KeyRouteRepository { + override suspend fun getIfrFile(ifrFileId: Long): File { + val ifrFileModel = tableDao.ifrFileById(ifrFileId) + val brandModel = tableDao.getBrandById(ifrFileModel.brandId) + val categoryModel = tableDao.getCategoryById(brandModel.categoryId) + + val categoryFolderName = categoryModel.folderName + val brandFolderName = brandModel.folderName + val file = ParserPathResolver.ifrFile( + category = categoryFolderName, + brand = brandFolderName, + ifrFolderName = ifrFileModel.folderName + ) + return file + } +} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/di/KeyModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/di/KeyModule.kt index 26314f0..098ce42 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/di/KeyModule.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/di/KeyModule.kt @@ -11,7 +11,9 @@ interface KeyModule { val keyRouteRepository: KeyRouteRepository class Default(signalApiModule: SignalApiModule) : KeyModule { - override val keyRouteRepository: KeyRouteRepository = KeyRouteRepositoryImpl(signalApiModule.database) + override val keyRouteRepository = KeyRouteRepositoryImpl( + tableDao = signalApiModule.tableDao + ) override val registry: RouteRegistry by lazy { KeyRouteRegistry( keyRouteRepository, diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/presentation/KeySwagger.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/presentation/KeySwagger.kt index a83b7b4..dadb250 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/presentation/KeySwagger.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/presentation/KeySwagger.kt @@ -6,16 +6,16 @@ import io.ktor.http.HttpStatusCode internal object KeySwagger { fun OpenApiRoute.createSwaggerDefinition() { - description = "Get json content of UI file" + description = "Get content of .IR file" request { queryParameter("ifr_file_id") { - description = "Unique id of ifr file" + description = "Unique id of .ir file" required = true } } response { HttpStatusCode.OK to { - description = "Content of IR" + description = "Content of IR file" body { description = "Content of IR file" } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/CounterRepository.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/CounterRepository.kt deleted file mode 100644 index d62b13f..0000000 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/CounterRepository.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.route.signal.data - -import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalOrderTable - -internal class CounterRepository { - fun countOrders(ifrFileId: Long): Long { - return SignalOrderTable - .select(SignalOrderTable.id) - .where { SignalOrderTable.ifrFileId eq ifrFileId } - .count() - } -} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/DefaultSignalRepository.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/DefaultSignalRepository.kt deleted file mode 100644 index 57daa10..0000000 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/DefaultSignalRepository.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.route.signal.data - -import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalTable -import com.flipperdevices.ifrmvp.backend.model.IfrFileModel -import com.flipperdevices.ifrmvp.backend.model.SignalModel -import com.flipperdevices.ifrmvp.backend.model.SignalRequestModel -import com.flipperdevices.ifrmvp.backend.model.SignalResponse -import com.flipperdevices.ifrmvp.backend.model.SignalResponseModel -import com.flipperdevices.ifrmvp.backend.route.signal.mapping.SignalModelMapper.toSignalModel -import com.flipperdevices.ifrmvp.model.buttondata.ButtonData -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.selectAll - -internal class DefaultSignalRepository { - - private fun getSignal( - ifrFileId: Long, - signalRequestModel: SignalRequestModel - ): SignalModel? { - val count = signalRequestModel.successResults.count { it.ifrFileId == ifrFileId } - return SignalTable - .selectAll() - .andWhere { SignalTable.ifrFileId eq ifrFileId } - .andWhere { SignalTable.id.notInList(signalRequestModel.successResults.map { it.signalId }) } - .apply { - when { - count == 0 -> andWhere { - SignalTable.name.eq("power") - .or(SignalTable.name.eq("on")) - .or(SignalTable.name.like("%power%")) - .or(SignalTable.name.like("%on%")) - } - - count >= 1 -> andWhere { - SignalTable.name.neq("power") - .or(SignalTable.name.neq("on")) - .or(SignalTable.name.notLike("%power%")) - .or(SignalTable.name.notLike("%on%")) - } - - else -> this - } - } - .limit(1) - .map { signalResultRow -> signalResultRow.toSignalModel() } - .firstOrNull() - } - - fun getDefaultSignal( - ifrFile: IfrFileModel, - signalRequestModel: SignalRequestModel, - categorySingularDisplayName: String, - successCount: Long, - ): SignalResponseModel { - val signalModel = getSignal(ifrFile.id, signalRequestModel) - return when { - signalModel == null && successCount == 0L -> { - SignalResponseModel() - } - - signalModel == null -> { - SignalResponseModel(ifrFileModel = ifrFile) - } - - else -> { - SignalResponseModel( - signalResponse = SignalResponse( - signalModel = signalModel, - data = SignalResponse.Data( - type = ButtonData.ButtonType.TEXT.name, - iconId = null, - text = signalModel.name - ), - categoryName = categorySingularDisplayName, - message = "Does $categorySingularDisplayName respond to button?" - ) - ) - } - } - } -} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/InstantCategoryConfigRepository.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/InstantCategoryConfigRepository.kt new file mode 100644 index 0000000..8d61192 --- /dev/null +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/InstantCategoryConfigRepository.kt @@ -0,0 +1,18 @@ +package com.flipperdevices.ifrmvp.backend.route.signal.data + +import com.flipperdevices.ifrmvp.backend.model.CategoryConfiguration +import com.flipperdevices.ifrmvp.backend.model.CategoryType +import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver + +interface CategoryConfigRepository { + fun getOrNull(categoryType: CategoryType, index: Int): CategoryConfiguration.OrderModel? +} + +object IRDBCategoryConfigRepository : CategoryConfigRepository { + override fun getOrNull(categoryType: CategoryType, index: Int): CategoryConfiguration.OrderModel? { + return ParserPathResolver + .categoryConfiguration(categoryType.folderName) + .orders + .getOrNull(index) + } +} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/IrFileRepository.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/IrFileRepository.kt deleted file mode 100644 index 94c5a43..0000000 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/IrFileRepository.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.route.signal.data - -import com.flipperdevices.ifrmvp.backend.db.signal.table.IfrFileTable -import com.flipperdevices.ifrmvp.backend.model.IfrFileModel -import com.flipperdevices.ifrmvp.backend.model.SignalRequestModel -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.selectAll - -internal class IrFileRepository { - - fun getNextIrFile(signalRequestModel: SignalRequestModel): IfrFileModel? { - return IfrFileTable - .selectAll() - .andWhere { IfrFileTable.id.notInList(signalRequestModel.failedResults.map { it.ifrFileId }) } - .andWhere { IfrFileTable.categoryId eq signalRequestModel.categoryId } - .andWhere { IfrFileTable.brandId eq signalRequestModel.brandId } - .limit(1) - .map { - IfrFileModel( - id = it[IfrFileTable.id].value, - categoryId = it[IfrFileTable.categoryId].value, - brandId = it[IfrFileTable.brandId].value, - fileName = it[IfrFileTable.fileName] - ) - }.firstOrNull() - } - - fun getMostSuccessfulIfrFile(signalRequestModel: SignalRequestModel): IfrFileModel? { - val id = signalRequestModel.successResults - .groupBy { it.ifrFileId } - .maxByOrNull { it.value.size } - ?.key - return IfrFileTable - .selectAll() - .where { IfrFileTable.id eq id } - .map { - IfrFileModel( - id = it[IfrFileTable.id].value, - categoryId = it[IfrFileTable.categoryId].value, - brandId = it[IfrFileTable.brandId].value, - fileName = it[IfrFileTable.fileName] - ) - }.firstOrNull() - } -} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/SignalByOrderRepository.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/SignalByOrderRepository.kt deleted file mode 100644 index 45d9ef5..0000000 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/data/SignalByOrderRepository.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.route.signal.data - -import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalOrderTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalTable -import com.flipperdevices.ifrmvp.backend.model.IfrFileModel -import com.flipperdevices.ifrmvp.backend.model.SignalRequestModel -import com.flipperdevices.ifrmvp.backend.model.SignalResponse -import com.flipperdevices.ifrmvp.backend.model.SignalResponseModel -import com.flipperdevices.ifrmvp.backend.route.signal.mapping.SignalModelMapper.toSignalModel -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.selectAll - -internal class SignalByOrderRepository { - - fun getSignalByOrder( - signalRequestModel: SignalRequestModel, - ifrFile: IfrFileModel, - orderCount: Long, - successCount: Long, - categorySingularDisplayName: String - ): SignalResponseModel { - if (successCount == orderCount) { - return SignalResponseModel(ifrFileModel = ifrFile) - } - println( - "SignalOrderTable size: ${ - SignalOrderTable.select( - SignalOrderTable.id - ).where { SignalOrderTable.ifrFileId eq ifrFile.id }.count() - }" - ) - val signalResponse = SignalOrderTable - .selectAll() - .orderBy(SignalOrderTable.order, SortOrder.ASC) - .where { SignalOrderTable.ifrFileId eq ifrFile.id } - .andWhere { - SignalOrderTable.ifrSignalId.notInList(signalRequestModel.successResults.map { it.signalId }) - } - .map { signalOrderResultRow -> - val signalId = signalOrderResultRow[SignalOrderTable.ifrSignalId].value - SignalResponse( - data = SignalResponse.Data( - type = signalOrderResultRow[SignalOrderTable.dataType], - iconId = signalOrderResultRow[SignalOrderTable.dataIconId], - text = signalOrderResultRow[SignalOrderTable.dataText], - ), - signalModel = SignalTable - .selectAll() - .where { SignalTable.id eq signalId } - .map { signalResultRow -> signalResultRow.toSignalModel() } - .first(), - message = signalOrderResultRow[SignalOrderTable.message].format(categorySingularDisplayName), - categoryName = categorySingularDisplayName - ) - }.firstOrNull() - // All signals passed - if (signalResponse == null) { - return SignalResponseModel(ifrFileModel = ifrFile) - } - return SignalResponseModel(signalResponse = signalResponse) - } -} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/di/SignalModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/di/SignalModule.kt index bb54d72..38327f2 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/di/SignalModule.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/di/SignalModule.kt @@ -2,10 +2,6 @@ package com.flipperdevices.ifrmvp.backend.route.signal.di import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry import com.flipperdevices.ifrmvp.backend.db.signal.di.SignalApiModule -import com.flipperdevices.ifrmvp.backend.route.signal.data.CounterRepository -import com.flipperdevices.ifrmvp.backend.route.signal.data.DefaultSignalRepository -import com.flipperdevices.ifrmvp.backend.route.signal.data.IrFileRepository -import com.flipperdevices.ifrmvp.backend.route.signal.data.SignalByOrderRepository import com.flipperdevices.ifrmvp.backend.route.signal.presentation.SignalRouteRegistry interface SignalModule { @@ -14,11 +10,8 @@ interface SignalModule { class Default(signalApiModule: SignalApiModule) : SignalModule { override val registry: RouteRegistry by lazy { SignalRouteRegistry( - signalApiModule.database, - irFileRepository = IrFileRepository(), - counterRepository = CounterRepository(), - defaultSignalRepository = DefaultSignalRepository(), - signalByOrderRepository = SignalByOrderRepository() + database = signalApiModule.database, + tableDao = signalApiModule.tableDao ) } } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/mapping/SignalModelMapper.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/mapping/SignalModelMapper.kt deleted file mode 100644 index e058aac..0000000 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/mapping/SignalModelMapper.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.flipperdevices.ifrmvp.backend.route.signal.mapping - -import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalTable -import com.flipperdevices.ifrmvp.backend.model.SignalModel -import org.jetbrains.exposed.sql.ResultRow - -internal object SignalModelMapper { - fun ResultRow.toSignalModel(): SignalModel { - val signalResultRow = this - return SignalModel( - id = signalResultRow[SignalTable.id].value, - irFileId = signalResultRow[SignalTable.ifrFileId].value, - brandId = signalResultRow[SignalTable.brandId].value, - categoryId = signalResultRow[SignalTable.categoryId].value, - name = signalResultRow[SignalTable.name], - type = signalResultRow[SignalTable.type], - protocol = signalResultRow[SignalTable.protocol], - address = signalResultRow[SignalTable.address], - command = signalResultRow[SignalTable.command], - frequency = signalResultRow[SignalTable.frequency], - dutyCycle = signalResultRow[SignalTable.dutyCycle], - data = signalResultRow[SignalTable.data], - hash = signalResultRow[SignalTable.hash] - ) - } -} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/presentation/SignalRouteRegistry.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/presentation/SignalRouteRegistry.kt index 9102777..08e9ed4 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/presentation/SignalRouteRegistry.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/signal/presentation/SignalRouteRegistry.kt @@ -1,89 +1,220 @@ package com.flipperdevices.ifrmvp.backend.route.signal.presentation import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry -import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryMetaTable +import com.flipperdevices.ifrmvp.backend.db.signal.dao.TableDao +import com.flipperdevices.ifrmvp.backend.db.signal.exception.TableDaoException +import com.flipperdevices.ifrmvp.backend.db.signal.table.InfraredFileTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.InfraredFileToSignalTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalKeyTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.SignalTable +import com.flipperdevices.ifrmvp.backend.model.BrandModel +import com.flipperdevices.ifrmvp.backend.model.CategoryConfiguration +import com.flipperdevices.ifrmvp.backend.model.CategoryType +import com.flipperdevices.ifrmvp.backend.model.DeviceCategory +import com.flipperdevices.ifrmvp.backend.model.SignalModel import com.flipperdevices.ifrmvp.backend.model.SignalRequestModel +import com.flipperdevices.ifrmvp.backend.model.SignalResponse import com.flipperdevices.ifrmvp.backend.model.SignalResponseModel -import com.flipperdevices.ifrmvp.backend.route.signal.data.CounterRepository -import com.flipperdevices.ifrmvp.backend.route.signal.data.DefaultSignalRepository -import com.flipperdevices.ifrmvp.backend.route.signal.data.IrFileRepository -import com.flipperdevices.ifrmvp.backend.route.signal.data.SignalByOrderRepository +import com.flipperdevices.ifrmvp.backend.route.signal.data.CategoryConfigRepository +import com.flipperdevices.ifrmvp.backend.route.signal.data.IRDBCategoryConfigRepository +import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier +import com.flipperdevices.ifrmvp.model.buttondata.SingleKeyButtonData import io.github.smiley4.ktorswaggerui.dsl.routing.post +import io.ktor.http.HttpStatusCode import io.ktor.server.request.receive import io.ktor.server.response.respond import io.ktor.server.routing.Routing import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.JoinType +import org.jetbrains.exposed.sql.Query +import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.sql.andWhere +import org.jetbrains.exposed.sql.count +import org.jetbrains.exposed.sql.or +import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.wrapAsExpression +@Suppress("UnusedPrivateProperty") internal class SignalRouteRegistry( private val database: Database, - private val irFileRepository: IrFileRepository, - private val counterRepository: CounterRepository, - private val defaultSignalRepository: DefaultSignalRepository, - private val signalByOrderRepository: SignalByOrderRepository + private val tableDao: TableDao, + private val categoryConfigRepository: CategoryConfigRepository = IRDBCategoryConfigRepository, ) : RouteRegistry { - private fun getCategorySingularDisplayName(categoryId: Long): String { - return CategoryMetaTable - .select(CategoryMetaTable.singularDisplayName) - .where { CategoryMetaTable.categoryId eq categoryId } - .limit(1) - .map { it[CategoryMetaTable.singularDisplayName] } - .firstOrNull() - ?: error("Could not find category $categoryId") + // Keep only those files, in which signals have been successfully passed + private fun getIncludedFileIds(signalRequestModel: SignalRequestModel): Query { + return transaction(database) { + InfraredFileToSignalTable + .select(InfraredFileToSignalTable.infraredFileId) + .groupBy(InfraredFileToSignalTable.infraredFileId) + .apply { + val successSignalIds = signalRequestModel + .successResults + .map(SignalRequestModel.SignalResultData::signalId) + if (successSignalIds.isNotEmpty()) { + where { + InfraredFileToSignalTable + .signalId + .inList(successSignalIds) + } + } else { + this + } + } + } } - private fun getSignal(signalRequestModel: SignalRequestModel): SignalResponseModel { + private suspend fun getSignalModel( + signalRequestModel: SignalRequestModel, + order: CategoryConfiguration.OrderModel, + brand: BrandModel + ): SignalModel? { return transaction(database) { - // Looking for ifr file id with non-failed results - // If file not found then we don't have signals - val ifrFile = irFileRepository.getNextIrFile(signalRequestModel) - if (ifrFile == null) { - val mostSuccessIfrFile = irFileRepository.getMostSuccessfulIfrFile(signalRequestModel) - return@transaction SignalResponseModel(ifrFileModel = mostSuccessIfrFile) - } - - val orderCount = counterRepository.countOrders(ifrFile.id) - - val successCount = signalRequestModel - .successResults - .count { it.ifrFileId == ifrFile.id } - .toLong() - - val categorySingularDisplayName = getCategorySingularDisplayName(signalRequestModel.categoryId) + SignalTable + .join( + otherTable = InfraredFileToSignalTable, + joinType = JoinType.LEFT, + onColumn = SignalTable.id, + otherColumn = InfraredFileToSignalTable.signalId + ) + .join( + otherTable = InfraredFileTable, + joinType = JoinType.LEFT, + onColumn = InfraredFileToSignalTable.infraredFileId, + otherColumn = InfraredFileTable.id + ) + .join( + otherTable = SignalKeyTable, + joinType = JoinType.LEFT, + onColumn = SignalTable.id, + otherColumn = SignalKeyTable.signalId + ) + .selectAll() + .where { SignalTable.brandId eq brand.id } + .apply { + val singleButtonData = order.data as? SingleKeyButtonData + when (val identifier = singleButtonData?.keyIdentifier) { + is IfrKeyIdentifier.Sha256 -> { + andWhere { + SignalKeyTable.deviceKey.eq(order.key) + .or { SignalKeyTable.hash.eq(identifier.hash) } + } + } - println("Success count: $successCount; Orders count: $orderCount") - println("IfrFileId: ${ifrFile.id} category: ${ifrFile.categoryId} brand: ${ifrFile.brandId}") - - return@transaction when { - orderCount == 1L && successCount >= 1 -> { - SignalResponseModel(ifrFileModel = ifrFile) + else -> andWhere { SignalKeyTable.deviceKey.eq(order.key) } + } } - - orderCount == 0L && successCount >= MAX_SUCCESS_ROW -> { - SignalResponseModel(ifrFileModel = ifrFile) + .andWhere { SignalKeyTable.deviceKey eq order.key } + .andWhere { SignalKeyTable.signalId eq SignalTable.id } + .apply { + val failedSignalIds = signalRequestModel.failedResults + .map(SignalRequestModel.SignalResultData::signalId) + if (failedSignalIds.isEmpty()) { + this + } else { + andWhere { SignalTable.id.notInList(failedSignalIds) } + } } - - orderCount == 0L -> { - defaultSignalRepository.getDefaultSignal( - ifrFile = ifrFile, - signalRequestModel = signalRequestModel, - categorySingularDisplayName = categorySingularDisplayName, - successCount = successCount - ) + .apply { + val successfulSignalIds = signalRequestModel.successResults + .map(SignalRequestModel.SignalResultData::signalId) + if (successfulSignalIds.isEmpty()) { + this + } else { + andWhere { SignalTable.id.notInList(successfulSignalIds) } + } } - - else -> { - signalByOrderRepository.getSignalByOrder( - signalRequestModel = signalRequestModel, - ifrFile = ifrFile, - orderCount = orderCount, - successCount = successCount, - categorySingularDisplayName = categorySingularDisplayName, + .apply { + val successfulSignalIds = signalRequestModel.successResults + .map(SignalRequestModel.SignalResultData::signalId) + if (successfulSignalIds.isEmpty()) { + this + } else { + andWhere { + InfraredFileToSignalTable.infraredFileId inSubQuery InfraredFileToSignalTable + .select(InfraredFileToSignalTable.infraredFileId) + .where { InfraredFileToSignalTable.signalId inList successfulSignalIds } + } + } + } + .orderBy( + wrapAsExpression( + InfraredFileToSignalTable + .select(InfraredFileToSignalTable.signalId.count()) + .where { InfraredFileToSignalTable.signalId eq SignalTable.id } + ) to SortOrder.DESC + ) + .limit(1) + .map { + SignalModel( + id = it[SignalTable.id].value, + remote = SignalModel.FlipperRemote( + name = it[SignalTable.name], + type = it[SignalTable.type], + protocol = it[SignalTable.protocol], + address = it[SignalTable.address], + command = it[SignalTable.command], + frequency = it[SignalTable.frequency], + dutyCycle = it[SignalTable.dutyCycle], + data = it[SignalTable.data], + ) ) } + .firstOrNull() + } + } + + private suspend fun findSignal( + signalRequestModel: SignalRequestModel, + includedFileIds: Query, + brand: BrandModel, + // Index of successful results + index: Int = signalRequestModel.successResults.size, + categoryType: CategoryType, + category: DeviceCategory + ): SignalResponseModel { + val order = categoryConfigRepository.getOrNull( + categoryType = categoryType, + index = index + ) + // todo When orders is empty we can't define the next key. Need to think how to bypass it or may be just log + if (order == null) { + val infraredFileId = transaction(database) { + includedFileIds + .map { it[InfraredFileToSignalTable.infraredFileId] } + .first() + .value } + val response = SignalResponseModel(ifrFileModel = tableDao.ifrFileById(infraredFileId)) + return response } + + val signalModel = getSignalModel( + signalRequestModel = signalRequestModel, + order = order, + brand = brand + ) + if (signalModel == null) { + return findSignal( + signalRequestModel = signalRequestModel, + includedFileIds = includedFileIds, + brand = brand, + index = index + 1, + categoryType = categoryType, + category = category + ) + } + + val response = SignalResponseModel( + signalResponse = SignalResponse( + signalModel = signalModel, + message = order.message, + categoryName = category.meta.manifest.singularDisplayName, + data = order.data + ) + ) + return response } private fun Routing.statusRoute() { @@ -91,9 +222,46 @@ internal class SignalRouteRegistry( path = "signal", builder = { with(SignalSwagger) { createSwaggerDefinition() } }, body = { + @Suppress("UnusedPrivateProperty") val signalRequestModel = context.receive() - val signalResponseModel = getSignal(signalRequestModel) - context.respond(signalResponseModel) + + val brand = tableDao.getBrandById(signalRequestModel.brandId) + val category = tableDao.getCategoryById(brand.categoryId) + val categoryType = CategoryType + .entries + .firstOrNull { it.folderName == category.folderName } + ?: throw TableDaoException.CategoryNotFound(category.id) + + val includedFileIds = getIncludedFileIds(signalRequestModel) + val includedInfraredFilesCount = transaction(database) { includedFileIds.count() } + when (includedInfraredFilesCount) { + 0L -> { + context.respond(HttpStatusCode.NoContent) + return@post + } + + 1L -> { + val infraredFileId = transaction(database) { + includedFileIds + .map { it[InfraredFileToSignalTable.infraredFileId] } + .first() + .value + } + val response = SignalResponseModel(ifrFileModel = tableDao.ifrFileById(infraredFileId)) + context.respond(response) + } + + else -> { + val response = findSignal( + signalRequestModel = signalRequestModel, + includedFileIds = includedFileIds, + brand = brand, + categoryType = categoryType, + category = category + ) + context.respond(response) + } + } } ) } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/data/UiFileRepository.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/data/UiFileRepository.kt new file mode 100644 index 0000000..d9d022f --- /dev/null +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/data/UiFileRepository.kt @@ -0,0 +1,8 @@ +package com.flipperdevices.ifrmvp.backend.route.ui.data + +import com.flipperdevices.ifrmvp.backend.model.UiPresetModel + +interface UiFileRepository { + suspend fun getUiFileModelOrNull(ifrFileId: Long): UiPresetModel? + suspend fun getUiFileContent(uiFileModel: UiPresetModel): String +} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/data/UiFileRepositoryImpl.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/data/UiFileRepositoryImpl.kt new file mode 100644 index 0000000..94c1bb4 --- /dev/null +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/data/UiFileRepositoryImpl.kt @@ -0,0 +1,46 @@ +package com.flipperdevices.ifrmvp.backend.route.ui.data + +import com.flipperdevices.ifrmvp.backend.db.signal.dao.TableDao +import com.flipperdevices.ifrmvp.backend.db.signal.table.UiPresetTable +import com.flipperdevices.ifrmvp.backend.model.UiPresetModel +import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction + +class UiFileRepositoryImpl( + private val tableDao: TableDao, + private val database: Database, +) : UiFileRepository { + override suspend fun getUiFileModelOrNull(ifrFileId: Long): UiPresetModel? { + val uiFileModel = transaction(database) { + UiPresetTable.selectAll() + .where { UiPresetTable.infraredFileId eq ifrFileId } + .map { + UiPresetModel( + id = it[UiPresetTable.id].value, + fileName = it[UiPresetTable.fileName], + infraredFileId = it[UiPresetTable.infraredFileId].value + ) + } + .firstOrNull() + } + return uiFileModel + } + + override suspend fun getUiFileContent(uiFileModel: UiPresetModel): String { + val ifrFileModel = tableDao.ifrFileById(uiFileModel.infraredFileId) + val brandModel = tableDao.getBrandById(ifrFileModel.brandId) + val categoryModel = tableDao.getCategoryById(brandModel.categoryId) + val brandFolderName = brandModel.folderName + val categoryFolderName = categoryModel.folderName + val uiPresetFile = ParserPathResolver.uiPresetFile( + category = categoryFolderName, + brand = brandFolderName, + presetFileName = uiFileModel.fileName, + ifrFolderName = ifrFileModel.fileName.replace(".ir", "") + ) + if (!uiPresetFile.exists()) error("Preset file ${uiPresetFile.absolutePath} not exists") + return uiPresetFile.readText() + } +} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/di/UiModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/di/UiModule.kt index 18591d8..8114999 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/di/UiModule.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/di/UiModule.kt @@ -3,6 +3,7 @@ package com.flipperdevices.ifrmvp.backend.route.ui.di import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry import com.flipperdevices.ifrmvp.backend.db.signal.di.SignalApiModule import com.flipperdevices.ifrmvp.backend.route.key.di.KeyModule +import com.flipperdevices.ifrmvp.backend.route.ui.data.UiFileRepositoryImpl import com.flipperdevices.ifrmvp.backend.route.ui.presentation.UiRouteRegistry import com.flipperdevices.ifrmvp.parser.UiGeneratorImpl @@ -15,9 +16,12 @@ interface UiModule { ) : UiModule { override val registry: RouteRegistry by lazy { UiRouteRegistry( - signalApiModule.database, keyRouteRepository = keyModule.keyRouteRepository, - uiGenerator = UiGeneratorImpl() + uiGenerator = UiGeneratorImpl(), + uiFileRepository = UiFileRepositoryImpl( + tableDao = signalApiModule.tableDao, + database = signalApiModule.database + ) ) } } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiRouteRegistry.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiRouteRegistry.kt index ba1ebcc..cdee172 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiRouteRegistry.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiRouteRegistry.kt @@ -1,27 +1,19 @@ package com.flipperdevices.ifrmvp.backend.route.ui.presentation import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry -import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.IfrFileTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.UiPresetTable -import com.flipperdevices.ifrmvp.backend.model.IfrFileModel import com.flipperdevices.ifrmvp.backend.route.key.data.KeyRouteRepository +import com.flipperdevices.ifrmvp.backend.route.ui.data.UiFileRepository import com.flipperdevices.ifrmvp.parser.UiGenerator -import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver import io.github.smiley4.ktorswaggerui.dsl.routing.get import io.ktor.http.ContentType import io.ktor.server.response.respond import io.ktor.server.response.respondText import io.ktor.server.routing.Routing -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction internal class UiRouteRegistry( - private val database: Database, private val keyRouteRepository: KeyRouteRepository, - private val uiGenerator: UiGenerator + private val uiGenerator: UiGenerator, + private val uiFileRepository: UiFileRepository ) : RouteRegistry { private fun Routing.statusRoute() { @@ -30,56 +22,16 @@ internal class UiRouteRegistry( builder = { with(UiSwagger) { createSwaggerDefinition() } }, body = { val ifrFileId = context.parameters["ifr_file_id"]?.toLongOrNull() ?: -1 - val ifrFileModel = transaction(database) { - IfrFileTable.selectAll() - .where { IfrFileTable.id eq ifrFileId } - .map { - IfrFileModel( - id = it[IfrFileTable.id].value, - categoryId = it[IfrFileTable.categoryId].value, - brandId = it[IfrFileTable.brandId].value, - fileName = it[IfrFileTable.fileName] - ) - } - .firstOrNull() - ?: error("Ir file with id $ifrFileId not found!") - } - val categoryFolderName = transaction(database) { - CategoryTable.select(CategoryTable.folderName) - .where { CategoryTable.id eq ifrFileModel.categoryId } - .map { it[CategoryTable.folderName] } - .firstOrNull() - ?: error("Category with id ${ifrFileModel.categoryId} not found!") - } - val brandFolderName = transaction(database) { - BrandTable.select(BrandTable.displayName) - .where { BrandTable.id eq ifrFileModel.brandId } - .map { it[BrandTable.displayName] } - .firstOrNull() - ?: error("Brand with id ${ifrFileModel.brandId} not found!") - } - val uiFileName = transaction(database) { - UiPresetTable.selectAll() - .where { UiPresetTable.ifrFileId eq ifrFileId } - .map { it[UiPresetTable.fileName] } - .firstOrNull() -// ?: error("Could not find ui files with ifrid: $ifrFileId") - } - if (uiFileName != null) { - val uiPresetFile = ParserPathResolver.uiPresetFile( - category = categoryFolderName, - brand = brandFolderName, - presetFileName = uiFileName, - ifrFolderName = ifrFileModel.fileName.replace(".ir", "") - ) - if (!uiPresetFile.exists()) error("Preset file ${uiPresetFile.absolutePath} not exists") + val uiPresetModel = uiFileRepository.getUiFileModelOrNull(ifrFileId) + if (uiPresetModel == null) { + val ifrFile = keyRouteRepository.getIfrFile(ifrFileId) + context.respond(uiGenerator.generate(ifrFile.readText())) + } else { + val content = uiFileRepository.getUiFileContent(uiPresetModel) context.respondText( contentType = ContentType.Application.Json, - text = uiPresetFile.readText() + text = content ) - } else { - val ifrFile = keyRouteRepository.getIfrFile(ifrFileId) - context.respond(uiGenerator.generate(ifrFile.readText())) } } )