From 31503d8b68b683ca26d887cabcdd49ed87076b53 Mon Sep 17 00:00:00 2001 From: thiflict <121az348@gmail.com> Date: Fri, 3 Apr 2026 08:31:28 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9A=D0=BE=D0=B4=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=87=D0=B8=D0=B9,=20=D0=BE=D1=81=D1=82=D0=B0=D0=BB=D0=BE?= =?UTF-8?q?=D1=81=D1=8C=20=D1=84=D0=B8=D0=BA=D1=81=D0=B8=D1=82=D1=8C=20?= =?UTF-8?q?=D0=B2=D0=B7=D0=B0=D0=B8=D0=BC=D0=BE=D0=B4=D0=B5=D0=B9=D1=81?= =?UTF-8?q?=D1=82=D0=B2=D0=B8=D0=B5=20=D1=81=20ClamAV?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/ButtonListener.kt | 20 +++- src/main/kotlin/GPIOController.kt | 16 ++- src/main/kotlin/Main.kt | 174 ++++++++++-------------------- 3 files changed, 84 insertions(+), 126 deletions(-) diff --git a/src/main/kotlin/ButtonListener.kt b/src/main/kotlin/ButtonListener.kt index 4aa9077..7fe9da1 100644 --- a/src/main/kotlin/ButtonListener.kt +++ b/src/main/kotlin/ButtonListener.kt @@ -1,7 +1,7 @@ package ru.thiflict import kotlinx.coroutines.* -import ru.thiflict.DebugMode.isDebug +import ru.thiflict.DebugMode.DEBUG import ru.thiflict.PinConfig.BUTTON import java.io.BufferedReader import java.io.InputStreamReader @@ -26,6 +26,10 @@ class ButtonListener( clickListeners.add(listener) } + fun removeClickListener(listener: ButtonClickListener) { + clickListeners.remove(listener) + } + private fun notifyClick() { val event = ButtonClickEvent() clickListeners.forEach { it.invoke(event) } @@ -51,25 +55,25 @@ class ButtonListener( var line: String? while (reader.readLine().also { line = it } != null) { - println(if (isDebug) "$line" else "") + println(if (DEBUG) "$line" else "") when { line?.contains("rising") == true -> { currentButtonState.set(true) onStateChange?.invoke(true) - println(if (isDebug) "Кнопка нажата" else "") + println(if (DEBUG) "Кнопка нажата" else "") // Генерируем событие клика при нажатии notifyClick() } line?.contains("falling") == true -> { currentButtonState.set(false) onStateChange?.invoke(false) - println(if (isDebug) "Кнопка разжата" else "") + println(if (DEBUG) "Кнопка разжата" else "") // Убрана проверка на разжатие для генерации события } } } val exitCode = process!!.waitFor() - println(if (isDebug) "Процесс завершён ($exitCode)" else "") + println("Процесс завершён ($exitCode)") isListening.set(false) } } catch (e: Exception) { @@ -78,4 +82,10 @@ class ButtonListener( } return currentButtonState.get() } + + fun stopListening() { + listenerJob?.cancel() + process?.destroy() + isListening.set(false) + } } \ No newline at end of file diff --git a/src/main/kotlin/GPIOController.kt b/src/main/kotlin/GPIOController.kt index f4278fc..eda64cf 100644 --- a/src/main/kotlin/GPIOController.kt +++ b/src/main/kotlin/GPIOController.kt @@ -1,9 +1,16 @@ package ru.thiflict -import ru.thiflict.DebugMode.isDebug +import kotlinx.coroutines.delay +import ru.thiflict.PinConfig +import java.io.File +import kotlin.system.exitProcess class GPIOController(private val pin: Int) { + fun setDirection(direction: String) { + // Для libgpiod направление задаётся автоматически + } + fun setValue(value: Int) { try { // Пробуем разные варианты синтаксиса gpioset @@ -20,7 +27,7 @@ class GPIOController(private val pin: Int) { } } catch (e: Exception) { - println(if (isDebug) "Ошибка установки GPIO $pin = $value: ${e.message}" else "") + println("Ошибка установки GPIO $pin = $value: ${e.message}") } } @@ -34,7 +41,7 @@ class GPIOController(private val pin: Int) { process.waitFor() output.toIntOrNull() ?: 0 } catch (e: Exception) { - println(if (isDebug) "Ошибка чтения GPIO $pin: ${e.message}" else "") + println("Ошибка чтения GPIO $pin: ${e.message}") 0 } } @@ -44,4 +51,5 @@ class GPIOController(private val pin: Int) { } -} \ No newline at end of file +} + diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index ba880e5..6522d7a 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,13 +1,9 @@ package ru.thiflict import kotlinx.coroutines.* -import ru.thiflict.DebugMode.isDebug -import java.io.BufferedReader +import ru.thiflict.DebugMode.DEBUG import java.io.File -import java.io.InputStreamReader import kotlin.system.exitProcess -import java.util.* -import java.util.concurrent.* // Конфигурация GPIO (BCM номера) object PinConfig { @@ -18,11 +14,7 @@ object PinConfig { } object DebugMode { - var isDebug = false - - fun enableDebugging() { - isDebug = true - } + const val DEBUG = true } // Состояния системы @@ -40,11 +32,12 @@ class USBVirusScanner { private lateinit var greenLed: GPIOController private lateinit var yellowLed: GPIOController private lateinit var redLed: GPIOController + private lateinit var buttonReader: ButtonListener private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) fun initialize() { - println(if (isDebug) "Инициализация..." else "") + println("Инициализация...") try { // Проверяем доступность gpiod @@ -61,18 +54,16 @@ class USBVirusScanner { // Запускаем индикацию состояния startStateIndication() + currentState = SystemState.IDLE + Thread.sleep(500) - antivirusDatabaseUpdater() - - Thread.sleep(1000) - - println(if (isDebug) "Инициализация завершена." else "") + println("Инициализация завершена.") currentState = SystemState.IDLE } catch (e: Exception) { currentState = SystemState.ERROR - println(if (isDebug) "❌ Ошибка инициализации: ${e.message}" else "") + println("❌ Ошибка инициализации: ${e.message}") e.printStackTrace() throw e } @@ -84,23 +75,23 @@ class USBVirusScanner { val exitCode = process.waitFor() if (exitCode != 0) { - throw Exception(if (isDebug) "gpiodetect не найден" else "") + throw Exception("gpiodetect не найден") } val output = process.inputStream.bufferedReader().readText() - println(if (isDebug) "✅ libgpiod найден" else "") + println("✅ libgpiod найден") if (output.isNotBlank()) { - println(if (isDebug) "Доступные чипы: ${output.trim()}" else "") + println("Доступные чипы: ${output.trim()}") } } catch (e: Exception) { - throw Exception(if (isDebug) "libgpiod Не установлен!" else "") + throw Exception("libgpiod Не установлен!") } } fun buttonPress(defaultButtonState: Boolean): Boolean { var finalButtonState = defaultButtonState - println(if (isDebug) "Начало finalButtonState: $finalButtonState" else "") + println(if (DEBUG) "Начало finalButtonState: $finalButtonState" else "") val buttonListener = ButtonListener() val initialState = buttonListener.startListening { isPressed -> @@ -108,7 +99,7 @@ class USBVirusScanner { finalButtonState = true } } - println(if (isDebug) "finalButtonState: $finalButtonState" else "") + println(if (DEBUG) "finalButtonState: $finalButtonState" else "") return finalButtonState } @@ -173,27 +164,27 @@ class USBVirusScanner { fun startScan() { if (scanJob?.isActive == true) { - println(if (isDebug) "Сканирование уже выполняется" else "") + println("Сканирование уже выполняется") return } scanJob = scope.launch { currentState = SystemState.SCANNING - println(if (isDebug) "=== Начало сканирования ===" else "") + println("=== Начало сканирования ===") try { // Поиск USB-устройств val usbDevices = findUSBDevices() if (usbDevices.isEmpty()) { - println(if (isDebug) "Устройства не найдены" else "") + println("Устройства не найдены") currentState = SystemState.ERROR delay(5000) currentState = SystemState.IDLE return@launch } - println(if (isDebug) "📀 Найдены USB-устройства:" else "") + println("📀 Найдены USB-устройства:") usbDevices.forEach { device -> println(" - ${device.mountPoint}") } @@ -203,23 +194,23 @@ class USBVirusScanner { val scanResults = mutableListOf() for (device in usbDevices) { - println(if (isDebug) "🔍 Сканирование: ${device.mountPoint}" else "") + println("🔍 Сканирование: ${device.mountPoint}") val result = scanWithClamAV(device.mountPoint) scanResults.add(result) if (result.isInfected) { hasVirus = true - println(if (isDebug) "⚠️ ВНИМАНИЕ! Найдены угрозы!" else "") + println("⚠️ ВНИМАНИЕ! Найдены угрозы!") result.infectedFiles.take(10).forEach { file -> println(" 🦠 ${file.take(100)}") } if (result.infectedFiles.size > 10) { - println(if (isDebug) " ... и ещё ${result.infectedFiles.size - 10} файлов" else "") + println(" ... и ещё ${result.infectedFiles.size - 10} файлов") } } else if (result.error != null) { - println(if (isDebug) "❌ Ошибка сканирования: ${result.error}" else "") + println("❌ Ошибка сканирования: ${result.error}") } else { - println(if (isDebug) "✅ Сканирование завершено: угроз не найдено" else "") + println("✅ Сканирование завершено: угроз не найдено") } println() } @@ -234,25 +225,25 @@ class USBVirusScanner { } val message = if (hasVirus) { - if (isDebug) "🦠 Найдены вирусы! Красный LED горит" else "" + "🦠 Найдены вирусы! Красный LED горит" } else { - if (isDebug) "✅ Флешка чиста! Зелёный LED горит" else "" + "✅ Флешка чиста! Зелёный LED горит" } - println(if (isDebug) message else "") + println(message) // Держим состояние 5 секунд delay(5000) currentState = SystemState.IDLE } catch (e: Exception) { - println(if (isDebug) "❌ Критическая ошибка: ${e.message}" else "") + println("❌ Критическая ошибка: ${e.message}") e.printStackTrace() currentState = SystemState.ERROR delay(5000) currentState = SystemState.IDLE } - println(if (isDebug) "=== Сканирование завершено ===\n" else "") + println("=== Сканирование завершено ===\n") } } @@ -293,7 +284,7 @@ class USBVirusScanner { if (parts.size >= 2) { val mountPoint = parts[1] - if ((mountPoint.startsWith("/media")) && + if ((mountPoint.startsWith("/media") || mountPoint.startsWith("/mnt")) && File(mountPoint).exists() && devices.none { it.mountPoint == mountPoint }) { @@ -304,7 +295,7 @@ class USBVirusScanner { } } catch (e: Exception) { - println(if (isDebug) "Ошибка при поиске USB-устройств: ${e.message}" else "") + println("Ошибка при поиске USB-устройств: ${e.message}") } return devices.distinctBy { it.mountPoint } @@ -325,7 +316,7 @@ class USBVirusScanner { infectedFiles = emptyList(), virusNames = emptyMap(), exitCode = -1, - error = if (isDebug) "ClamAV не установлен. Установите: sudo apt install clamav" else "" + error = "ClamAV не установлен. Установите: sudo apt install clamav" ) } @@ -337,11 +328,11 @@ class USBVirusScanner { infectedFiles = emptyList(), virusNames = emptyMap(), exitCode = -1, - error = if (isDebug)"Путь не существует или не является директорией: $path" else "" + error = "Путь не существует или не является директорией: $path" ) } - println(if (isDebug) " Запуск clamscan (это может занять некоторое время)..." else "") + println(" Запуск clamscan (это может занять некоторое время)...") // Запускаем ClamAV val processBuilder = ProcessBuilder( @@ -349,8 +340,8 @@ class USBVirusScanner { "-r", "--stdout", "--infected", - "--max-filesize=1M", - "--max-scansize=10M", + "--max-filesize=1G", + "--max-scansize=1G", path ) @@ -378,17 +369,17 @@ class USBVirusScanner { infectedFiles = infectedFiles, virusNames = virusNames, exitCode = exitCode, - error = if (isDebug) (if (exitCode != 0 && exitCode != 1) "ClamAV вернул код ошибки: $exitCode" else "") else "" + error = if (exitCode != 0 && exitCode != 1) "ClamAV вернул код ошибки: $exitCode" else null ) } catch (e: Exception) { - println(if (isDebug) " Ошибка при выполнении clamscan: ${e.message}" else "") + println(" Ошибка при выполнении clamscan: ${e.message}") return ScanResult( isInfected = false, infectedFiles = emptyList(), virusNames = emptyMap(), exitCode = -1, - error = if (isDebug) e.message else "" + error = e.message ) } } @@ -428,14 +419,14 @@ class USBVirusScanner { } } - println(if (isDebug) "📝 Лог сохранён: ${logFile.absolutePath}" else "") + println("📝 Лог сохранён: ${logFile.absolutePath}") } catch (e: Exception) { - println(if (isDebug) "Ошибка сохранения лога: ${e.message}" else "") + println("Ошибка сохранения лога: ${e.message}") } } fun shutdown() { - println(if (isDebug) "\nЗавершение работы..." else "") + println("\nЗавершение работы...") isRunning = false indicationJob?.cancel() @@ -454,7 +445,7 @@ class USBVirusScanner { // Игнорируем ошибки при закрытии } - println(if (isDebug) "✅ Система остановлена" else "") + println("✅ Система остановлена") } } @@ -471,63 +462,14 @@ data class ScanResult( val error: String? = null ) -fun scheduleUpdaterAt3AM() { - val scheduler = Executors.newSingleThreadScheduledExecutor() - val now = Calendar.getInstance() - val targetTime = Calendar.getInstance().apply { - set(Calendar.HOUR_OF_DAY, 3) - set(Calendar.MINUTE, 0) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - } - var initialDelay = targetTime.timeInMillis - now.timeInMillis +fun main() { - // Если время уже прошло сегодня, планируем на завтра - if (initialDelay <= 0) { - targetTime.add(Calendar.DAY_OF_MONTH, 1) - initialDelay = targetTime.timeInMillis - now.timeInMillis - } - - scheduler.scheduleAtFixedRate( - { antivirusDatabaseUpdater() }, - initialDelay, - TimeUnit.DAYS.toMillis(1), - TimeUnit.MILLISECONDS - ) - - println("Планировщик запущен. Первый запуск через ${initialDelay / 1000 / 60} минут") -} - -fun antivirusDatabaseUpdater() { - println(if (isDebug) "Обновление базы данных антивируса" else "") - val processBuilder = ProcessBuilder("freshclam", "-vvv") - - processBuilder.redirectErrorStream(true) - - val process = processBuilder.start() - val reader = BufferedReader(InputStreamReader(process.inputStream)) - if (isDebug) { - reader.useLines { lines -> - lines.forEach { println("Process output: $it") } - } - } - process.waitFor() -} - -fun main(args: Array) { - - if (args.contains("--debug")) { - DebugMode.enableDebugging() - } - - scheduleUpdaterAt3AM() - - println(if (isDebug) "Зелёный - ${PinConfig.GREEN_LED}" else "") - println(if (isDebug) "Жёлтый - ${PinConfig.YELLOW_LED}" else "") - println(if (isDebug) "Красный - ${PinConfig.RED_LED}" else "") - println(if (isDebug) "Кнопка - ${PinConfig.BUTTON}" else "") + println(if (DebugMode.DEBUG) "Зелёный - ${PinConfig.GREEN_LED}" else "") + println(if (DebugMode.DEBUG) "Жёлтый - ${PinConfig.YELLOW_LED}" else "") + println(if (DebugMode.DEBUG) "Красный - ${PinConfig.RED_LED}" else "") + println(if (DebugMode.DEBUG) "Кнопка - ${PinConfig.BUTTON}" else "") val defaultButtonState = false val scanner = USBVirusScanner() @@ -545,23 +487,21 @@ fun main(args: Array) { } buttonListener.startListening() - println(if (isDebug) """ - ═══════════════════════════════════════ - USB Virus Scanner готов к работе - ═══════════════════════════════════════ - 📌 Вставьте флешку и нажмите кнопку - 📌 Зелёный LED мигает - режим ожидания - 📌 Жёлтый LED мигает - идёт проверка - 📌 Зелёный LED горит - флешка чиста - 📌 Красный LED горит - найдены вирусы - 📌 Для выхода нажмите Ctrl+C - """.trimIndent() else "") + println("═══════════════════════════════════════") + println(" USB Virus Scanner готов к работе") + println("═══════════════════════════════════════") + println("📌 Вставьте флешку и нажмите кнопку") + println("📌 Зелёный LED мигает - режим ожидания") + println("📌 Жёлтый LED мигает - идёт проверка") + println("📌 Зелёный LED горит - флешка чиста") + println("📌 Красный LED горит - найдены вирусы") + println("📌 Для выхода нажмите Ctrl+C\n") while (true) { Thread.sleep(1000) } } catch (e: Exception) { - println(if (isDebug) "❌ Критическая ошибка: ${e.message}" else "") + println("❌ Критическая ошибка: ${e.message}") e.printStackTrace() scanner.shutdown() exitProcess(1)