From a8e882f5640329ebd5bc53084afd0f77b3550ece Mon Sep 17 00:00:00 2001 From: Abel Mekonnen Date: Thu, 11 Dec 2025 01:41:59 +0300 Subject: [PATCH 1/5] feat: Add core automation components and database management - Introduced `ActuatorManager`, `ItemTracker`, `ProductionLine`, `SensorManager`, and `SimulationEngine` classes for managing automation processes. - Implemented `DatabaseManager` for SQLite database interactions, including connection handling and CRUD operations. - Created `ProductItem` entity to replace the deprecated `Product` class, enhancing item tracking capabilities. - Developed `Logger` utility for logging events and errors within the system. - Added `ConsoleApp` and `ConsoleUI` for user interaction and simulation control. - Removed obsolete `EventLog` and `ProductRepository` classes to streamline the codebase. - Updated package structure to follow consistent naming conventions. --- PROJECT_OVERVIEW.html | 115 +++++++++++++ PROJECT_OVERVIEW.md | 162 ++++++++++++++++++ .../Controllers/ActuatorManager.java | 5 + .../Automation/Controllers/ItemTracker.java | 53 ++++++ .../Controllers/ProductionLine.java | 5 + .../Automation/Controllers/SensorManager.java | 5 + .../Controllers/WorkFlowController.java | 7 +- src/main/java/org/Automation/Main.java | 10 +- .../{ => database}/DatabaseManager.java | 36 +--- .../org/Automation/engine/ClockObserver.java | 5 + .../SimulationClock.java | 2 +- .../SimulationEngine.java | 7 +- .../org/Automation/entities/ConveyorBelt.java | 2 +- .../org/Automation/entities/EventLog.java | 24 --- .../java/org/Automation/entities/Machine.java | 2 +- .../java/org/Automation/entities/Product.java | 26 ++- .../org/Automation/entities/ProductItem.java | 74 ++++++++ .../java/org/Automation/entities/Sensor.java | 2 +- .../java/org/Automation/entities/Worker.java | 2 +- .../repositories/ConveyorRepository.java | 6 +- .../repositories/EventLogRepository.java | 39 ----- .../repositories/MachineRepository.java | 6 +- .../repositories/ProductItemRepository.java | 39 +++++ .../repositories/ProductRepository.java | 32 ---- .../Automation/repositories/Repository.java | 4 +- .../repositories/SensorRepository.java | 6 +- .../repositories/WorkerRepository.java | 7 +- .../{ConsoleUI => ui}/ConsoleApp.java | 10 +- .../{ConsoleUI => ui}/ConsoleUI.java | 3 +- .../java/org/Automation/utils/Logger.java | 55 ++++++ 30 files changed, 575 insertions(+), 176 deletions(-) create mode 100644 PROJECT_OVERVIEW.html create mode 100644 PROJECT_OVERVIEW.md create mode 100644 src/main/java/org/Automation/Controllers/ActuatorManager.java create mode 100644 src/main/java/org/Automation/Controllers/ItemTracker.java create mode 100644 src/main/java/org/Automation/Controllers/ProductionLine.java create mode 100644 src/main/java/org/Automation/Controllers/SensorManager.java rename src/main/java/org/Automation/{ => database}/DatabaseManager.java (84%) create mode 100644 src/main/java/org/Automation/engine/ClockObserver.java rename src/main/java/org/Automation/{Controllers/Simluators => engine}/SimulationClock.java (98%) rename src/main/java/org/Automation/{Controllers/Simluators => engine}/SimulationEngine.java (84%) delete mode 100644 src/main/java/org/Automation/entities/EventLog.java create mode 100644 src/main/java/org/Automation/entities/ProductItem.java delete mode 100644 src/main/java/org/Automation/repositories/EventLogRepository.java create mode 100644 src/main/java/org/Automation/repositories/ProductItemRepository.java delete mode 100644 src/main/java/org/Automation/repositories/ProductRepository.java rename src/main/java/org/Automation/{ConsoleUI => ui}/ConsoleApp.java (74%) rename src/main/java/org/Automation/{ConsoleUI => ui}/ConsoleUI.java (92%) create mode 100644 src/main/java/org/Automation/utils/Logger.java diff --git a/PROJECT_OVERVIEW.html b/PROJECT_OVERVIEW.html new file mode 100644 index 0000000..d859c7d --- /dev/null +++ b/PROJECT_OVERVIEW.html @@ -0,0 +1,115 @@ +Mini Factory Automation Simulation — Project Overview
+

Mini Factory Automation Simulation — Project Overview

+

Project summary

+

This Java project simulates a small automated factory with conveyors, machines, sensors, workers, actuators and a simple logging / persistence layer. It provides a simulation engine, controllers for workflow and item tracking, a console UI, and repositories that persist entities to an SQLite database (automation.sqlite).

+
+
+

High-level architecture

+
    +
  • Entities: domain objects representing factory parts (Product, ProductItem, Machine, Sensor, Worker, ConveyorBelt, EventLog, ItemTracker).
  • +
  • Controllers / Simulators: SimulationEngine, SimulationClock, WorkFlowController, ItemTracker controller manage simulation steps and item flow.
  • +
  • Repositories: CRUD-like persistence layer (Repository, MachineRepository, ConveyorRepository, SensorRepository, WorkerRepository, ProductItemRepository, LoggerRepository).
  • +
  • Actuators / Packaging: Actuator, ActuatorManager, PackagingMachine — components responsible for simulated physical actions.
  • +
  • UI: ConsoleUI package with ConsoleApp and ConsoleUI to interact with the simulation.
  • +
  • Utilities: Logger and DatabaseManager for logging and DB access.
  • +
+
+
+

Important files & responsibilities

+
    +
  • +

    src/main/java/org/Automation/Main.java — Application entry point.

    +
  • +
  • +

    src/main/java/org/Automation/DatabaseManager.java — SQLite connection and DB helpers.

    +
  • +
  • +

    src/main/java/org/Automation/Logger.java — Application logging wrapper.

    +
  • +
  • +

    src/main/java/org/Automation/ConsoleUI/ConsoleApp.java, ConsoleUI.java — Console-based interface and commands.

    +
  • +
  • +

    src/main/java/org/Automation/Controllers/WorkFlowController.java — Orchestrates item flow between machines/conveyors.

    +
  • +
  • +

    src/main/java/org/Automation/Controllers/Simluators/SimulationEngine.java — Core simulation loop; advances SimulationClock and triggers events.

    +
  • +
+
+
+
    +
  • +

    src/main/java/org/Automation/entities/* — Domain models (Product, ProductItem, Machine, Sensor, ConveyorBelt, Worker, EventLog, ItemTracker).

    +
  • +
  • +

    src/main/java/org/Automation/repositories/* — Persistence adapter layer into SQLite; check Repository.java for interface details.

    +
  • +
  • +

    Actuator/, ActuatorManager/, PackagingMachine/ — Additional modules representing actuators and packaging machines (single-file implementations).

    +
  • +
+
+
+

How the simulation typically runs

+
    +
  1. Main initializes DatabaseManager, repositories, logger and loads initial entities.
  2. +
  3. SimulationEngine / SimulationClock drive simulated time and call controllers.
  4. +
  5. WorkFlowController and ItemTracker move ProductItem instances through conveyors and machines.
  6. +
  7. Sensors and Actuators simulate detection and actions; EventLog records notable events.
  8. +
  9. ConsoleUI allows starting/stopping the simulation and observing logs.
  10. +
+
+
+

Database

+
    +
  • File: automation.sqlite (in repo root)
  • +
  • DatabaseManager abstracts JDBC/SQLite usage. Backups or schema changes should be handled carefully.
  • +
+

Logging

+
    +
  • Logger and LoggerRepository provide logging to console and persistence (EventLog entries).
  • +
+
+
+

Extending the project

+
    +
  • Add new entity: create in entities/, add repository implementation and register it where repositories are loaded.
  • +
  • Add a machine or actuator: implement Machine/Actuator subclass and update WorkFlowController to use it.
  • +
  • Add UI commands: extend ConsoleApp/ConsoleUI to expose new controls.
  • +
+
+
+

Notes and pointers for contributors

+
    +
  • Read Repository.java to understand persistence contract.
  • +
  • SimulationEngine is the heart of runtime behavior—review it before modifying controllers.
  • +
  • Keep domain model logic in entities; controllers should orchestrate behavior, not contain domain rules.
  • +
  • Tests are in src/test/ — add unit tests for new behavior.
  • +
  • The project uses SQLite for simplicity; consider migration scripts if schema evolves.
  • +
+
+
+

Quick file map (top-level)

+
    +
  • pom.xml — Maven build
  • +
  • automation.sqlite — persisted DB used during simulation
  • +
  • src/main/java/org/Automation — main source tree
  • +
  • Actuator/, ActuatorManager/, PackagingMachine/ — simple module files
  • +
+

Contact / further help

+

For targeted walkthroughs (class-by-class), request which package or file to dig into next and a focused .md can be produced.

+
+
\ No newline at end of file diff --git a/PROJECT_OVERVIEW.md b/PROJECT_OVERVIEW.md new file mode 100644 index 0000000..b4e9196 --- /dev/null +++ b/PROJECT_OVERVIEW.md @@ -0,0 +1,162 @@ +--- +marp: true +theme: default +size: 16:9 +paginate: true +class: lead +style: | + section { + background: #0b1221; + color: #ffffff; + font-family: 'Inter', sans-serif; + padding: 60px; + } + + h1, h2, h3 { + font-weight: 900; + letter-spacing: -0.5px; + } + + h1 { + font-size: 3.2rem; + color: #7c5dff; + } + + h2 { + font-size: 2.2rem; + color: #c1b3ff; + } + + p { + font-size: 1.2rem; + line-height: 1.6; + color: #d6d6e4; + } + + .card { + background: #0f1b2b; + padding: 20px; + border-radius: 18px; + margin: 20px 0; + box-shadow: 0 0 20px rgba(124, 93, 255, 0.2); + } + + ul { + font-size: 1.2rem; + line-height: 1.7; + } + +--- + +# 🌌 **Deep Dive Presentation** +### Elegant. Minimal. Professional. + +--- + +# ⭐ Introduction +Welcome to a deep and structured exploration of your topic. + +This presentation is designed to: +- Give clarity +- Build strong understanding +- Present ideas in a polished way + +--- + +# 🧠 Understanding the Core Concepts +## A Strong Foundation + +
+Your learning begins with breaking each idea into small, digestible parts. +
+ +We focus on: +- Definitions +- Purpose +- Real-world relevance + +--- + +# 🌍 Real-World Application +### Why These Concepts Matter + +
+Real engineering isn’t about memorizing theory—it’s about applying it. +
+ +Examples: +- Scalable systems +- Efficient problem-solving +- Clean architecture + +--- + +# ⚙️ Technical Insight +## Key Mechanics & How They Work + +This section explores: +- Internal processes +- Data flow +- Performance factors + +--- + +# 📊 Visualization +### How Everything Connects + +
+Use diagrams, mental models, and intuitive analogies. +
+ +Helps you: +- See patterns +- Predict results +- Build long-term understanding + +--- + +# 🧩 Problem Breakdown +## From Complex → Simple + +Your thinking process should: +1. Define the goal +2. Identify constraints +3. Break steps down +4. Optimize solutions + +--- + +# 🚀 Strategy & Best Practices +### What You Must Always Do + +- Think in systems +- Document your thought process +- Prioritize clarity +- Avoid unnecessary complexity + +--- + +# 🛡️ Common Mistakes +## What to Avoid + +- Overthinking trivial details +- Skipping fundamentals +- Writing unreadable code +- Forgetting performance impact + +--- + +# 🧭 Summary +### The Path to Mastery + +
+Build understanding → Apply → Reflect → Improve. +
+ +That is the cycle that makes an engineer unstoppable. + +--- + +# 🙌 Thank You +### Questions? +### Ready for the next step? diff --git a/src/main/java/org/Automation/Controllers/ActuatorManager.java b/src/main/java/org/Automation/Controllers/ActuatorManager.java new file mode 100644 index 0000000..7e91f4d --- /dev/null +++ b/src/main/java/org/Automation/Controllers/ActuatorManager.java @@ -0,0 +1,5 @@ +package org.automation.controllers; + +public class ActuatorManager { + +} diff --git a/src/main/java/org/Automation/Controllers/ItemTracker.java b/src/main/java/org/Automation/Controllers/ItemTracker.java new file mode 100644 index 0000000..18e21ae --- /dev/null +++ b/src/main/java/org/Automation/Controllers/ItemTracker.java @@ -0,0 +1,53 @@ +package org.automation.controllers; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.automation.utils.Logger; +import org.automation.entities.ProductItem; + +public class ItemTracker { + private final Map items = new ConcurrentHashMap<>(); + private int nextId = 1; + private final Logger logger; + + public ItemTracker(Logger logger){ + this.logger = logger; + }; + + public synchronized ProductItem addItem(ProductItem item) { + int id = nextId++; + item.setId(id); + items.put(id, item); + logger.log("ProductItem", id, "created", "Item created: " + item.getName()); + return item; + } + + public ProductItem getItem(int id) { + return items.get(id); + } + + public Collection getAllItems() { + return Collections.unmodifiableCollection(items.values()); + } + + public boolean updateItemStatus(int id, String status) { + ProductItem it = items.get(id); + if (it == null) return false; + String prev = it.getStatus(); + it.setStatus(status); + logger.log("ProductItem", id, "status_update", "Status changed: " + prev + " -> " + status); + return true; + } + + public boolean trackMovement(int id, int stationId) { + ProductItem it = items.get(id); + if (it == null) return false; + int prev = it.getCurrentStationId(); + it.setCurrentStationId(stationId); + logger.log("ProductItem", id, "moved", "Moved from station " + prev + " to " + stationId); + return true; + } +} diff --git a/src/main/java/org/Automation/Controllers/ProductionLine.java b/src/main/java/org/Automation/Controllers/ProductionLine.java new file mode 100644 index 0000000..2b489eb --- /dev/null +++ b/src/main/java/org/Automation/Controllers/ProductionLine.java @@ -0,0 +1,5 @@ +package org.automation.controllers; + +public class ProductionLine { + +} diff --git a/src/main/java/org/Automation/Controllers/SensorManager.java b/src/main/java/org/Automation/Controllers/SensorManager.java new file mode 100644 index 0000000..b4a97c4 --- /dev/null +++ b/src/main/java/org/Automation/Controllers/SensorManager.java @@ -0,0 +1,5 @@ +package org.automation.controllers; + +public class SensorManager { + +} diff --git a/src/main/java/org/Automation/Controllers/WorkFlowController.java b/src/main/java/org/Automation/Controllers/WorkFlowController.java index 11b3bc7..56b7746 100644 --- a/src/main/java/org/Automation/Controllers/WorkFlowController.java +++ b/src/main/java/org/Automation/Controllers/WorkFlowController.java @@ -1,8 +1,5 @@ -package WorkflowController; -import ProductionLine.ProductionLine; -import SensorManager.SensorManager; -import ActuatorManager.ActuatorManager; -import ItemTracker.ItemTracker; +package org.automation.controllers; + public class WorkflowController { //Instance Variables private ProductionLine productionLine; diff --git a/src/main/java/org/Automation/Main.java b/src/main/java/org/Automation/Main.java index 8fd6ea3..af30fa1 100644 --- a/src/main/java/org/Automation/Main.java +++ b/src/main/java/org/Automation/Main.java @@ -1,8 +1,16 @@ -package org.Automation; +package org.automation; + +import org.automation.controllers.ItemTracker; +import org.automation.entities.ProductItem; +import org.automation.database.DatabaseManager; +import org.automation.utils.Logger; public class Main { public static void main(String[] args) { DatabaseManager db = new DatabaseManager(); db.connect(); + + // cleanup: disconnect DB + db.disconnect(); } } \ No newline at end of file diff --git a/src/main/java/org/Automation/DatabaseManager.java b/src/main/java/org/Automation/database/DatabaseManager.java similarity index 84% rename from src/main/java/org/Automation/DatabaseManager.java rename to src/main/java/org/Automation/database/DatabaseManager.java index 37a8727..c8c1010 100644 --- a/src/main/java/org/Automation/DatabaseManager.java +++ b/src/main/java/org/Automation/database/DatabaseManager.java @@ -1,33 +1,13 @@ -package org.Automation; +package org.automation.database; +import java.lang.ref.Cleaner; import java.sql.*; -interface DatabaseManagerInterface { - - boolean connect(); - - Connection getConnection(); - - boolean disconnect(); - - ResultSet find(String tableName, String where, Object[] params); - - boolean insert(String tableName, String[] columns, Object[] values); - - boolean delete(String tableName, String where, Object[] params); - - boolean update(String tableName, String setClause, String where, Object[] params); -} - -public final class DatabaseManager implements DatabaseManagerInterface { +public final class DatabaseManager { private Connection connection; private final String url = "jdbc:sqlite:automation.sqlite"; - public DatabaseManager() { - } - - @Override public boolean connect() { if (connection != null) return true; @@ -41,12 +21,10 @@ public boolean connect() { } } - @Override public Connection getConnection() { return this.connection; } - @Override public boolean disconnect() { try { if (connection != null) { @@ -91,7 +69,6 @@ private boolean executeMutator(String sql, Object[] params) { } // ------------ Find (SELECT) ------------- - @Override public ResultSet find(String tableName, String where, Object[] params) { String sql = "SELECT * FROM " + tableName; @@ -103,7 +80,6 @@ public ResultSet find(String tableName, String where, Object[] params) { } // ------------ INSERT ------------- - @Override public boolean insert(String tableName, String[] columns, Object[] values) { if (columns.length != values.length) @@ -127,7 +103,6 @@ public boolean insert(String tableName, String[] columns, Object[] values) { } // ------------ DELETE ------------- - @Override public boolean delete(String tableName, String where, Object[] params) { String sql = "DELETE FROM " + tableName + (where != null ? " WHERE " + where : ""); @@ -136,7 +111,6 @@ public boolean delete(String tableName, String where, Object[] params) { } // ------------ UPDATE ------------- - @Override public boolean update(String tableName, String setClause, String where, Object[] params) { String sql = "UPDATE " + tableName + " SET " + setClause + @@ -144,4 +118,8 @@ public boolean update(String tableName, String setClause, String where, Object[] return executeMutator(sql, params); } + + void Cleaner(){ + disconnect(); + } } diff --git a/src/main/java/org/Automation/engine/ClockObserver.java b/src/main/java/org/Automation/engine/ClockObserver.java new file mode 100644 index 0000000..712cd4b --- /dev/null +++ b/src/main/java/org/Automation/engine/ClockObserver.java @@ -0,0 +1,5 @@ +package org.automation.engine; + +public class ClockObserver { + +} diff --git a/src/main/java/org/Automation/Controllers/Simluators/SimulationClock.java b/src/main/java/org/Automation/engine/SimulationClock.java similarity index 98% rename from src/main/java/org/Automation/Controllers/Simluators/SimulationClock.java rename to src/main/java/org/Automation/engine/SimulationClock.java index 80eb42c..59becdf 100644 --- a/src/main/java/org/Automation/Controllers/Simluators/SimulationClock.java +++ b/src/main/java/org/Automation/engine/SimulationClock.java @@ -1,4 +1,4 @@ -package Sim_Engine; +package org.automation.engine; import java.time.LocalDateTime; //import java.time.Duration; import java.util.ArrayList; diff --git a/src/main/java/org/Automation/Controllers/Simluators/SimulationEngine.java b/src/main/java/org/Automation/engine/SimulationEngine.java similarity index 84% rename from src/main/java/org/Automation/Controllers/Simluators/SimulationEngine.java rename to src/main/java/org/Automation/engine/SimulationEngine.java index c2dfc57..026a6b7 100644 --- a/src/main/java/org/Automation/Controllers/Simluators/SimulationEngine.java +++ b/src/main/java/org/Automation/engine/SimulationEngine.java @@ -1,8 +1,7 @@ -package Sim_Engine; - +package org.automation.engine; import java.time.LocalDateTime; - -import WorkflowController.WorkflowController; +import org.automation.controllers.WorkflowController; +import org.automation.utils.Logger; public class SimulationEngine { //Instance Variables diff --git a/src/main/java/org/Automation/entities/ConveyorBelt.java b/src/main/java/org/Automation/entities/ConveyorBelt.java index ea268c6..91df915 100644 --- a/src/main/java/org/Automation/entities/ConveyorBelt.java +++ b/src/main/java/org/Automation/entities/ConveyorBelt.java @@ -1,4 +1,4 @@ -package org.Automation.entities; +package org.automation.entities; public class ConveyorBelt { public int id; diff --git a/src/main/java/org/Automation/entities/EventLog.java b/src/main/java/org/Automation/entities/EventLog.java deleted file mode 100644 index 50b6f9e..0000000 --- a/src/main/java/org/Automation/entities/EventLog.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.Automation.entities; - -public class EventLog { - public int id; - public String timestamp; - public String componentType; - public int componentId; - public String eventType; - public String message; - - public EventLog(int id, String timestamp, String componentType, int componentId, String eventType, String message) { - this.id = id; - this.timestamp = timestamp; - this.componentType = componentType; - this.componentId = componentId; - this.eventType = eventType; - this.message = message; - } - - @Override - public String toString() { - return "EventLog{id=" + id + ", timestamp=" + timestamp + ", componentType=" + componentType + ", componentId=" + componentId + ", eventType=" + eventType + ", message=" + message + "}"; - } -} diff --git a/src/main/java/org/Automation/entities/Machine.java b/src/main/java/org/Automation/entities/Machine.java index 9582678..a6e3aaf 100644 --- a/src/main/java/org/Automation/entities/Machine.java +++ b/src/main/java/org/Automation/entities/Machine.java @@ -1,4 +1,4 @@ -package org.Automation.entities; +package org.automation.entities; public class Machine { public int id; diff --git a/src/main/java/org/Automation/entities/Product.java b/src/main/java/org/Automation/entities/Product.java index d50a76d..6e80551 100644 --- a/src/main/java/org/Automation/entities/Product.java +++ b/src/main/java/org/Automation/entities/Product.java @@ -1,18 +1,12 @@ -package org.Automation.entities; +package org.automation.entities; -public class Product { - public int id; - public String name; - public String status; - - public Product(int id, String name, String status) { - this.id = id; - this.name = name; - this.status = status; - } - - @Override - public String toString() { - return "Product{id=" + id + ", name=" + name + ", status=" + status + "}"; - } +/** + * DEPRECATED: Replaced by `ProductItem`. + * + * This class was kept for compatibility but should not be used anymore. + */ +@Deprecated +public final class Product { + private Product() { + /* prevent instantiation */ } } diff --git a/src/main/java/org/Automation/entities/ProductItem.java b/src/main/java/org/Automation/entities/ProductItem.java new file mode 100644 index 0000000..4c6d49a --- /dev/null +++ b/src/main/java/org/Automation/entities/ProductItem.java @@ -0,0 +1,74 @@ +package org.automation.entities; + +public class ProductItem { + public int id; + public String name; + public double weight; + public String status; + public int currentStationId; + public String createdAt; + + public ProductItem(int id, String name, double weight, String status, String createdAt, int currentStationId) { + this.id = id; + this.name = name; + this.weight = weight; + this.status = status; + this.createdAt = createdAt; + this.currentStationId = currentStationId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getWeight() { + return weight; + } + + public void setWeight(double weight) { + this.weight = weight; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public int getCurrentStationId() { + return currentStationId; + } + + public void setCurrentStationId(int currentStationId) { + this.currentStationId = currentStationId; + } + + @Override + public String toString() { + return "ProductItem{id=" + id + ", name=" + name + ", weight=" + weight + ", status=" + status + ", createdAt=" + + createdAt + ", currentStationId=" + currentStationId + "}"; + } +} + diff --git a/src/main/java/org/Automation/entities/Sensor.java b/src/main/java/org/Automation/entities/Sensor.java index b1cdd63..44f823b 100644 --- a/src/main/java/org/Automation/entities/Sensor.java +++ b/src/main/java/org/Automation/entities/Sensor.java @@ -1,4 +1,4 @@ -package org.Automation.entities; +package org.automation.entities; public class Sensor { public int id; diff --git a/src/main/java/org/Automation/entities/Worker.java b/src/main/java/org/Automation/entities/Worker.java index 1b122ba..30a1555 100644 --- a/src/main/java/org/Automation/entities/Worker.java +++ b/src/main/java/org/Automation/entities/Worker.java @@ -1,4 +1,4 @@ -package org.Automation.entities; +package org.automation.entities; public class Worker { public int id; diff --git a/src/main/java/org/Automation/repositories/ConveyorRepository.java b/src/main/java/org/Automation/repositories/ConveyorRepository.java index 93856b6..676563f 100644 --- a/src/main/java/org/Automation/repositories/ConveyorRepository.java +++ b/src/main/java/org/Automation/repositories/ConveyorRepository.java @@ -1,10 +1,10 @@ -package org.Automation.repositories; +package org.automation.repositories; import java.sql.ResultSet; import java.sql.SQLException; -import org.Automation.DatabaseManager; -import org.Automation.entities.ConveyorBelt; +import org.automation.database.DatabaseManager; +import org.automation.entities.ConveyorBelt; public class ConveyorRepository extends Repository { public ConveyorRepository(DatabaseManager db) { diff --git a/src/main/java/org/Automation/repositories/EventLogRepository.java b/src/main/java/org/Automation/repositories/EventLogRepository.java deleted file mode 100644 index 925b7df..0000000 --- a/src/main/java/org/Automation/repositories/EventLogRepository.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.Automation.repositories; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.Automation.DatabaseManager; -import org.Automation.entities.EventLog; - -public class EventLogRepository extends Repository { - public EventLogRepository(DatabaseManager db) { - super("EventLog", db); - } - - - @Override - public EventLog mapRow(ResultSet rs) throws SQLException { - return new EventLog( - rs.getInt("id"), - rs.getString("timestamp"), - rs.getString("componentType"), - rs.getInt("componentId"), - rs.getString("eventType"), - rs.getString("message")); - } - - @Override - public String createTableQuery() { - return """ - CREATE TABLE IF NOT EXISTS EventLog ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - timestamp TEXT NOT NULL, - componentType TEXT, - componentId INTEGER, - eventType TEXT, - message TEXT - ); - """; - } -} diff --git a/src/main/java/org/Automation/repositories/MachineRepository.java b/src/main/java/org/Automation/repositories/MachineRepository.java index d442f47..94b0166 100644 --- a/src/main/java/org/Automation/repositories/MachineRepository.java +++ b/src/main/java/org/Automation/repositories/MachineRepository.java @@ -1,6 +1,6 @@ -package org.Automation.repositories; -import org.Automation.entities.Machine; -import org.Automation.DatabaseManager; +package org.automation.repositories; +import org.automation.entities.Machine; +import org.automation.database.DatabaseManager; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/src/main/java/org/Automation/repositories/ProductItemRepository.java b/src/main/java/org/Automation/repositories/ProductItemRepository.java new file mode 100644 index 0000000..fe3d082 --- /dev/null +++ b/src/main/java/org/Automation/repositories/ProductItemRepository.java @@ -0,0 +1,39 @@ +package org.automation.repositories; + +import org.automation.entities.ProductItem; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.automation.database.DatabaseManager; + +public class ProductItemRepository extends Repository { + public ProductItemRepository(DatabaseManager db) { + super("ProductItem", db); + } + + @Override + public String createTableQuery() { + return """ + CREATE TABLE IF NOT EXISTS ProductItem ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + weight REAL, + status TEXT, + createdAt TEXT, + currentStationId INTEGER + ); + """; + } + + @Override + protected ProductItem mapRow(ResultSet rs) throws SQLException { + return new ProductItem( + rs.getInt("id"), + rs.getString("name"), + rs.getDouble("weight"), + rs.getString("status"), + rs.getString("createdAt"), + rs.getInt("currentStationId")); + } +} \ No newline at end of file diff --git a/src/main/java/org/Automation/repositories/ProductRepository.java b/src/main/java/org/Automation/repositories/ProductRepository.java deleted file mode 100644 index 899bf5e..0000000 --- a/src/main/java/org/Automation/repositories/ProductRepository.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.Automation.repositories; -import org.Automation.entities.Product; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.Automation.DatabaseManager; - -public class ProductRepository extends Repository { - public ProductRepository(DatabaseManager db) { - super("Product", db); - } - - @Override - public String createTableQuery() { - return """ - CREATE TABLE IF NOT EXISTS Product ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - status TEXT - ); - """; - } - - @Override - protected Product mapRow(ResultSet rs) throws SQLException { - return new Product( - rs.getInt("id"), - rs.getString("name"), - rs.getString("status")); - } -} \ No newline at end of file diff --git a/src/main/java/org/Automation/repositories/Repository.java b/src/main/java/org/Automation/repositories/Repository.java index ca0dab9..666ba81 100644 --- a/src/main/java/org/Automation/repositories/Repository.java +++ b/src/main/java/org/Automation/repositories/Repository.java @@ -1,5 +1,5 @@ -package org.Automation.repositories; -import org.Automation.DatabaseManager; +package org.automation.repositories; +import org.automation.database.DatabaseManager; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/src/main/java/org/Automation/repositories/SensorRepository.java b/src/main/java/org/Automation/repositories/SensorRepository.java index 85b861b..3ccd078 100644 --- a/src/main/java/org/Automation/repositories/SensorRepository.java +++ b/src/main/java/org/Automation/repositories/SensorRepository.java @@ -1,10 +1,10 @@ -package org.Automation.repositories; +package org.automation.repositories; import java.sql.ResultSet; import java.sql.SQLException; -import org.Automation.DatabaseManager; -import org.Automation.entities.Sensor; +import org.automation.database.DatabaseManager; +import org.automation.entities.Sensor; public class SensorRepository extends Repository { public SensorRepository(DatabaseManager db) { diff --git a/src/main/java/org/Automation/repositories/WorkerRepository.java b/src/main/java/org/Automation/repositories/WorkerRepository.java index 3c73d5a..5dfa9c8 100644 --- a/src/main/java/org/Automation/repositories/WorkerRepository.java +++ b/src/main/java/org/Automation/repositories/WorkerRepository.java @@ -1,16 +1,15 @@ -package org.Automation.repositories; +package org.automation.repositories; import java.sql.ResultSet; import java.sql.SQLException; -import org.Automation.DatabaseManager; -import org.Automation.entities.Worker; +import org.automation.database.DatabaseManager; +import org.automation.entities.Worker; public class WorkerRepository extends Repository { public WorkerRepository(DatabaseManager db) { super("Worker", db); } - @Override public Worker mapRow(ResultSet rs) throws SQLException { diff --git a/src/main/java/org/Automation/ConsoleUI/ConsoleApp.java b/src/main/java/org/Automation/ui/ConsoleApp.java similarity index 74% rename from src/main/java/org/Automation/ConsoleUI/ConsoleApp.java rename to src/main/java/org/Automation/ui/ConsoleApp.java index 5ba5601..442e9f6 100644 --- a/src/main/java/org/Automation/ConsoleUI/ConsoleApp.java +++ b/src/main/java/org/Automation/ui/ConsoleApp.java @@ -1,12 +1,14 @@ -package ConsoleUI; -import Sim_Engine.SimulationEngine; -import WorkflowController.WorkflowController; -import Logger.Logger; +package org.automation.ui; +import org.automation.engine.SimulationEngine; +import org.automation.controllers.WorkflowController; +import org.automation.utils.Logger; + public class ConsoleApp extends ConsoleUI { //Instance Variables private SimulationEngine simulationEngine; private WorkflowController controller; private Logger logger; + ConsoleApp(){ simulationEngine= new SimulationEngine(); } diff --git a/src/main/java/org/Automation/ConsoleUI/ConsoleUI.java b/src/main/java/org/Automation/ui/ConsoleUI.java similarity index 92% rename from src/main/java/org/Automation/ConsoleUI/ConsoleUI.java rename to src/main/java/org/Automation/ui/ConsoleUI.java index 7af809e..0028227 100644 --- a/src/main/java/org/Automation/ConsoleUI/ConsoleUI.java +++ b/src/main/java/org/Automation/ui/ConsoleUI.java @@ -1,11 +1,10 @@ -package ConsoleUI; +package org.automation.ui; import java.util.*; public class ConsoleUI { // Low-Level Menu print lines public void printHeader(String title) { } - public void printMenu(ArrayList options) { } diff --git a/src/main/java/org/Automation/utils/Logger.java b/src/main/java/org/Automation/utils/Logger.java new file mode 100644 index 0000000..640946d --- /dev/null +++ b/src/main/java/org/Automation/utils/Logger.java @@ -0,0 +1,55 @@ +package org.automation.utils; + +import org.automation.database.DatabaseManager; +import org.automation.entities.ProductItem; + +class Log { + protected int id; + protected String timestamp; + protected String componentType; + protected int componentId; + protected String eventType; + protected String message; + + public Log(int id, String timestamp, String componentType, int componentId, String eventType, String message) { + this.id = id; + this.timestamp = timestamp; + this.componentType = componentType; + this.componentId = componentId; + this.eventType = eventType; + this.message = message; + } +} + +public class Logger { + private DatabaseManager db; + + public Logger(DatabaseManager db) { + this.db = db; + } + + public void logProduct(ProductItem item) { + + } + + public void log(String componentType, int componentId, String eventType, String message) { + System.out.println(componentType + " [" + componentId + "] " + eventType + ": " + message); + } + + public void logError(String error) { + + } + + public void clear() { + + } + + public void print() { + + } + + @Override + public String toString() { + return "Logger"; + } +} From bb1c82433f159a21d5bed8751774deaaf2613ac5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 24 Dec 2025 14:08:28 +0300 Subject: [PATCH 2/5] update sensor --- automation.sqlite | Bin 12288 -> 16384 bytes package-lock.json | 142 +++++++ package.json | 6 + pom.xml | 130 +++--- scripts/test_input.txt | 8 + .../Automation/Controllers/SensorManager.java | 199 ++++++++- .../org/Automation/TestSensorManager.java | 262 +++++++----- .../Automation/controllers/SensorManager.java | 361 +++++++---------- .../SimulationClock.observerclass.java | 12 - .../Automation/database/DatabaseManager.java | 40 +- .../org/Automation/engine/ClockObserver.java | 7 +- .../Automation/engine/SimulationClock.java | 223 +++++++---- .../java/org/Automation/entities/Sensor.java | 225 ++++++++--- .../entities/TemperatureSensor.java | 377 ++++++++++-------- .../org/Automation/entities/WeightSensor.java | 344 +++++++++------- .../Automation/repositories/Repository.java | 29 +- .../repositories/SensorRepository.java | 184 ++++++++- src/main/java/org/automation/SensorDemo.java | 40 ++ .../org/automation/SensorManagerDemo.java | 93 +++++ .../org/automation/TestInitialization.java | 35 ++ .../org/automation/database/RowMapper.java | 12 + 21 files changed, 1868 insertions(+), 861 deletions(-) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/test_input.txt delete mode 100644 src/main/java/org/Automation/controllers/SimulationClock.observerclass.java create mode 100644 src/main/java/org/automation/SensorDemo.java create mode 100644 src/main/java/org/automation/SensorManagerDemo.java create mode 100644 src/main/java/org/automation/TestInitialization.java create mode 100644 src/main/java/org/automation/database/RowMapper.java diff --git a/automation.sqlite b/automation.sqlite index 335fb0256e78fabc8807cab5b73bac781fb01998..3c306ec59877a74130b73b90c889949f0f0b7c49 100644 GIT binary patch literal 16384 zcmeI2&rj1}7{^;cwv}}S<%ct9UKM2#KV?f2X9PxRxuIt<9`#j(G)8%d6f00e*l5IEHYUaT|+dZJPO8Sf^xI;n9pY2 ze%&mpL^BQNp4(MXi)GEgnZ4X@ca5lIt>)&qE2mTQSq1fLhFT=rI_^&jPMN_$q~J;w zTQt_fsOAg({+1vaVM50w1`r+aII|WB}50gBsi? z#i=YU7v$WeJcnjnsxhq;7vlM*)}Da@e$jc>wN;%iu^-j&>KdklK6Kf9;OQT_88g%h zPEuujfe-X_b@9(7C#AYZO#5g$Ahvr@Omx7|e~xAlyo@bWwX}+A)zJ#BBV@;DM$y6* zY!Fe1+iP>W=!XUbfB+Bx0zd!=00AHX1b_e#00KY&2%KU9_WGZfesJ^y4F~`MAOHk_ z01yBIKmZ5;0U!VbfB+CUMFa%CIm)~HOoqisnd=MYIq8%1O1c^O6?qf+EN(}Jq{rfx zxFP1n?(nzpW_Ue39&T^@+V-xk+ICC$EqoE)3J-FeriCW3PS9wE?#2{GSC+?yMD8k$ zyT+#U@Ym>tIuQ_-ps!FTz&E!=K2eJnC;e zw6CP|vyVB~nX!d^HJyt8sy(LT@FSMStN+RHc{u#C^N9iY8*sCPM R?6G4_Cv`N&vnh{0mp^Ui_2~cr delta 61 zcmZo@U~EX3AT7wqz`(!^#4x}(QO6i4s8`m>3lw7F4`bk8!5_9+P~aKg<}m(s0-IR` H{_q0;D5eY2 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f691ac6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,142 @@ +{ + "name": "OOP-Mini-Factory-Automation-Simulation", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "prettier": "3.7.4", + "prettier-plugin-java": "2.7.7" + } + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/java-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-3.0.1.tgz", + "integrity": "sha512-sDIR7u9b7O2JViNUxiZRhnRz7URII/eE7g2B+BmGxDeS6Ex3OYAcCyz5oh0H4LQ+hL/BS8OJTz8apMy9xtGmrQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chevrotain": "11.0.3", + "chevrotain-allstar": "0.3.1", + "lodash": "4.17.21" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-java": { + "version": "2.7.7", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-2.7.7.tgz", + "integrity": "sha512-K3N2lrdKzx2FAi67E0UOTLKybX6iitAxYGuiv/emY8v6TzzGzoaKjmhaAyDKIH5iakFqdN+xUwWoauXnE2JZPA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "java-parser": "3.0.1" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..301bb72 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "devDependencies": { + "prettier": "3.7.4", + "prettier-plugin-java": "2.7.7" + } +} diff --git a/pom.xml b/pom.xml index 000a5de..59393c5 100644 --- a/pom.xml +++ b/pom.xml @@ -1,53 +1,87 @@ - 4.0.0 - - org.example - Mini-Factory-Automation-Simulation - 1.0-SNAPSHOT - - - 22 - 22 - UTF-8 - - - - - org.xerial - sqlite-jdbc - 3.46.0.0 - - - - org.openjfx - javafx-controls - 21 - - - - - org.openjfx - javafx-fxml - 21 - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - 25 - 25 - 25 - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + org.example + Mini-Factory-Automation-Simulation + 1.0-SNAPSHOT + + + + 22 + ${java.version} + ${java.version} + UTF-8 + + + org.automation.TestSensorManager + + + + + + org.xerial + sqlite-jdbc + 3.46.0.0 + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${maven.compiler.source} + ${maven.compiler.target} + ${project.build.sourceEncoding} + ${java.version} + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + ${main.class} + + + + + + diff --git a/scripts/test_input.txt b/scripts/test_input.txt new file mode 100644 index 0000000..69d88cf --- /dev/null +++ b/scripts/test_input.txt @@ -0,0 +1,8 @@ +1 +2 +LineA +20 +0.5 +25 +1 +0 diff --git a/src/main/java/org/Automation/Controllers/SensorManager.java b/src/main/java/org/Automation/Controllers/SensorManager.java index b4a97c4..707bc2d 100644 --- a/src/main/java/org/Automation/Controllers/SensorManager.java +++ b/src/main/java/org/Automation/Controllers/SensorManager.java @@ -1,5 +1,202 @@ package org.automation.controllers; +import org.automation.database.DatabaseManager; +import org.automation.entities.Sensor; +import org.automation.repositories.SensorRepository; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * SensorManager - Central controller for factory sensors + * - Add/remove sensors + * - Start/stop sensors + * - Bulk operations + * - Toggle automatic/control modes per sensor and in bulk + * - Graceful shutdown + */ public class SensorManager { - + + private final Map sensors = new ConcurrentHashMap<>(); + private final ExecutorService executor = Executors.newCachedThreadPool(); + private final DatabaseManager dbManager; + private final SensorRepository sensorRepo; + + public SensorManager() { + System.out.println("SensorManager initialized"); + // initialize database and repository + this.dbManager = new DatabaseManager(); + SensorRepository repo = null; + try { + if (this.dbManager.connect()) { + repo = new SensorRepository(this.dbManager); + repo.ensureTable(); + // load persisted sensors + List persisted = repo.findAll(); + for (Sensor s : persisted) { + sensors.putIfAbsent(s.getSensorId(), s); + System.out.println("Loaded sensor from DB: " + s.getSensorInfo()); + } + } + } catch (Exception e) { + System.err.println("Database init failed: " + e.getMessage()); + } + this.sensorRepo = repo; + } + + // ------------------------- + // Basic sensor management + // ------------------------- + public void addSensor(Sensor sensor) { + if (sensor == null) return; + sensors.putIfAbsent(sensor.getSensorId(), sensor); + System.out.println("Added sensor: " + sensor.getSensorInfo()); + if (sensorRepo != null) { + try { + sensorRepo.save(sensor); + } catch (Exception e) { + System.err.println("Failed to persist sensor: " + e.getMessage()); + } + } + } + + public boolean removeSensor(int sensorId) { + Sensor removed = sensors.remove(sensorId); + if (removed != null) { + System.out.println("Removed sensor: " + removed.getSensorInfo()); + if (sensorRepo != null) { + try { + sensorRepo.deleteById(sensorId); + } catch (Exception e) { + System.err.println("Failed to remove sensor from DB: " + e.getMessage()); + } + } + return true; + } else { + System.err.println("Sensor ID " + sensorId + " not found"); + return false; + } + } + + public Sensor findSensorById(int sensorId) { + return sensors.get(sensorId); + } + + // ------------------------- + // Start / Stop a specific sensor + // ------------------------- + public boolean startSensor(int sensorId) { + Sensor s = findSensorById(sensorId); + if (s == null) return false; + if (s.isActive()) return false; + try { + s.start(); + return true; + } catch (Exception e) { + System.err.println("Failed to start sensor " + sensorId + ": " + e.getMessage()); + return false; + } + } + + public boolean stopSensor(int sensorId) { + Sensor s = findSensorById(sensorId); + if (s == null) return false; + if (!s.isActive()) return false; + try { + s.stop(); + return true; + } catch (Exception e) { + System.err.println("Failed to stop sensor " + sensorId + ": " + e.getMessage()); + return false; + } + } + + // ------------------------- + // Bulk operations & queries + // ------------------------- + public void startAll() { + new ArrayList<>(sensors.values()).forEach(s -> { if (!s.isActive()) s.start(); }); + } + + public void stopAll() { + new ArrayList<>(sensors.values()).forEach(s -> { if (s.isActive()) s.stop(); }); + } + + public List listSensorInfo() { + List info = new ArrayList<>(); + for (Sensor s : sensors.values()) info.add(s.getSensorInfo()); + return info; + } + + public String getSensorInfo(int sensorId) { + Sensor s = findSensorById(sensorId); + return s == null ? null : s.getSensorInfo(); + } + + // ------------------------- + // Control / automatic toggles + // ------------------------- + public boolean setSensorAutomaticMode(int sensorId, boolean enabled) { + Sensor s = findSensorById(sensorId); + if (s == null) return false; + if (enabled) s.enableAutomaticMode(); else s.disableAutomaticMode(); + return true; + } + + public boolean setSensorControl(int sensorId, boolean enabled, double target, double tolerance) { + Sensor s = findSensorById(sensorId); + if (s == null) return false; + if (enabled) s.enableControl(target, tolerance); else s.disableControl(); + return true; + } + + public boolean setAllAutomaticMode(boolean enabled) { + new ArrayList<>(sensors.values()).forEach(s -> { if (enabled) s.enableAutomaticMode(); else s.disableAutomaticMode(); }); + return true; + } + + public boolean setAllControl(boolean enabled, double target, double tolerance) { + new ArrayList<>(sensors.values()).forEach(s -> { if (enabled) s.enableControl(target, tolerance); else s.disableControl(); }); + return true; + } + + // ------------------------- + // Async helpers + // ------------------------- + public void startSensorAsync(int sensorId) { + executor.submit(() -> startSensor(sensorId)); + } + + public void stopSensorAsync(int sensorId) { + executor.submit(() -> stopSensor(sensorId)); + } + + // ------------------------- + // Graceful shutdown + // ------------------------- + public void shutdown(long timeoutSeconds) { + // stop sensors first + stopAll(); + // shutdown executor + executor.shutdown(); + try { + if (!executor.awaitTermination(timeoutSeconds, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + executor.shutdownNow(); + Thread.currentThread().interrupt(); + } + try { + // shutdown the simulation clock to avoid lingering threads + try { org.automation.engine.SimulationClock.getInstance().shutdown(); } catch (Exception ignored) {} + if (dbManager != null) dbManager.disconnect(); + } catch (Exception ignored) {} + System.out.println("SensorManager shutdown complete"); + } } diff --git a/src/main/java/org/Automation/TestSensorManager.java b/src/main/java/org/Automation/TestSensorManager.java index 6116df9..a84cc1a 100644 --- a/src/main/java/org/Automation/TestSensorManager.java +++ b/src/main/java/org/Automation/TestSensorManager.java @@ -1,114 +1,170 @@ -package org.Automation; +package org.automation; + +import org.automation.controllers.SensorManager; +import org.automation.entities.Sensor; +import org.automation.entities.TemperatureSensor; +import org.automation.entities.WeightSensor; -import org.Automation.controllers.SensorManager; -import org.Automation.entities.*; import java.util.List; +import java.util.Scanner; public class TestSensorManager { + + private static final Scanner scanner = new Scanner(System.in); + public static void main(String[] args) { - System.out.println("=== SensorManager Test Suite ===\n"); - - // Step 1: Create SensorManager - System.out.println("Step 1: Creating SensorManager"); + System.out.println("=== Interactive SensorManager Test ===\n"); SensorManager manager = new SensorManager(); - System.out.println("Initial sensor count: " + manager.getSensorCount()); - - // Step 2: Add Temperature Sensors - System.out.println("\nStep 2: Adding Temperature Sensors"); - TemperatureSensor temp1 = new TemperatureSensor("Temperature", "Factory Floor A", "Active", 20.0, 80.0, "°C"); - TemperatureSensor temp2 = new TemperatureSensor("Temperature", "Factory Floor B", "Active", 15.0, 75.0, "°C"); - - manager.addSensor(temp1); - manager.addSensor(temp2); - System.out.println("Sensor count after adding temperature sensors: " + manager.getSensorCount()); - - // Step 3: Add Weight Sensors - System.out.println("\nStep 3: Adding Weight Sensors"); - WeightSensor weight1 = new WeightSensor("Weight", "Conveyor Belt 1", "Active", 50.0, 100.0, "kg"); - WeightSensor weight2 = new WeightSensor("Weight", "Conveyor Belt 2", "Active", 10.0, 50.0, "kg"); - - manager.addSensor(weight1); - manager.addSensor(weight2); - System.out.println("Sensor count after adding weight sensors: " + manager.getSensorCount()); - - // Step 4: Display all sensors - manager.displayAllSensors(); - - // Step 5: Test sensor reading - System.out.println("\nStep 5: Reading all sensor data (3 times)"); - for (int i = 1; i <= 3; i++) { - System.out.println("\n--- Reading #" + i + " ---"); - manager.readAllSensorData(); - - // Check for alerts - List alerts = manager.getAlertSensors(); - if (!alerts.isEmpty()) { - System.out.println("\n⚠️ ALERTS DETECTED:"); - for (Sensor sensor : alerts) { - System.out.println(" " + sensor); - } - } - + + boolean running = true; + while (running) { + printMenu(); + int choice = readInt("Select option: "); try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); + switch (choice) { + case 1 -> listSensors(manager); + case 2 -> addTemperatureSensor(manager); + case 3 -> addWeightSensor(manager); + case 4 -> startSensor(manager); + case 5 -> stopSensor(manager); + case 6 -> readSensorValue(manager); + case 7 -> toggleAutomatic(manager); + case 8 -> setControl(manager); + case 9 -> removeSensor(manager); + case 10 -> findById(manager); + case 0 -> { + System.out.println("Shutting down..."); + manager.stopAll(); + manager.shutdown(5); + running = false; + } + default -> System.out.println("Unknown option"); + } + } catch (Exception e) { + System.err.println("Operation failed: " + e.getMessage()); } + System.out.println(); + } + + System.out.println("Goodbye."); + } + + private static void printMenu() { + System.out.println("Menu:"); + System.out.println(" 1) List sensors"); + System.out.println(" 2) Add TemperatureSensor"); + System.out.println(" 3) Add WeightSensor"); + System.out.println(" 4) Start sensor"); + System.out.println(" 5) Stop sensor"); + System.out.println(" 6) Read sensor value"); + System.out.println(" 7) Toggle automatic mode"); + System.out.println(" 8) Set control (enable/disable + params)"); + System.out.println(" 9) Remove sensor"); + System.out.println("10) Find sensor by ID"); + System.out.println(" 0) Exit (stop & shutdown)"); + } + + private static int readInt(String prompt) { + System.out.print(prompt); + try { + return Integer.parseInt(scanner.nextLine().trim()); + } catch (NumberFormatException e) { + return -1; + } + } + + private static double readDouble(String prompt) { + System.out.print(prompt); + try { + return Double.parseDouble(scanner.nextLine().trim()); + } catch (NumberFormatException e) { + return Double.NaN; } - - // Step 6: Test search by type - System.out.println("\n\nStep 6: Testing search by type"); - List tempSensors = manager.findSensorsByType("Temperature"); - System.out.println("Temperature sensors found: " + tempSensors.size()); - tempSensors.forEach(s -> System.out.println(" " + s)); - - List weightSensors = manager.findSensorsByType("Weight"); - System.out.println("Weight sensors found: " + weightSensors.size()); - weightSensors.forEach(s -> System.out.println(" " + s)); - - // Step 7: Test search by location - System.out.println("\nStep 7: Testing search by location"); - List floorASensors = manager.findSensorsByLocation("Factory Floor A"); - System.out.println("Sensors at Factory Floor A: " + floorASensors.size()); - floorASensors.forEach(s -> System.out.println(" " + s)); - - // Step 8: Test find by ID - System.out.println("\nStep 8: Testing find by ID"); - Sensor found = manager.findSensorById(2); - if (found != null) { - System.out.println("Found sensor with ID 2: " + found); - } else { - System.out.println("Sensor with ID 2 not found"); + } + + private static void listSensors(SensorManager manager) { + List infos = manager.listSensorInfo(); + if (infos.isEmpty()) { + System.out.println("No sensors registered."); + return; } - - // Step 9: Test calibration - System.out.println("\nStep 9: Testing sensor calibration"); - manager.calibrateSensor(1); - manager.calibrateAllSensors(); - - // Step 10: Test activation/deactivation - System.out.println("\nStep 10: Testing activation/deactivation"); - manager.deactivateAllSensors(); - manager.printSensorStatus(); - - manager.activateAllSensors(); - manager.printSensorStatus(); - - // Step 11: Test sensor removal - System.out.println("\nStep 11: Testing sensor removal"); - System.out.println("Removing sensor with ID 3"); - boolean removed = manager.removeSensor(3); - System.out.println("Removal successful: " + removed); - System.out.println("Final sensor count: " + manager.getSensorCount()); - - manager.displayAllSensors(); - - // Step 12: Test edge cases - System.out.println("\nStep 12: Testing edge cases"); - manager.addSensor(null); // Should handle null - manager.removeSensor(999); // Should handle non-existent ID - manager.calibrateSensor(999); // Should handle non-existent ID - - System.out.println("\n=== Test Complete ==="); + for (String info : infos) System.out.println(info); + } + + private static void addTemperatureSensor(SensorManager manager) { + System.out.println("Add TemperatureSensor:"); + String location = prompt("Location: "); + double start = readDouble("Start threshold: "); + double tol = readDouble("Tolerance: "); + double target = readDouble("Target temperature: "); + TemperatureSensor t = new TemperatureSensor("Temperature", location, "Init", start, tol, target, "°C"); + manager.addSensor(t); + } + + private static void addWeightSensor(SensorManager manager) { + System.out.println("Add WeightSensor:"); + String location = prompt("Location: "); + double initial = readDouble("Initial weight: "); + double capacity = readDouble("Capacity: "); + WeightSensor w = new WeightSensor("Weight", location, "Init", initial, capacity, "kg"); + manager.addSensor(w); + } + + private static void startSensor(SensorManager manager) { + int id = readInt("Sensor ID to start: "); + boolean ok = manager.startSensor(id); + System.out.println(ok ? "Started" : "Start failed"); + } + + private static void stopSensor(SensorManager manager) { + int id = readInt("Sensor ID to stop: "); + boolean ok = manager.stopSensor(id); + System.out.println(ok ? "Stopped" : "Stop failed"); + } + + private static void readSensorValue(SensorManager manager) { + int id = readInt("Sensor ID to read: "); + Sensor s = manager.findSensorById(id); + if (s == null) { System.out.println("Not found"); return; } + s.readValue(); + } + + private static void toggleAutomatic(SensorManager manager) { + int id = readInt("Sensor ID: "); + int v = readInt("0 = disable, 1 = enable automatic: "); + boolean enabled = v == 1; + boolean ok = manager.setSensorAutomaticMode(id, enabled); + System.out.println(ok ? "Updated" : "Failed"); + } + + private static void setControl(SensorManager manager) { + int id = readInt("Sensor ID: "); + int v = readInt("0 = disable control, 1 = enable: "); + if (v == 0) { + boolean ok = manager.setSensorControl(id, false, 0, 0); + System.out.println(ok ? "Control disabled" : "Failed"); + return; + } + double target = readDouble("Target value: "); + double tol = readDouble("Tolerance: "); + boolean ok = manager.setSensorControl(id, true, target, tol); + System.out.println(ok ? "Control set" : "Failed"); + } + + private static void removeSensor(SensorManager manager) { + int id = readInt("Sensor ID to remove: "); + boolean ok = manager.removeSensor(id); + System.out.println(ok ? "Removed" : "Remove failed"); + } + + private static void findById(SensorManager manager) { + int id = readInt("Sensor ID: "); + Sensor s = manager.findSensorById(id); + if (s == null) System.out.println("Not found"); else System.out.println(s.getSensorInfo()); + } + + private static String prompt(String p) { + System.out.print(p); + return scanner.nextLine().trim(); } -} \ No newline at end of file +} diff --git a/src/main/java/org/Automation/controllers/SensorManager.java b/src/main/java/org/Automation/controllers/SensorManager.java index 1254c0a..707bc2d 100644 --- a/src/main/java/org/Automation/controllers/SensorManager.java +++ b/src/main/java/org/Automation/controllers/SensorManager.java @@ -1,261 +1,202 @@ -package org.Automation.controllers; +package org.automation.controllers; + +import org.automation.database.DatabaseManager; +import org.automation.entities.Sensor; +import org.automation.repositories.SensorRepository; -import org.Automation.entities.Sensor; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; /** - * SensorManager - Manages all sensors in the factory - * Responsibilities: + * SensorManager - Central controller for factory sensors * - Add/remove sensors - * - Update all sensor values - * - Monitor sensor status - * - Find sensors by ID or type + * - Start/stop sensors + * - Bulk operations + * - Toggle automatic/control modes per sensor and in bulk + * - Graceful shutdown */ public class SensorManager { - private Map sensors; - private int totalSensors; - private int activeSensors; - - // Simulation clock for sensor manager - private SimulationClock simulationClock; - public SensorManager() { - this.sensors = new HashMap<>(); - - // Create simulation clock for sensor manager - this.simulationClock = new SimulationClock("SensorManager", 2000) { - @Override - protected void tick() { - super.tick(); - readValue(); - } - }; - - System.out.println("SensorManager initialized with empty sensor list"); - } - - // SimulationClock delegation - public void start() { simulationClock.start(); } - public void stop() { simulationClock.stop(); } - public boolean isRunning() { return simulationClock.isRunning(); } - public int getCurrentTime() { return simulationClock.getCurrentTime(); } - public void setInterval(int intervalMs) { simulationClock.setInterval(intervalMs); } - - // Utility methods - public int getSensorCount() { - return totalSensors; - } + private final Map sensors = new ConcurrentHashMap<>(); + private final ExecutorService executor = Executors.newCachedThreadPool(); + private final DatabaseManager dbManager; + private final SensorRepository sensorRepo; - public Map getAllSensors() { - return new HashMap<>(sensors); - } - - private void updateCounts() { - totalSensors = sensors.size(); - activeSensors = 0; - for (Sensor sensor : sensors.values()) { - if (sensor.isActive()) { - activeSensors++; + public SensorManager() { + System.out.println("SensorManager initialized"); + // initialize database and repository + this.dbManager = new DatabaseManager(); + SensorRepository repo = null; + try { + if (this.dbManager.connect()) { + repo = new SensorRepository(this.dbManager); + repo.ensureTable(); + // load persisted sensors + List persisted = repo.findAll(); + for (Sensor s : persisted) { + sensors.putIfAbsent(s.getSensorId(), s); + System.out.println("Loaded sensor from DB: " + s.getSensorInfo()); + } } + } catch (Exception e) { + System.err.println("Database init failed: " + e.getMessage()); } + this.sensorRepo = repo; } - public void updateValue() { - readValue(); - } - - public Object getValue() { - return getAllSensors(); - } - - - // Core management methods + // ------------------------- + // Basic sensor management + // ------------------------- public void addSensor(Sensor sensor) { - if (sensor == null) { - System.out.println("Error: Cannot add null sensor"); - return; - } - - Integer key = sensor.getSensorId(); - - if (sensors.containsKey(key)) { - System.out.println("Sensor with ID " + key + " already exists"); - return; - } - - sensors.put(key, sensor); - totalSensors++; - - if (sensor.isActive()) { - activeSensors++; + if (sensor == null) return; + sensors.putIfAbsent(sensor.getSensorId(), sensor); + System.out.println("Added sensor: " + sensor.getSensorInfo()); + if (sensorRepo != null) { + try { + sensorRepo.save(sensor); + } catch (Exception e) { + System.err.println("Failed to persist sensor: " + e.getMessage()); + } } - System.out.println("Added sensor: " + sensor); } public boolean removeSensor(int sensorId) { - if (!sensors.containsKey(sensorId)) { - System.out.println("Sensor with ID " + sensorId + " not found"); - return false; - } Sensor removed = sensors.remove(sensorId); - totalSensors--; - if (removed.isActive()) { - activeSensors--; - } - System.out.println("Removed sensor: " + removed); - return true; - } - - public void activateAllSensors() { - System.out.println("\n=== Activating All Sensors ==="); - sensors.values().forEach(sensor -> { - sensor.activateSensor(); - if (sensor.isActive()) { - activeSensors++; - } - }); - } - - public void deactivateAllSensors() { - System.out.println("\n=== Deactivating All Sensors ==="); - sensors.values().forEach(sensor -> { - sensor.deactivateSensor(); - if (!sensor.isActive()) { - activeSensors--; + if (removed != null) { + System.out.println("Removed sensor: " + removed.getSensorInfo()); + if (sensorRepo != null) { + try { + sensorRepo.deleteById(sensorId); + } catch (Exception e) { + System.err.println("Failed to remove sensor from DB: " + e.getMessage()); + } } - }); + return true; + } else { + System.err.println("Sensor ID " + sensorId + " not found"); + return false; + } } - // Search methods public Sensor findSensorById(int sensorId) { - return sensors.get(sensorId); + return sensors.get(sensorId); + } + + // ------------------------- + // Start / Stop a specific sensor + // ------------------------- + public boolean startSensor(int sensorId) { + Sensor s = findSensorById(sensorId); + if (s == null) return false; + if (s.isActive()) return false; + try { + s.start(); + return true; + } catch (Exception e) { + System.err.println("Failed to start sensor " + sensorId + ": " + e.getMessage()); + return false; + } } - public List findSensorsByType(String sensorType) { - return sensors.get(sensorType); + public boolean stopSensor(int sensorId) { + Sensor s = findSensorById(sensorId); + if (s == null) return false; + if (!s.isActive()) return false; + try { + s.stop(); + return true; + } catch (Exception e) { + System.err.println("Failed to stop sensor " + sensorId + ": " + e.getMessage()); + return false; + } } - // Operations methods - public void readValue() { - System.out.println("\n=== Reading All Sensor Data ==="); - sensors.values().forEach(sensor -> { - sensor.readSensorData(); - System.out.println(" " + sensor); - }); - updateCounts(); + // ------------------------- + // Bulk operations & queries + // ------------------------- + public void startAll() { + new ArrayList<>(sensors.values()).forEach(s -> { if (!s.isActive()) s.start(); }); } - public void updateAllSensors() { - System.out.println("\n=== Updating All Sensors ==="); - sensors.forEach((id,sensor) -> { - sensor.updateValue(); - System.out.println(" " + sensor); - }); - updateCounts(); + public void stopAll() { + new ArrayList<>(sensors.values()).forEach(s -> { if (s.isActive()) s.stop(); }); } - public void calibrateAllSensors() { - System.out.println("\n=== Calibrating All Sensors ==="); - sensors.values().forEach(Sensor::calibrateSensor); + public List listSensorInfo() { + List info = new ArrayList<>(); + for (Sensor s : sensors.values()) info.add(s.getSensorInfo()); + return info; } - public void calibrateSensor(int sensorId) { - Sensor sensor = findSensorById(sensorId); - if (sensor != null) { - sensor.calibrateSensor(); - } else { - System.out.println("Sensor with ID " + sensorId + " not found for calibration"); - } + public String getSensorInfo(int sensorId) { + Sensor s = findSensorById(sensorId); + return s == null ? null : s.getSensorInfo(); } - // Status monitoring - public List getAlertSensors() { - return sensors.values().stream() - .filter(sensor -> sensor.getStatus().contains("Alert")) - .toList(); + // ------------------------- + // Control / automatic toggles + // ------------------------- + public boolean setSensorAutomaticMode(int sensorId, boolean enabled) { + Sensor s = findSensorById(sensorId); + if (s == null) return false; + if (enabled) s.enableAutomaticMode(); else s.disableAutomaticMode(); + return true; } - public List getActiveSensors() { - return sensors.values().stream() - .filter(Sensor::isActive) - .toList(); + public boolean setSensorControl(int sensorId, boolean enabled, double target, double tolerance) { + Sensor s = findSensorById(sensorId); + if (s == null) return false; + if (enabled) s.enableControl(target, tolerance); else s.disableControl(); + return true; } - public List getInactiveSensors() { - return sensors.values().stream() - .filter(sensor -> !sensor.isActive()) - .toList(); + public boolean setAllAutomaticMode(boolean enabled) { + new ArrayList<>(sensors.values()).forEach(s -> { if (enabled) s.enableAutomaticMode(); else s.disableAutomaticMode(); }); + return true; } - // Display methods - public void displayAllSensors() { - System.out.println("\n=== All Sensors ==="); - if (sensors.isEmpty()) { - System.out.println(" No sensors registered"); - } else { - sensors.values().forEach(sensor -> System.out.println(" " + sensor)); - } + public boolean setAllControl(boolean enabled, double target, double tolerance) { + new ArrayList<>(sensors.values()).forEach(s -> { if (enabled) s.enableControl(target, tolerance); else s.disableControl(); }); + return true; } - public void printSensorStatus() { - updateCounts(); - System.out.println("\n=== Sensor Status Summary ==="); - System.out.println("Total sensors: " + totalSensors); - System.out.println("Active sensors: " + activeSensors); - System.out.println("Inactive sensors: " + (totalSensors - activeSensors)); - System.out.println("Alert sensors: " + getAlertSensors().size()); + // ------------------------- + // Async helpers + // ------------------------- + public void startSensorAsync(int sensorId) { + executor.submit(() -> startSensor(sensorId)); } - - // Temperature control methods - public void enableTemperatureControl(int sensorId, double targetTemp, double threshold) { - Sensor sensor = findSensorById(sensorId); - if (sensor instanceof TemperatureSensor) { - ((TemperatureSensor) sensor).enableTemperatureControl(targetTemp, threshold); - } else { - System.out.println("Sensor " + sensorId + " is not a temperature sensor"); - } - } - - public void enableAllTemperatureControl(double targetTemp, double threshold) { - List tempSensors = findSensorsByType("Temperature"); - for (Sensor sensor : tempSensors) { - ((TemperatureSensor) sensor).enableTemperatureControl(targetTemp, threshold); - } + public void stopSensorAsync(int sensorId) { + executor.submit(() -> stopSensor(sensorId)); } - - // Weight control methods - public void enableWeightControl(int sensorId, double capacity) { - Sensor sensor = findSensorById(sensorId); - if (sensor instanceof WeightSensor) { - ((WeightSensor) sensor).enableWeightControl(capacity); - } else { - System.out.println("Sensor " + sensorId + " is not a weight sensor"); - } - } - - public boolean addWeightToSensor(int sensorId, double weight) { - Sensor sensor = findSensorById(sensorId); - if (sensor instanceof WeightSensor) { - return ((WeightSensor) sensor).addWeight(weight); - } - System.out.println("Sensor " + sensorId + " is not a weight sensor"); - return false; - } - - public void printControlStatus() { - System.out.println("\n=== Control Status ==="); - for (Sensor sensor : sensors.values()) { - if (sensor instanceof TemperatureSensor) { - TemperatureSensor temp = (TemperatureSensor) sensor; - System.out.println("Temp Sensor " + sensor.getSensorId() + ": " + - temp.getTemperatureStatus() + " (" + temp.getCurrentValue() + "°C)"); - } else if (sensor instanceof WeightSensor) { - WeightSensor weight = (WeightSensor) sensor; - System.out.println("Weight Sensor " + sensor.getSensorId() + ": " + - weight.getWeightStatus() + " (" + weight.getCurrentValue() + "kg)"); + + // ------------------------- + // Graceful shutdown + // ------------------------- + public void shutdown(long timeoutSeconds) { + // stop sensors first + stopAll(); + // shutdown executor + executor.shutdown(); + try { + if (!executor.awaitTermination(timeoutSeconds, TimeUnit.SECONDS)) { + executor.shutdownNow(); } + } catch (InterruptedException e) { + executor.shutdownNow(); + Thread.currentThread().interrupt(); } + try { + // shutdown the simulation clock to avoid lingering threads + try { org.automation.engine.SimulationClock.getInstance().shutdown(); } catch (Exception ignored) {} + if (dbManager != null) dbManager.disconnect(); + } catch (Exception ignored) {} + System.out.println("SensorManager shutdown complete"); } } diff --git a/src/main/java/org/Automation/controllers/SimulationClock.observerclass.java b/src/main/java/org/Automation/controllers/SimulationClock.observerclass.java deleted file mode 100644 index 6ef253a..0000000 --- a/src/main/java/org/Automation/controllers/SimulationClock.observerclass.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.Automation.controllers; - -import java.time.LocalDateTime; - -/** - * ClockObserver - Interface for receiving simulation clock notifications - */ -public interface ClockObserver { - void onTick(LocalDateTime currentTime); -} - - diff --git a/src/main/java/org/Automation/database/DatabaseManager.java b/src/main/java/org/Automation/database/DatabaseManager.java index c8c1010..3751e32 100644 --- a/src/main/java/org/Automation/database/DatabaseManager.java +++ b/src/main/java/org/Automation/database/DatabaseManager.java @@ -9,8 +9,7 @@ public final class DatabaseManager { private final String url = "jdbc:sqlite:automation.sqlite"; public boolean connect() { - if (connection != null) - return true; + if (connection != null) return true; try { connection = DriverManager.getConnection(url); @@ -58,6 +57,43 @@ private ResultSet executeQuery(String sql, Object[] params) { } } + /** + * Safe query helper that maps rows using the provided RowMapper and + * ensures PreparedStatement and ResultSet are closed properly. + */ + public java.util.List query(String tableName, String where, Object[] params, org.automation.database.RowMapper mapper) { + String sql = "SELECT * FROM " + tableName + (where != null && !where.trim().isEmpty() ? " WHERE " + where : ""); + java.util.List result = new java.util.ArrayList<>(); + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + bindParams(pstmt, params); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + result.add(mapper.mapRow(rs)); + } + } + } catch (SQLException e) { + throw new RuntimeException("Query execution failed: " + e.getMessage(), e); + } + return result; + } + + public T queryOne(String tableName, String where, Object[] params, org.automation.database.RowMapper mapper) { + java.util.List list = query(tableName, where, params, mapper); + return list.isEmpty() ? null : list.get(0); + } + + /** + * Execute DDL statements such as CREATE TABLE. + */ + public boolean executeDDL(String sql) { + try (Statement stmt = connection.createStatement()) { + stmt.execute(sql); + return true; + } catch (SQLException e) { + throw new RuntimeException("DDL execution failed: " + e.getMessage(), e); + } + } + // ---------------- Helper method for INSERT, UPDATE, DELETE ---------------- private boolean executeMutator(String sql, Object[] params) { try (PreparedStatement pstmt = connection.prepareStatement(sql)) { diff --git a/src/main/java/org/Automation/engine/ClockObserver.java b/src/main/java/org/Automation/engine/ClockObserver.java index 712cd4b..9777b4b 100644 --- a/src/main/java/org/Automation/engine/ClockObserver.java +++ b/src/main/java/org/Automation/engine/ClockObserver.java @@ -1,5 +1,8 @@ package org.automation.engine; -public class ClockObserver { - +import java.time.LocalDateTime; + +public interface ClockObserver { + void onTick(LocalDateTime currentTime); } + diff --git a/src/main/java/org/Automation/engine/SimulationClock.java b/src/main/java/org/Automation/engine/SimulationClock.java index 59becdf..8147d40 100644 --- a/src/main/java/org/Automation/engine/SimulationClock.java +++ b/src/main/java/org/Automation/engine/SimulationClock.java @@ -1,94 +1,143 @@ package org.automation.engine; + import java.time.LocalDateTime; -//import java.time.Duration; -import java.util.ArrayList; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.time.temporal.ChronoUnit; + +/** + * SimulationClock + * + * - Singleton clock used by sensors + * - Notifies registered ClockObserver instances once per simulated second + * - Exposes getTickIntervalMs() for sensors to adapt increments + */ public class SimulationClock { - //Instance Variables - private static SimulationClock instance; - - private LocalDateTime simTime; - private LocalDateTime lastNotificationDate; - private int speedFactor=1; - private boolean ispaused=false; - - private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();//for the entire system to work on the same time - private final int Tick_Per_MS=50;// well we set it into a task per 50 milliseconds, meaning 20 times in a second - - private ArrayList observers = new ArrayList(); - - private SimulationClock(){ - this.simTime= LocalDateTime.now(); - this.lastNotificationDate=this.simTime; - - startIntegralTimer(); - } - - //Instance Methods - public static synchronized SimulationClock getInstance() { // used to create a single instance for all tasks - if(instance != null) { - instance= new SimulationClock(); - } - return instance; - } - - private void startIntegralTimer() { - scheduler.scheduleAtFixedRate(()->{ - if(!ispaused) { - tick(); - }} , 0, Tick_Per_MS, TimeUnit.MILLISECONDS); - } - - private void tick() { - - long MillsToAdd = Tick_Per_MS * speedFactor; - - simTime = simTime.plusNanos(MillsToAdd*1_000_000); - - long secondsAfterPreviousNotification = ChronoUnit.SECONDS.between(simTime, lastNotificationDate); - - if(secondsAfterPreviousNotification >=1) { - notifyOthers(); - - lastNotificationDate = lastNotificationDate.plusSeconds(secondsAfterPreviousNotification); - - } - - } - - public synchronized void register(ClockObservers observer) { - observers.add(observer); - } - - private synchronized void notifyOthers() { - for(ClockObservers observer : observers) { - observer.onTick(simTime); - } - - } - - public void start() { - ispaused=false; - System.out.println("The Simulation has been started at time: \n"+ simTime); - } - - public void stop() { - ispaused=true; - System.out.println("The Simulation has been stopped at time: \n"+ simTime); - } - - public void setSpeedFactor(int speed) { - speedFactor=speed; - System.out.println("The Simulation speed has been intiated into: \n"+ speedFactor); - } - - public LocalDateTime getCurrentTime() { - return simTime; - } - public interface ClockObservers{ - void onTick(LocalDateTime currentTime); - } + + // Singleton instance + private static SimulationClock instance; + + // Simulation time state + private LocalDateTime simTime; + private LocalDateTime lastNotificationDate; + + // Speed and pause control + private volatile int speedFactor = 1; + private volatile boolean isPaused = false; + + // Scheduler and tick configuration + private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r, "SimulationClock"); + t.setDaemon(true); + return t; + }); + private static final int TICK_PER_MS = 50; // base tick in milliseconds + + // Observers (thread-safe for iteration) + private final CopyOnWriteArrayList observers = new CopyOnWriteArrayList<>(); + + private SimulationClock() { + this.simTime = LocalDateTime.now(); + this.lastNotificationDate = this.simTime; + startIntegralTimer(); + } + + public static synchronized SimulationClock getInstance() { + if (instance == null) { + instance = new SimulationClock(); + } + return instance; + } + + private void startIntegralTimer() { + scheduler.scheduleAtFixedRate(() -> { + if (!isPaused) { + tick(); + } + }, 0, TICK_PER_MS, TimeUnit.MILLISECONDS); + } + + private void tick() { + long millisToAdd = (long) TICK_PER_MS * speedFactor; + simTime = simTime.plusNanos(millisToAdd * 1_000_000L); + + long secondsAfterPreviousNotification = ChronoUnit.SECONDS.between(lastNotificationDate, simTime); + if (secondsAfterPreviousNotification >= 1) { + notifyObservers(); + lastNotificationDate = lastNotificationDate.plusSeconds(secondsAfterPreviousNotification); + } + } + + public synchronized void register(ClockObserver observer) { + if (observer != null && !observers.contains(observer)) { + observers.add(observer); + } + } + + public synchronized void unregister(ClockObserver observer) { + observers.remove(observer); + } + + private void notifyObservers() { + for (ClockObserver observer : observers) { + try { + observer.onTick(simTime); + } catch (Throwable t) { + // keep clock running even if an observer throws + t.printStackTrace(); + } + } + } + + public synchronized void start() { + isPaused = false; + System.out.println("The Simulation has been started at time:\n" + simTime); + } + + public synchronized void stop() { + isPaused = true; + System.out.println("The Simulation has been stopped at time:\n" + simTime); + } + + public void setSpeedFactor(int speed) { + if (speed <= 0) throw new IllegalArgumentException("Speed must be > 0"); + this.speedFactor = speed; + System.out.println("Simulation speed set to: " + speedFactor); + } + + public LocalDateTime getCurrentTime() { + return simTime; + } + + /** + * Method expected by sensors: returns effective tick interval in milliseconds. + */ + public int getTickIntervalMs() { + return TICK_PER_MS * speedFactor; + } + + /** + * Graceful shutdown for the scheduler (call at application exit). + */ + public synchronized void shutdown() { + try { + // request orderly shutdown + scheduler.shutdown(); + // wait briefly for tasks to finish + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + // force shutdown if not finished + scheduler.shutdownNow(); + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + System.err.println("SimulationClock did not terminate"); + } + } + } catch (InterruptedException ie) { + scheduler.shutdownNow(); + Thread.currentThread().interrupt(); + } catch (Throwable t) { + t.printStackTrace(); + } + } } diff --git a/src/main/java/org/Automation/entities/Sensor.java b/src/main/java/org/Automation/entities/Sensor.java index 571ee65..de1769b 100644 --- a/src/main/java/org/Automation/entities/Sensor.java +++ b/src/main/java/org/Automation/entities/Sensor.java @@ -1,81 +1,198 @@ package org.automation.entities; +import java.time.LocalDateTime; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Abstract Sensor base class. + * - Centralizes lifecycle bookkeeping and status management + * - Final template method for interval -> increments mapping + * - Provides lightweight automatic/control flags and APIs used by concrete sensors + * + * Thread-safety: + * - start()/stop() are synchronized to protect lifecycle transitions. + * - primaryIncrement and coolingRate are volatile to ensure visibility across threads. + */ public abstract class Sensor { - private static int idCounter = 0; - - protected int sensorId; - protected String sensorType; + private static final AtomicInteger ID_GEN = new AtomicInteger(1); + + // identity / metadata + protected final int sensorId; + protected final String sensorType; protected String location; protected String status; - protected double currentValue; - protected boolean isActive; - // Constructor with auto-increment ID - public Sensor(String sensorType, String location, String status) { - this.sensorId = ++idCounter; + // lifecycle + protected volatile boolean isActive; + protected LocalDateTime startTime; + protected LocalDateTime stopTime; + + // shared, clock-driven increments + protected volatile double primaryIncrement; + protected volatile double coolingRate; + + // control / automation flags (default: automatic and control enabled) + protected volatile boolean automaticMode = true; + protected volatile boolean controlEnabled = true; + protected volatile double controlTarget = 0.0; + protected volatile double controlTolerance = 0.0; + + protected Sensor(String sensorType, String location, String status) { + this.sensorId = ID_GEN.getAndIncrement(); this.sensorType = sensorType; this.location = location; this.status = status; this.isActive = false; - this.currentValue = 0.0; + this.primaryIncrement = getBaselineIncrement(); + this.coolingRate = getBaselineCooling(); } - // Constructor with manual ID - public Sensor(int sensorId, String sensorType, String location, String status) { - this.sensorId = sensorId; + /** + * Construct sensor with a forced id (used when loading from persistent storage). + * Ensures the global ID generator is advanced past the forced id to avoid collisions. + */ + protected Sensor(int forcedId, String sensorType, String location, String status) { + this.sensorId = forcedId; this.sensorType = sensorType; this.location = location; this.status = status; this.isActive = false; - this.currentValue = 0.0; - if (sensorId > idCounter) { - idCounter = sensorId; + this.primaryIncrement = getBaselineIncrement(); + this.coolingRate = getBaselineCooling(); + // advance ID generator to avoid future collisions + int next = forcedId + 1; + while (true) { + int cur = ID_GEN.get(); + if (cur >= next) break; + if (ID_GEN.compareAndSet(cur, next)) break; } } - // Abstract methods from diagram - public abstract void readValue(); - public abstract void calibrateSensor(); - public abstract boolean validateReading(); - public abstract void sendAlert(); - public abstract void updateValue(); - public abstract Object getValue(); - - // Concrete methods - public void activateSensor() { - this.isActive = true; - this.status = "Active"; - System.out.println("Sensor " + sensorId + " activated"); - } - - public void deactivateSensor() { - this.isActive = false; - this.status = "Inactive"; - System.out.println("Sensor " + sensorId + " deactivated"); + // ----------------------- + // Lifecycle (centralized) + // ----------------------- + public synchronized void start() { + if (isActive) throw new IllegalStateException("Sensor " + sensorId + " is already active."); + isActive = true; + startTime = LocalDateTime.now(); + stopTime = null; + setStatus("Active"); + onStart(); } - - public void updateStatus(String newStatus) { - this.status = newStatus; + + public synchronized void stop() { + if (!isActive) return; + isActive = false; + stopTime = LocalDateTime.now(); + onStop(); + setStatus("Stopped"); } - - // Getters and Setters + + protected void activateSensor() { isActive = true; setStatus("Active"); } + protected void deactivateSensor() { isActive = false; setStatus("Paused"); } + + public boolean isActive() { return isActive; } public int getSensorId() { return sensorId; } public String getSensorType() { return sensorType; } public String getLocation() { return location; } public String getStatus() { return status; } - public double getCurrentValue() { return currentValue; } - public boolean isActive() { return isActive; } - - public void setSensorId(int sensorId) { this.sensorId = sensorId; } - public void setSensorType(String sensorType) { this.sensorType = sensorType; } - public void setLocation(String location) { this.location = location; } - public void setStatus(String status) { this.status = status; } - public void setCurrentValue(double currentValue) { this.currentValue = currentValue; } - public void setActive(boolean active) { this.isActive = active; } - - @Override - public String toString() { - return "Sensor{id=" + sensorId + ", type=" + sensorType + ", location=" + location + - ", status=" + status + ", value=" + currentValue + ", active=" + isActive + "}"; + + // ----------------------- + // Status helpers + // ----------------------- + protected synchronized void setStatus(String newStatus) { + this.status = newStatus; + } + + /** + * Update status after a read. Keeps statuses consistent across sensors. + * @param valid whether the last reading validated successfully + */ + protected synchronized void updateStatusAfterRead(boolean valid) { + if (!isActive) setStatus("Stopped"); + else if (!valid) setStatus("Error"); + else setStatus("OK"); + } + + // ----------------------- + // Control / automation API + // ----------------------- + public void enableAutomaticMode() { this.automaticMode = true; setStatus("AutoMode"); } + public void disableAutomaticMode() { this.automaticMode = false; setStatus("ManualMode"); } + public boolean isAutomaticMode() { return automaticMode; } + + public void enableControl(double target, double tolerance) { + this.controlTarget = target; + this.controlTolerance = tolerance; + this.controlEnabled = true; + setStatus("ControlEnabled"); + } + + public void disableControl() { + this.controlEnabled = false; + setStatus("ControlDisabled"); + } + + public boolean isControlEnabled() { return controlEnabled; } + public double getControlTarget() { return controlTarget; } + public double getControlTolerance() { return controlTolerance; } + + // ----------------------- + // Interval -> increment mapping (single place) + // ----------------------- + /** + * Final template method mapping clock interval -> increments. + */ + public final void setSimulationInterval(int intervalMs) { + double baseline = getBaselineIncrement(); + double baselineCooling = getBaselineCooling(); + + if (intervalMs <= 1000) { + primaryIncrement = baseline; + coolingRate = baselineCooling; + } else if (intervalMs <= 2000) { + primaryIncrement = baseline * 2.0; + coolingRate = baselineCooling * 2.0; + } else { + primaryIncrement = baseline * 4.0; + coolingRate = baselineCooling * 4.0; + } + + onIntervalUpdated(intervalMs); + } + + // Subclass hooks + protected abstract double getBaselineIncrement(); + protected double getBaselineCooling() { return 0.5; } + protected void onIntervalUpdated(int intervalMs) { /* optional override */ } + protected void onStart() { /* optional override */ } + protected void onStop() { /* optional override */ } + + // ----------------------- + // Shared helpers + // ----------------------- + protected void setCurrentValue(Object value) { /* telemetry hook */ } + + protected void raiseAlert(String title, String details) { + setStatus("Error"); + System.err.println("ALERT [" + sensorId + "] " + title + " - " + details); + } + + // ----------------------- + // Abstract contract + // ----------------------- + public abstract Object getValue(); + public abstract void readValue(); + public abstract void updateValue(double change); + public abstract void calibrateSensor(); + public abstract boolean validateReading(); + public abstract void sendAlert(double currentValue); + public abstract String getSensorInfo(); + public abstract void performCycle(); + + // helper to manually adjust next id (rarely needed) + public static void setNextId(int next) { + if (next <= 0) return; + ID_GEN.set(next); } } diff --git a/src/main/java/org/Automation/entities/TemperatureSensor.java b/src/main/java/org/Automation/entities/TemperatureSensor.java index e8428f6..c8f63a9 100644 --- a/src/main/java/org/Automation/entities/TemperatureSensor.java +++ b/src/main/java/org/Automation/entities/TemperatureSensor.java @@ -1,223 +1,276 @@ -package org.Automation.entities; +package org.automation.entities; + +import org.automation.engine.SimulationClock; +import org.automation.engine.ClockObserver; -import org.Automation.controllers.SimulationClock; import java.time.LocalDateTime; -import java.util.Timer; -import java.util.TimerTask; -public class TemperatureSensor extends Sensor implements SimulationClock.ClockObserver { - - // ========== CORE TEMPERATURE FIELDS ========== +/** + * TemperatureSensor + * - Implements baseline increment and cooling baseline + * - Registers/unregisters with SimulationClock in onStart/onStop hooks + * - Uses primaryIncrement and coolingRate set by Sensor.setSimulationInterval(...) + * - Uses Sensor's automaticMode and controlEnabled flags; provides a convenience wrapper + */ +public class TemperatureSensor extends Sensor implements ClockObserver { + private double currentTemperature; - private double minTemperature; - private double maxTemperature; - private String temperatureUnit; + private final String temperatureUnit; private double calibrationOffset; - private double temperatureIncrement; - - // ========== CONTROL SYSTEM FIELDS ========== + private boolean calibrated = false; + private double targetTemperature; - private double temperatureThreshold; + private double temperatureTolerance; private boolean temperatureControlEnabled; - - // ========== SIMULATION FIELDS ========== - private double startThreshold; + + private final double startThreshold; private boolean maxReached; - private double coolingRate; private LocalDateTime lastActionTime; - // ========== CONSTRUCTOR ========== - public TemperatureSensor(String sensorType, String location, String status, - double minTemperature, double maxTemperature, String temperatureUnit) { + private SimulationClock simulationClock; + + public TemperatureSensor(String sensorType, String location, String status, + double startThreshold, double temperatureTolerance, + double targetTemperature, String temperatureUnit) { super(sensorType, location, status); - this.minTemperature = minTemperature; - this.maxTemperature = maxTemperature; + this.startThreshold = startThreshold; + this.temperatureTolerance = temperatureTolerance; + this.targetTemperature = targetTemperature; this.temperatureUnit = temperatureUnit; - this.currentTemperature = minTemperature; - - // Register with global simulation clock - SimulationClock.getInstance().register(this); + this.currentTemperature = startThreshold; + this.maxReached = false; + // initialize control target/tolerance in base + this.controlTarget = targetTemperature; + this.controlTolerance = temperatureTolerance; } - public TemperatureSensor(String sensorType, int location, String status, - double minTemperature, double maxTemperature) { - this(sensorType, String.valueOf(location), status, minTemperature, maxTemperature, "°C"); + /** + * Construct a TemperatureSensor with an explicit sensor id (used when loading from DB). + */ + public TemperatureSensor(int sensorId, String sensorType, String location, String status, + double startThreshold, double temperatureTolerance, + double targetTemperature, String temperatureUnit) { + super(sensorId, sensorType, location, status); + this.startThreshold = startThreshold; + this.temperatureTolerance = temperatureTolerance; + this.targetTemperature = targetTemperature; + this.temperatureUnit = temperatureUnit; + this.currentTemperature = startThreshold; + this.maxReached = false; + this.controlTarget = targetTemperature; + this.controlTolerance = temperatureTolerance; } - @Override - public void onTick(LocalDateTime currentTime) { - if (lastActionTime == null || currentTime.minusSeconds(5).isAfter(lastActionTime)) { - if (isActive() && !maxReached) { - performTemperatureCycle(); - } - lastActionTime = currentTime; - } + public TemperatureSensor(String sensorType, int location, String status, + double startThreshold, double temperatureTolerance, + double targetTemperature) { + this(sensorType, String.valueOf(location), status, startThreshold, temperatureTolerance, targetTemperature, "°C"); } - // ========== GETTERS ========== - public double getCurrentTemperature() { return currentTemperature; } - public String getTemperatureUnit() { return temperatureUnit; } - public double getTargetTemperature() { return targetTemperature; } - public boolean isTemperatureControlEnabled() { return temperatureControlEnabled; } + @Override + protected double getBaselineIncrement() { return 0.5; } - public String getSensorInfo() { - return String.format("TemperatureSensor[ID=%d, Type=%s, Location=%s, Status=%s, " + - "Current=%.2f%s, Range=%.1f-%.1f%s, Target=%.1f%s, Control=%s]", - getSensorId(), getSensorType(), getLocation(), getStatus(), - currentTemperature, temperatureUnit, - minTemperature, maxTemperature, temperatureUnit, - targetTemperature, temperatureUnit, - temperatureControlEnabled ? "Enabled" : "Disabled"); + @Override + protected double getBaselineCooling() { return 0.25; } + + @Override + protected void onStart() { + this.simulationClock = SimulationClock.getInstance(); + this.simulationClock.register(this); + // initialize increments based on current clock interval so first cycle is aligned + try { setSimulationInterval(this.simulationClock.getTickIntervalMs()); } catch (Exception ignored) {} + calibrateSensor(); + activateSensor(); + maxReached = false; + lastActionTime = null; + // sync subclass target with base control fields + this.controlTarget = this.targetTemperature; + this.controlTolerance = this.temperatureTolerance; + enableTemperatureControl(this.targetTemperature, this.temperatureTolerance); + updateValue(0); + setStatus("Active"); + System.out.println("🌡️ TemperatureSensor " + getSensorId() + " started at " + currentTemperature + temperatureUnit); } - // ========== ABSTRACT METHOD IMPLEMENTATIONS ========== @Override - public void readValue() { - readSensorData(); + protected void onStop() { + try { simulationClock.unregister(this); } catch (Exception ignored) {} + deactivateSensor(); + temperatureControlEnabled = false; + lastActionTime = null; + calibrated = false; + updateValue(0); + setStatus("Stopped"); + System.out.println("🛑 TemperatureSensor " + getSensorId() + " stopped at " + currentTemperature + temperatureUnit); } @Override - public void updateValue() { - simulateTemperature(); + public void onTick(LocalDateTime currentTime) { + synchronized (this) { + if (!isActive()) return; + if (!automaticMode) return; // gate automatic behavior + if (lastActionTime == null || currentTime.minusSeconds(5).isAfter(lastActionTime)) { + try { + int intervalMs = SimulationClock.getInstance().getTickIntervalMs(); + setSimulationInterval(intervalMs); + if (controlEnabled && temperatureControlEnabled) { + performCycle(); + } else { + // passive behavior: small drift or no-op; here we apply primaryIncrement as passive growth + updateValue(primaryIncrement); + } + } catch (Exception e) { + sendAlert(currentTemperature + calibrationOffset); + setStatus("Error"); + } + lastActionTime = currentTime; + } + } } + // ----------------------- + // Abstract implementations + // ----------------------- @Override - public Object getValue() { - return currentTemperature; + public Object getValue() { return currentTemperature; } + + @Override + public void readValue() { + calibrateSensor(); + double calibratedTemp = currentTemperature + calibrationOffset; + System.out.println(getSensorInfo() + " | Calibrated Temperature: " + String.format("%.2f%s", calibratedTemp, temperatureUnit)); + boolean valid = validateReading(calibratedTemp); + if (!valid) sendAlert(calibratedTemp); + updateStatusAfterRead(valid); + System.out.println("⚡ Status: " + getTemperatureStatus(calibratedTemp)); + } + + @Override + public void updateValue(double change) { + simulateTemperature(change); + readValue(); } @Override public void calibrateSensor() { - System.out.println("🔧 Calibrating temperature sensor " + getSensorId()); - this.calibrationOffset = 0.5; + if (!calibrated) { + setStatus("Calibrating"); + System.out.println("🔧 Calibrating temperature sensor " + getSensorId()); + this.calibrationOffset = 0.05 + Math.random() * 0.1; + calibrated = true; + setStatus("OK"); + } } @Override public boolean validateReading() { - return currentTemperature >= minTemperature && currentTemperature <= maxTemperature; + return validateReading(currentTemperature + calibrationOffset); + } + + public boolean validateReading(double calibratedTemp) { + return Math.abs(calibratedTemp - targetTemperature) <= temperatureTolerance; } @Override - public void sendAlert() { - System.out.println("🚨 TEMPERATURE ALERT - Sensor " + getSensorId() + - ": " + currentTemperature + temperatureUnit); - updateStatus("Alert"); + public void sendAlert(double currentTemp) { + double calibratedTemp = currentTemp + calibrationOffset; + raiseAlert("Temperature out of range", temperatureUnit + " (Calibrated: " + String.format("%.2f", calibratedTemp) + ")"); } - // ========== CORE TEMPERATURE METHODS ========== - public void readSensorData() { - double temp = simulateTemperature(); - this.currentTemperature = temp + calibrationOffset; - setCurrentValue(currentTemperature); - - System.out.println(getSensorInfo()); - - if (!validateReading()) { - sendAlert(); - } + @Override + public String getSensorInfo() { + return String.format( + "TemperatureSensor[ID=%d, Type=%s, Location=%s, Status=%s, Current=%.2f%s, Start=%.2f%s, Target=%.2f%s, Tolerance=%.2f%s, Control=%s, Auto=%s]", + getSensorId(), getSensorType(), getLocation(), getStatus(), + currentTemperature, temperatureUnit, + startThreshold, temperatureUnit, + targetTemperature, temperatureUnit, + temperatureTolerance, temperatureUnit, + temperatureControlEnabled ? "Enabled" : "Disabled", + automaticMode ? "On" : "Off" + ); } - public double simulateTemperature() { - this.currentTemperature += temperatureIncrement; - - if (currentTemperature > maxTemperature) { - currentTemperature = maxTemperature; - } else if (currentTemperature < minTemperature) { - currentTemperature = minTemperature; - } - - this.currentTemperature = Math.round(currentTemperature); + // ----------------------- + // Simulation logic + // ----------------------- + public double simulateTemperature(double change) { + currentTemperature += change; + if (currentTemperature > targetTemperature) currentTemperature = targetTemperature; + if (currentTemperature < startThreshold) currentTemperature = startThreshold; setCurrentValue(currentTemperature); return currentTemperature; } - // ========== CONTROL SYSTEM METHODS ========== - public void enableTemperatureControl(double targetTemp, double threshold) { + public void enableTemperatureControl(double targetTemp, double tolerance) { this.targetTemperature = targetTemp; - this.temperatureThreshold = threshold; + this.temperatureTolerance = tolerance; this.temperatureControlEnabled = true; + // sync base control fields + enableControl(targetTemp, tolerance); } - public String getTemperatureStatus() { - if (!temperatureControlEnabled) { - return "Control Disabled"; - } - - double difference = Math.abs(currentTemperature - targetTemperature); - - if (difference <= temperatureThreshold) { - return "Within Target Range"; - } else if (currentTemperature > targetTemperature + temperatureThreshold) { - return "Too Hot"; - } else { - return "Too Cold"; - } + public void disableTemperatureControl() { + this.temperatureControlEnabled = false; + disableControl(); } - // ========== SIMULATION CONTROL ========== - public void start() { - this.currentTemperature = startThreshold; - setCurrentValue(currentTemperature); - maxReached = false; - activateSensor(); - System.out.println("🌡️ Starting temperature sensor " + getSensorId() + " simulation"); - System.out.println("Initial temperature: " + getCurrentTemperature() + temperatureUnit); + public String getTemperatureStatus(double tempToCheck) { + if (!temperatureControlEnabled) return "Control Disabled"; + double difference = Math.abs(tempToCheck - targetTemperature); + if (difference <= temperatureTolerance) return "Within Target Range"; + else if (tempToCheck > targetTemperature + temperatureTolerance) return "Too Hot"; + else return "Too Cold"; } - public void stop() { - deactivateSensor(); - } - - public boolean isRunning() { - return isActive(); + // ----------------------- + // Heating / Cooling cycle (uses primaryIncrement and coolingRate) + // ----------------------- + private void startHeating() { + updateValue(primaryIncrement); + if (currentTemperature >= targetTemperature + temperatureTolerance) { + throw new IllegalStateException("Too Hot: " + currentTemperature + temperatureUnit); + } + if (currentTemperature >= targetTemperature) { + maxReached = true; + deactivateSensor(); + setStatus("Cooling"); + } } - public void configureHeatingParameters(double startThreshold, double increment, double coolingRate) { - this.startThreshold = startThreshold; - this.coolingRate = coolingRate; - this.temperatureIncrement = increment; - } - - // ========== PRIVATE SIMULATION HELPERS ========== - private void performTemperatureCycle() { - if (isActive() && !maxReached) { - this.currentTemperature += temperatureIncrement; - setCurrentValue(currentTemperature); - System.out.println("🌡️ Sensor " + getSensorId() + " heating: " + getCurrentTemperature() + temperatureUnit); - - if (currentTemperature >= maxTemperature) { - this.currentTemperature = maxTemperature; - setCurrentValue(currentTemperature); - maxReached = true; - deactivateSensor(); - System.out.println("🌡️ Sensor " + getSensorId() + " reached max: " + maxTemperature + "°C - DEACTIVATED"); - System.out.println(getSensorInfo()); - startCoolingMonitor(); - } + private void startCooling() { + updateValue(-coolingRate); + if (currentTemperature <= startThreshold - temperatureTolerance) { + throw new IllegalStateException("Too Cold: " + currentTemperature + temperatureUnit); + } + if (currentTemperature <= startThreshold) { + maxReached = false; + activateSensor(); + setStatus("Active"); + System.out.println("🔄 Sensor " + getSensorId() + " cooled to start threshold - RESTARTING"); } } - - private void startCoolingMonitor() { - Timer coolingTimer = new Timer("Cooling-" + getSensorId(), true); - coolingTimer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - double cooledTemp = currentTemperature - coolingRate; - currentTemperature = Math.max(cooledTemp, startThreshold); - setCurrentValue(currentTemperature); - - System.out.println("❄️ Sensor " + getSensorId() + " cooling: " + getCurrentTemperature() + temperatureUnit); - - if (currentTemperature <= startThreshold + 5.0) { - coolingTimer.cancel(); - System.out.println("🔄 Sensor " + getSensorId() + " temperature dropped - RESTARTING"); - maxReached = false; - activateSensor(); - start(); - } - } - }, 2000, 2000); - } -} + @Override + public void performCycle() { + if (!temperatureControlEnabled) return; + // hysteresis: only heat if below target - tolerance, only cool if above target + tolerance + double calibrated = currentTemperature + calibrationOffset; + if (calibrated < targetTemperature - temperatureTolerance) { + startHeating(); + } else if (calibrated > targetTemperature + temperatureTolerance) { + startCooling(); + } else { + // within deadband: no action + setStatus("WithinDeadband"); + } + } + // getters + public double getCurrentTemperature() { return currentTemperature; } + public String getTemperatureUnit() { return temperatureUnit; } + public double getTargetTemperature() { return targetTemperature; } + public boolean isTemperatureControlEnabled() { return temperatureControlEnabled; } + public double getStartThreshold() { return startThreshold; } + public double getTemperatureTolerance() { return temperatureTolerance; } +} diff --git a/src/main/java/org/Automation/entities/WeightSensor.java b/src/main/java/org/Automation/entities/WeightSensor.java index 0eda8b4..5ae5190 100644 --- a/src/main/java/org/Automation/entities/WeightSensor.java +++ b/src/main/java/org/Automation/entities/WeightSensor.java @@ -1,204 +1,254 @@ -package org.Automation.entities; +package org.automation.entities; -import org.Automation.controllers.SimulationClock; +import org.automation.engine.SimulationClock; +import org.automation.engine.ClockObserver; + +import java.time.LocalDateTime; + +/** + * WeightSensor + * - Calibration offset is applied only when reporting/validating (Option 1) + * - Internal simulated value (currentWeight) remains the raw truth + * - Uses Sensor.primaryIncrement (clock-driven) for automatic per-tick weight changes + * - Uses Sensor's automaticMode and controlEnabled flags; provides convenience wrappers + */ +public class WeightSensor extends Sensor implements ClockObserver { -public class WeightSensor extends Sensor { - // ========== CORE WEIGHT FIELDS ========== - private double minWeight; - private double maxWeight; private double currentWeight; - private String weightUnit; - private double calibrationFactor; - private double weightIncrement; - - // ========== CONTROL SYSTEM FIELDS ========== + private final String weightUnit; private double machineCapacity; private boolean weightControlEnabled; - - // ========== SIMULATION FIELDS ========== + private double lastWeight; + + private double calibrationOffset; + private boolean calibrated = false; + + private volatile LocalDateTime lastActionTime; private SimulationClock simulationClock; - private double startWeight; - private boolean maxReached; - // ========== CONSTRUCTOR ========== - public WeightSensor(String sensorType, String location, String status, - double minWeight, double maxWeight, String weightUnit) { + public WeightSensor(String sensorType, String location, String status, + double initialWeight, double capacity, String weightUnit) { super(sensorType, location, status); - this.minWeight = minWeight; - this.maxWeight = maxWeight; + this.currentWeight = initialWeight; + this.lastWeight = initialWeight; + this.machineCapacity = capacity; this.weightUnit = weightUnit; - this.currentWeight = minWeight; - this.startWeight = minWeight; - - this.simulationClock = new SimulationClock("WeightSensor-" + getSensorId(), 1000) { - @Override - protected void tick() { - super.tick(); - performWeightCycle(); - } - }; + this.weightControlEnabled = true; + this.simulationClock = SimulationClock.getInstance(); + // initialize base control fields + this.controlTarget = initialWeight; + this.controlTolerance = 0.0; + } + + /** + * Construct a WeightSensor with an explicit sensor id (used when loading from DB). + */ + public WeightSensor(int sensorId, String sensorType, String location, String status, + double initialWeight, double capacity, String weightUnit) { + super(sensorId, sensorType, location, status); + this.currentWeight = initialWeight; + this.lastWeight = initialWeight; + this.machineCapacity = capacity; + this.weightUnit = weightUnit; + this.weightControlEnabled = true; + this.simulationClock = SimulationClock.getInstance(); + this.controlTarget = initialWeight; + this.controlTolerance = 0.0; } - // ========== GETTERS ========== - public double getCurrentWeight() { return currentWeight; } - public String getWeightUnit() { return weightUnit; } - public double getMachineCapacity() { return machineCapacity; } - public boolean isWeightControlEnabled() { return weightControlEnabled; } + @Override + protected double getBaselineIncrement() { return 1.0; } - public String getSensorInfo() { - return String.format("WeightSensor[ID=%d, Type=%s, Location=%s, Status=%s, " + - "Current=%.2f%s, Range=%.1f-%.1f%s, Capacity=%.1f%s, Control=%s]", - getSensorId(), getSensorType(), getLocation(), getStatus(), - currentWeight, weightUnit, - minWeight, maxWeight, weightUnit, - machineCapacity, weightUnit, - weightControlEnabled ? "Enabled" : "Disabled"); + @Override + protected double getBaselineCooling() { return 0.5; } + + @Override + protected void onStart() { + simulationClock.register(this); + // initialize increments based on current clock interval so first cycle is aligned + try { setSimulationInterval(simulationClock.getTickIntervalMs()); } catch (Exception ignored) {} + calibrateSensor(); + updateValue(0); + setStatus("Active"); + System.out.println("⚖️ WeightSensor " + getSensorId() + " started at " + currentWeight + weightUnit); } - // ========== ABSTRACT METHOD IMPLEMENTATIONS ========== @Override - public void readValue() { - readSensorData(); + protected void onStop() { + try { simulationClock.unregister(this); } catch (Exception ignored) {} + setStatus("Stopped"); + System.out.println("🛑 WeightSensor " + getSensorId() + " stopped at " + currentWeight + weightUnit); } @Override - public void updateValue() { - simulateWeight(); + public void onTick(LocalDateTime currentTime) { + synchronized (this) { + if (!isActive()) return; + if (!automaticMode) return; // gate automatic behavior + if (lastActionTime == null || currentTime.minusSeconds(1).isAfter(lastActionTime)) { + try { + int intervalMs = SimulationClock.getInstance().getTickIntervalMs(); + setSimulationInterval(intervalMs); + if (controlEnabled && weightControlEnabled) { + performCycle(); + } else { + // passive automatic addition if desired + updateValue(primaryIncrement); + } + } catch (IllegalArgumentException e) { + sendAlert(getCalibratedWeight()); + setStatus("Error"); + } + lastActionTime = currentTime; + } + } } + // ----------------------- + // Abstract implementations + // ----------------------- @Override - public Object getValue() { - return currentWeight; + public Object getValue() { return currentWeight; } + + @Override + public void readValue() { + calibrateSensor(); + double calibratedWeight = getCalibratedWeight(); + System.out.println(getSensorInfo() + " | Calibrated Weight: " + String.format("%.2f%s", calibratedWeight, weightUnit)); + boolean valid = validateReading(); + if (!valid) sendAlert(calibratedWeight); + updateStatusAfterRead(valid); + System.out.println("⚡ Status: " + (valid ? "Within Capacity" : "Out of Range")); + } + + @Override + public void updateValue(double change) { + try { + simulateWeight(change); + } catch (IllegalArgumentException e) { + sendAlert(getCalibratedWeight()); + setStatus("Error"); + return; + } + readValue(); } @Override public void calibrateSensor() { - System.out.println("🔧 Calibrating weight sensor " + getSensorId()); - this.calibrationFactor = 0.1; + if (!calibrated) { + setStatus("Calibrating"); + System.out.println("🔧 Calibrating weight sensor " + getSensorId()); + this.calibrationOffset = 0.1 + Math.random() * 0.2; + calibrated = true; + setStatus("OK"); + } + } + + public double getCalibratedWeight() { + return currentWeight + calibrationOffset; } @Override public boolean validateReading() { - return currentWeight >= minWeight && currentWeight <= maxWeight; + double calibrated = getCalibratedWeight(); + return calibrated > 0 && calibrated <= machineCapacity; } @Override - public void sendAlert() { - System.out.println("🚨 WEIGHT ALERT - Sensor " + getSensorId() + - ": " + currentWeight + weightUnit); - updateStatus("Alert"); + public void sendAlert(double currentValue) { + raiseAlert("Weight out of range", String.format("%.2f%s", currentValue, weightUnit)); } - // ========== CORE WEIGHT METHODS ========== - public void readSensorData() { - double weight = simulateWeight(); - this.currentWeight = weight + calibrationFactor; - setCurrentValue(currentWeight); - - System.out.println(getSensorInfo()); - - if (!validateReading()) { - sendAlert(); - } + @Override + public String getSensorInfo() { + return String.format("WeightSensor[ID=%d, Type=%s, Location=%s, Status=%s, Current=%.2f%s, Capacity=%.2f%s, Auto=%s]", + getSensorId(), getSensorType(), getLocation(), getStatus(), + currentWeight, weightUnit, machineCapacity, weightUnit, automaticMode ? "On" : "Off"); } - public double simulateWeight() { - this.currentWeight += weightIncrement; - - if (currentWeight > maxWeight) { - currentWeight = maxWeight; - } else if (currentWeight < minWeight) { - currentWeight = minWeight; + // ----------------------- + // Simulation logic (raw value) + // ----------------------- + private synchronized void simulateWeight(double change) { + double newWeight = currentWeight + change; + + if (newWeight <= 0) { + throw new IllegalArgumentException("Load cannot be ≤ 0. Attempted: " + newWeight + weightUnit); + } + if (newWeight > machineCapacity) { + throw new IllegalArgumentException("Overload detected. Attempted: " + newWeight + weightUnit); + } + if (Math.abs(newWeight - lastWeight) > machineCapacity * 0.3) { + throw new IllegalArgumentException("Shock load detected. Change: " + (newWeight - lastWeight) + weightUnit); } - - this.currentWeight = Math.round(currentWeight); - setCurrentValue(currentWeight); - return currentWeight; - } - // ========== CONTROL SYSTEM METHODS ========== - public void enableWeightControl(double capacity) { - this.machineCapacity = capacity; - this.weightControlEnabled = true; + currentWeight = newWeight; + lastWeight = currentWeight; + setCurrentValue(currentWeight); } + // Manual control public boolean addWeight(double weight) { - double currentWeightValue = getCurrentWeight(); - if (currentWeightValue + weight <= machineCapacity) { - this.currentWeight += weight; - setCurrentValue(currentWeight); - System.out.println("⚖️ Added " + weight + weightUnit + " to sensor " + getSensorId() + - " (Total: " + getCurrentWeight() + weightUnit + ")"); - System.out.println(getSensorInfo()); + try { + updateValue(weight); + System.out.println("⚖️ Added " + weight + weightUnit + " -> Current: " + currentWeight + weightUnit); return true; - } else { - System.out.println("🚨 Cannot add " + weight + weightUnit + " - would exceed capacity!"); - System.out.println("Current: " + getCurrentWeight() + weightUnit + ", Capacity: " + machineCapacity + weightUnit); + } catch (IllegalArgumentException e) { + sendAlert(getCalibratedWeight()); + setStatus("Error"); return false; } } - // ========== SIMULATION CONTROL ========== - public void start() { - this.currentWeight = startWeight; - setCurrentValue(currentWeight); - maxReached = false; - activateSensor(); - simulationClock.start(); - System.out.println("⚖️ Starting weight sensor " + getSensorId() + " simulation"); - System.out.println("Initial weight: " + getCurrentWeight() + weightUnit); - } - - public void stop() { - simulationClock.stop(); - } - - public boolean isRunning() { - return simulationClock.isRunning(); + public boolean removeWeight(double weight) { + try { + updateValue(-weight); + System.out.println("⚖️ Removed " + weight + weightUnit + " -> Current: " + currentWeight + weightUnit); + return true; + } catch (IllegalArgumentException e) { + sendAlert(getCalibratedWeight()); + setStatus("Error"); + return false; + } } - public void setSimulationInterval(int intervalMs) { - calculateIncrementForInterval(intervalMs); - simulationClock.setInterval(intervalMs); + // Control wrappers specific to weight + public void enableWeightControl(double capacity) { + this.machineCapacity = capacity; + this.weightControlEnabled = true; + enableControl(currentWeight, 0.0); } - public void configureWeightParameters(double startWeight, double increment, int intervalMs) { - this.startWeight = startWeight; - setSimulationInterval(intervalMs); - this.weightIncrement = increment; + public void disableWeightControl() { + this.weightControlEnabled = false; + disableControl(); } - // ========== PRIVATE SIMULATION HELPERS ========== - private void performWeightCycle() { - if (isActive() && !maxReached) { - double currentWeightValue = getCurrentWeight(); - - while (currentWeightValue < machineCapacity && !maxReached) { - this.currentWeight += weightIncrement; - setCurrentValue(currentWeight); - currentWeightValue = getCurrentWeight(); - System.out.println("⚖️ Sensor " + getSensorId() + " loading: " + currentWeightValue + weightUnit); + @Override + public void performCycle() { + if (!weightControlEnabled) return; + // simple control: try to maintain controlTarget (base) within controlTolerance + double calibrated = getCalibratedWeight(); + if (calibrated < controlTarget - controlTolerance) { + // add small increment + updateValue(primaryIncrement); + } else if (calibrated > controlTarget + controlTolerance) { + // remove small amount (if safe) + try { + updateValue(-primaryIncrement); + } catch (Exception e) { + // if removal would violate safety, raise alert and disable automatic control + raiseAlert("Auto-remove failed", e.getMessage()); + disableAutomaticMode(); + disableControl(); } - - this.currentWeight = machineCapacity; - setCurrentValue(currentWeight); - maxReached = true; - deactivateSensor(); - simulationClock.stop(); - System.out.println("⚖️ Sensor " + getSensorId() + " reached capacity: " + machineCapacity + - "kg - DEACTIVATED & STOPPED"); - System.out.println(getSensorInfo()); + } else { + setStatus("WithinDeadband"); } } - private void calculateIncrementForInterval(int intervalMs) { - if (intervalMs <= 1000) this.weightIncrement = 5.0; - else if (intervalMs <= 2000) this.weightIncrement = 10.0; - else this.weightIncrement = 15.0; - } + // getters + public double getCurrentWeight() { return currentWeight; } + public String getWeightUnit() { return weightUnit; } + public double getMachineCapacity() { return machineCapacity; } } - - - - - diff --git a/src/main/java/org/Automation/repositories/Repository.java b/src/main/java/org/Automation/repositories/Repository.java index 666ba81..00e574c 100644 --- a/src/main/java/org/Automation/repositories/Repository.java +++ b/src/main/java/org/Automation/repositories/Repository.java @@ -30,38 +30,27 @@ public boolean delete(String where, Object[] params) { } public T findOne(String where, Object[] params) { - try (ResultSet rs = db.find(tableName, where, params)) { - if (rs.next()) { - return mapRow(rs); - } - } catch (SQLException e) { + try { + return db.queryOne(tableName, where, params, rs -> mapRow(rs)); + } catch (RuntimeException e) { throw new RuntimeException("findOne failed: " + e.getMessage(), e); } - return null; } public List findAllWhere(String where, Object[] params) { - List result = new ArrayList<>(); - try (ResultSet rs = db.find(tableName, where, params)) { - while (rs.next()) { - result.add(mapRow(rs)); - } - } catch (SQLException e) { + try { + return db.query(tableName, where, params, rs -> mapRow(rs)); + } catch (RuntimeException e) { throw new RuntimeException("findAllWhere failed: " + e.getMessage(), e); } - return result; } public List findAll() { - List result = new ArrayList<>(); - try (ResultSet rs = db.find(tableName, null, null)) { - while (rs.next()) { - result.add(mapRow(rs)); - } - } catch (SQLException e) { + try { + return db.query(tableName, null, null, rs -> mapRow(rs)); + } catch (RuntimeException e) { throw new RuntimeException("findAll failed: " + e.getMessage(), e); } - return result; } // ---------- Maps a row from ResultSet into an entity ---------- diff --git a/src/main/java/org/Automation/repositories/SensorRepository.java b/src/main/java/org/Automation/repositories/SensorRepository.java index 3ccd078..373728c 100644 --- a/src/main/java/org/Automation/repositories/SensorRepository.java +++ b/src/main/java/org/Automation/repositories/SensorRepository.java @@ -5,32 +5,190 @@ import org.automation.database.DatabaseManager; import org.automation.entities.Sensor; +import org.automation.entities.TemperatureSensor; +import org.automation.entities.WeightSensor; +/** + * SensorRepository + * + * - Maps rows to concrete Sensor implementations (TemperatureSensor or WeightSensor) + * - Uses nullable columns for sensor-specific fields and provides sensible defaults + * - Creates a table schema that contains common and sensor-specific columns + */ public class SensorRepository extends Repository { + public SensorRepository(DatabaseManager db) { super("Sensor", db); } - + /** + * Ensure sensor table exists in DB + */ + public boolean ensureTable() { + return db.executeDDL(createTableQuery()); + } + + /** + * Persist a sensor into the Sensor table. Inserts only (no upsert for simplicity). + */ + public boolean save(Sensor sensor) { + if (sensor == null) return false; + String type = sensor.getSensorType(); + // If a row with this sensor ID exists -> UPDATE, otherwise INSERT (omit id to let SQLite autoincrement) + Sensor existing = findOne("id = ?", new Object[]{sensor.getSensorId()}); + try { + if (sensor instanceof TemperatureSensor) { + TemperatureSensor t = (TemperatureSensor) sensor; + if (existing != null) { + String set = "type = ?, location = ?, status = ?, startThreshold = ?, tolerance = ?, targetTemperature = ?, temperatureUnit = ?"; + Object[] params = new Object[]{t.getSensorType(), t.getLocation(), t.getStatus(), t.getStartThreshold(), t.getTemperatureTolerance(), t.getTargetTemperature(), t.getTemperatureUnit(), t.getSensorId()}; + return update(set, "id = ?", params); + } else { + // Insert with explicit id so in-memory id and DB id stay in sync. ID collision is avoided + // because we already checked for an existing row. + String[] cols = new String[]{"id","type","location","status","startThreshold","tolerance","targetTemperature","temperatureUnit"}; + Object[] vals = new Object[]{t.getSensorId(), t.getSensorType(), t.getLocation(), t.getStatus(), t.getStartThreshold(), t.getTemperatureTolerance(), t.getTargetTemperature(), t.getTemperatureUnit()}; + return insert(cols, vals); + } + } else if (sensor instanceof WeightSensor) { + WeightSensor w = (WeightSensor) sensor; + if (existing != null) { + String set = "type = ?, location = ?, status = ?, initialWeight = ?, capacity = ?, weightUnit = ?"; + Object[] params = new Object[]{w.getSensorType(), w.getLocation(), w.getStatus(), w.getCurrentWeight(), w.getMachineCapacity(), w.getWeightUnit(), w.getSensorId()}; + return update(set, "id = ?", params); + } else { + String[] cols = new String[]{"id","type","location","status","initialWeight","capacity","weightUnit"}; + Object[] vals = new Object[]{w.getSensorId(), w.getSensorType(), w.getLocation(), w.getStatus(), w.getCurrentWeight(), w.getMachineCapacity(), w.getWeightUnit()}; + return insert(cols, vals); + } + } else { + if (existing != null) { + String set = "type = ?, location = ?, status = ?"; + Object[] params = new Object[]{sensor.getSensorType(), sensor.getLocation(), sensor.getStatus(), sensor.getSensorId()}; + return update(set, "id = ?", params); + } else { + String[] cols = new String[]{"id","type","location","status"}; + Object[] vals = new Object[]{sensor.getSensorId(), sensor.getSensorType(), sensor.getLocation(), sensor.getStatus()}; + return insert(cols, vals); + } + } + } catch (RuntimeException re) { + throw re; + } + } + + public boolean deleteById(int id) { + return delete("id = ?", new Object[]{id}); + } + + /** + * Map a ResultSet row to a concrete Sensor instance. + * Expects the table to contain columns for both temperature and weight sensors. + */ @Override public Sensor mapRow(ResultSet rs) throws SQLException { - return new Sensor( - rs.getInt("id"), - rs.getString("type"), - rs.getInt("machineId"), - rs.getString("status")); + int id = 0; + try { id = rs.getInt("id"); } catch (SQLException ignored) {} + String type = safeGetString(rs, "type", "Unknown"); + String location = safeGetString(rs, "location", "Unknown"); + String status = safeGetString(rs, "status", "inactive"); + + // Temperature-specific columns (may be null for non-temperature rows) + double startThreshold = safeGetDouble(rs, "startThreshold", 0.0); + double tolerance = safeGetDouble(rs, "tolerance", 1.0); + double targetTemperature = safeGetDouble(rs, "targetTemperature", 25.0); + String tempUnit = safeGetString(rs, "temperatureUnit", "°C"); + + // Weight-specific columns (may be null for non-weight rows) + double initialWeight = safeGetDouble(rs, "initialWeight", 0.0); + double capacity = safeGetDouble(rs, "capacity", 100.0); + String weightUnit = safeGetString(rs, "weightUnit", "kg"); + + // Choose concrete type based on the 'type' column (case-insensitive) + if ("temperature".equalsIgnoreCase(type) || type.toLowerCase().contains("temp")) { + return new TemperatureSensor( + id, + type, + location, + status, + startThreshold, + tolerance, + targetTemperature, + tempUnit + ); + } else if ("weight".equalsIgnoreCase(type)) { + return new WeightSensor( + id, + type, + location, + status, + initialWeight, + capacity, + weightUnit + ); + } else { + // Fallback: return a TemperatureSensor with safe defaults to avoid nulls + return new TemperatureSensor( + id, + type, + location, + status, + startThreshold, + tolerance, + targetTemperature, + tempUnit + ); + } } + /** + * Table schema includes common columns plus nullable sensor-specific columns. + * This keeps a single Sensor table compatible with both TemperatureSensor and WeightSensor. + */ @Override public String createTableQuery() { return """ - CREATE TABLE IF NOT EXISTS Sensor ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - type TEXT NOT NULL, - machineId INTEGER, - status TEXT DEFAULT 'inactive', - FOREIGN KEY(machineId) REFERENCES Machine(id) - ); + CREATE TABLE IF NOT EXISTS Sensor ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + type TEXT NOT NULL, + location TEXT, + machineId INTEGER, + status TEXT DEFAULT 'inactive', + + -- Temperature sensor fields (nullable) + startThreshold REAL, + tolerance REAL, + targetTemperature REAL, + temperatureUnit TEXT, + + -- Weight sensor fields (nullable) + initialWeight REAL, + capacity REAL, + weightUnit TEXT, + + FOREIGN KEY(machineId) REFERENCES Machine(id) + ); """; } + + // ----------------------- + // Helper getters with defaults + // ----------------------- + private String safeGetString(ResultSet rs, String column, String defaultValue) { + try { + String v = rs.getString(column); + return v == null ? defaultValue : v; + } catch (SQLException e) { + return defaultValue; + } + } + + private double safeGetDouble(ResultSet rs, String column, double defaultValue) { + try { + double v = rs.getDouble(column); + return rs.wasNull() ? defaultValue : v; + } catch (SQLException e) { + return defaultValue; + } + } } diff --git a/src/main/java/org/automation/SensorDemo.java b/src/main/java/org/automation/SensorDemo.java new file mode 100644 index 0000000..ceaacc7 --- /dev/null +++ b/src/main/java/org/automation/SensorDemo.java @@ -0,0 +1,40 @@ +// package org.automation; + +// import org.automation.entities.*; + +// public class SensorDemo { +// public static void main(String[] args) { +// System.out.println("=== Factory Sensor Monitoring System ===\n"); + +// // Create Temperature and Weight sensors +// TemperatureSensor tempSensor = new TemperatureSensor("Temperature","Factory Floor A", "Active", 20.0,5, 80.0, "°C"); +// WeightSensor weightSensor = new WeightSensor("Weight", "Conveyor Belt 1", "Active", 50.0, 100.0, "kg"); + +// System.out.println("Initial State:"); +// System.out.println(tempSensor); +// System.out.println(weightSensor); + +// System.out.println("\n=== Updating Sensor Values ===\n"); + +// // Simulate 5 readings +// for (int i = 1; i <= 5; i++) { +// System.out.println("Reading #" + i + ":"); + +// tempSensor.updateValue(0.5); +// weightSensor.updateValue(1.0); + +// System.out.println(" " + tempSensor); +// System.out.println(" " + weightSensor); +// System.out.println(); + +// try { +// Thread.sleep(1000); // Wait 1 second between readings +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// } + +// System.out.println("=== Monitoring Complete ==="); +// } +// } + diff --git a/src/main/java/org/automation/SensorManagerDemo.java b/src/main/java/org/automation/SensorManagerDemo.java new file mode 100644 index 0000000..c29afaa --- /dev/null +++ b/src/main/java/org/automation/SensorManagerDemo.java @@ -0,0 +1,93 @@ +// package org.automation; + +// import org.automation.controllers.SensorManager; +// import org.automation.entities.*; +// import java.util.List; + +// /** +// * Demo showing SensorManager initialization and usage +// */ +// public class SensorManagerDemo { +// public static void main(String[] args) { +// System.out.println("=== SensorManager Initialization Demo ===\n"); + +// // Step 1: Create SensorManager (initializes empty list) +// System.out.println("Step 1: Creating SensorManager"); +// SensorManager manager = new SensorManager(); +// // System.out.println(" Sensor count: " + manager.getSensorCount()); + +// // Step 2: Add Temperature Sensors +// System.out.println("\nStep 2: Adding Temperature Sensors"); +// TemperatureSensor temp1 = new TemperatureSensor("Temperature", "Factory Floor A", "Active", 20.0, 5, 80.0, "°C"); +// TemperatureSensor temp2 = new TemperatureSensor("Temperature", "Factory Floor B", "Active", 15.0,6, 75.0, "°C"); + +// manager.addSensor(temp1); +// manager.addSensor(temp2); +// // System.out.println(" Sensor count: " + manager.getSensorCount()); + +// // Step 3: Add Weight Sensors +// System.out.println("\nStep 3: Adding Weight Sensors"); +// WeightSensor weight1 = new WeightSensor("Weight", "Conveyor Belt 1", "Active", 50.0, 100.0, "kg"); +// WeightSensor weight2 = new WeightSensor("Weight", "Conveyor Belt 2", "Active", 10.0, 50.0, "kg"); + +// manager.addSensor(weight1); +// manager.addSensor(weight2); +// // System.out.println(" Sensor count: " + manager.getSensorCount()); + +// // Step 4: Display all sensors +// manager.displayAllSensors(); + +// // Step 5: Update all sensors (simulate readings) +// // System.out.println("\nStep 5: Updating all sensors (3 readings)"); +// // for (int i = 1; i <= 3; i++) { +// // System.out.println("\n--- Reading #" + i + " ---"); +// // manager.updateAllSensors(); + +// // // Check for alerts +// // List alerts = manager.getAlertSensors(); +// // if (!alerts.isEmpty()) { +// // System.out.println("\n⚠️ ALERTS DETECTED:"); +// // for (Sensor sensor : alerts) { +// // System.out.println(" " + sensor); +// // } +// // } + +// // try { +// // Thread.sleep(1000); +// // } catch (InterruptedException e) { +// // e.printStackTrace(); +// // } +// // } + +// // Step 6: Find sensors by type +// // System.out.println("\n\nStep 6: Finding sensors by type"); +// // List tempSensors = manager.findSensorsByType("Temperature"); +// // System.out.println(" Temperature sensors: " + tempSensors.size()); +// // for (Sensor s : tempSensors) { +// // System.out.println(" " + s); +// // } + +// // List weightSensors = manager.findSensorsByType("Weight"); +// // System.out.println(" Weight sensors: " + weightSensors.size()); +// // for (Sensor s : weightSensors) { +// // System.out.println(" " + s); +// // } + +// // Step 7: Find sensor by ID +// System.out.println("\nStep 7: Finding sensor by ID"); +// Sensor found = manager.findSensorById(2); +// if (found != null) { +// System.out.println(" Found: " + found); +// } + +// // Step 8: Remove a sensor +// System.out.println("\nStep 8: Removing sensor with ID 3"); +// manager.removeSensor(3); +// // System.out.println(" Sensor count: " + manager.getSensorCount()); + +// manager.displayAllSensors(); + +// System.out.println("\n=== Demo Complete ==="); +// } +// } + diff --git a/src/main/java/org/automation/TestInitialization.java b/src/main/java/org/automation/TestInitialization.java new file mode 100644 index 0000000..3e88f34 --- /dev/null +++ b/src/main/java/org/automation/TestInitialization.java @@ -0,0 +1,35 @@ +// package org.automation; + +// import org.automation.entities.TemperatureSensor; + +// public class TestInitialization { +// public static void main(String[] args) { +// System.out.println("=== Testing Initialization ===\n"); + +// Create sensor with minTemp=20, maxTemp=80 +// TemperatureSensor sensor = new TemperatureSensor("Temperature", "Factory Floor A", "Active", 20.0, 5,80.0, "°C"); + +// // System.out.println("After constructor:"); +// // System.out.println(" minTemp = " + sensor.getMinTemp()); // Should be 20.0 +// // System.out.println(" maxTemp = " + sensor.getMaxTemp()); // Should be 80.0 + +// System.out.println("\nCalling updateValue() 5 times:"); +// for (int i = 1; i <= 5; i++) { +// sensor.updateValue(0.5); +// double temp = (Double) sensor.getValue(); +// System.out.println(" Reading #" + i + ": " + temp + "°C"); + +// // Verify temperature is using minTemp and maxTemp correctly +// if (temp >= 20.0 && temp <= 80.0) { +// System.out.println(" ✅ Within range [20.0 - 80.0]"); +// } else { +// System.out.println(" ⚠️ Outside range (testing alert condition)"); +// } +// } + +// System.out.println("\n=== Conclusion ==="); +// System.out.println("minTemp and maxTemp ARE initialized in the constructor!"); +// System.out.println("They are used by simulateTemperature() to generate values."); +// } +// } + diff --git a/src/main/java/org/automation/database/RowMapper.java b/src/main/java/org/automation/database/RowMapper.java new file mode 100644 index 0000000..e9f60ee --- /dev/null +++ b/src/main/java/org/automation/database/RowMapper.java @@ -0,0 +1,12 @@ +package org.automation.database; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Functional interface for mapping a ResultSet row to a domain object. + */ +@FunctionalInterface +public interface RowMapper { + T mapRow(ResultSet rs) throws SQLException; +} From 09beb96c1e3bb92caae2c68381488deacd03fa8f Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 25 Dec 2025 01:12:41 +0300 Subject: [PATCH 3/5] Add sensor management and test class --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 59393c5..81efdc3 100644 --- a/pom.xml +++ b/pom.xml @@ -23,11 +23,12 @@ - org.xerial - sqlite-jdbc - 3.46.0.0 + org.xerial + sqlite-jdbc + 3.45.2.0 + From 4771687e8f6172fbe58f6eb0b4ca61a6c6784d21 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 25 Dec 2025 01:13:26 +0300 Subject: [PATCH 4/5] Refactor WeightSensor and Repository Classes - Updated WeightSensor class to improve weight simulation and calibration logic. - Enhanced the constructor to support initialization with explicit sensor ID. - Implemented ClockObserver interface for better time-based weight updates. - Refactored Repository class methods to streamline database interactions using a functional RowMapper. - Improved SensorRepository to handle both TemperatureSensor and WeightSensor with appropriate database operations. - Added safe getters for ResultSet to handle potential null values gracefully. - Created new test and demo classes for sensor initialization and management. - Introduced package-lock.json and package.json for dependency management. --- .../org/Automation/TestSensorManager.java | 151 ++------ .../Automation/controllers/SensorManager.java | 4 +- .../Automation/database/DatabaseManager.java | 242 ++++++------ .../repositories/SensorRepository.java | 225 +++++------ .../java/org/Automation/ui/ConsoleApp.java | 366 ++++++++++++++++-- 5 files changed, 582 insertions(+), 406 deletions(-) diff --git a/src/main/java/org/Automation/TestSensorManager.java b/src/main/java/org/Automation/TestSensorManager.java index a84cc1a..2119f02 100644 --- a/src/main/java/org/Automation/TestSensorManager.java +++ b/src/main/java/org/Automation/TestSensorManager.java @@ -13,158 +13,71 @@ public class TestSensorManager { private static final Scanner scanner = new Scanner(System.in); public static void main(String[] args) { - System.out.println("=== Interactive SensorManager Test ===\n"); + System.out.println("Initializing Sensor Management System..."); SensorManager manager = new SensorManager(); boolean running = true; while (running) { printMenu(); int choice = readInt("Select option: "); - try { - switch (choice) { - case 1 -> listSensors(manager); - case 2 -> addTemperatureSensor(manager); - case 3 -> addWeightSensor(manager); - case 4 -> startSensor(manager); - case 5 -> stopSensor(manager); - case 6 -> readSensorValue(manager); - case 7 -> toggleAutomatic(manager); - case 8 -> setControl(manager); - case 9 -> removeSensor(manager); - case 10 -> findById(manager); - case 0 -> { - System.out.println("Shutting down..."); - manager.stopAll(); - manager.shutdown(5); - running = false; - } - default -> System.out.println("Unknown option"); - } - } catch (Exception e) { - System.err.println("Operation failed: " + e.getMessage()); + switch (choice) { + case 1 -> listSensors(manager); + case 2 -> addTemperatureSensor(manager); + case 3 -> addWeightSensor(manager); + case 4 -> startSensor(manager); + case 5 -> stopSensor(manager); + case 6 -> readSensorValue(manager); + case 7 -> toggleAutomatic(manager); + case 8 -> setControl(manager); + case 9 -> removeSensor(manager); + case 10 -> findById(manager); + case 11 -> manager.startAll(); + case 12 -> manager.stopAll(); + case 0 -> { manager.shutdown(5); running = false; } + default -> System.out.println("Unknown option."); } - System.out.println(); } - - System.out.println("Goodbye."); } private static void printMenu() { - System.out.println("Menu:"); - System.out.println(" 1) List sensors"); - System.out.println(" 2) Add TemperatureSensor"); - System.out.println(" 3) Add WeightSensor"); - System.out.println(" 4) Start sensor"); - System.out.println(" 5) Stop sensor"); - System.out.println(" 6) Read sensor value"); - System.out.println(" 7) Toggle automatic mode"); - System.out.println(" 8) Set control (enable/disable + params)"); - System.out.println(" 9) Remove sensor"); - System.out.println("10) Find sensor by ID"); - System.out.println(" 0) Exit (stop & shutdown)"); + System.out.println("\n1-List 2-Add Temp 3-Add Weight 4-Start 5-Stop 6-Read 7-Auto 8-Control 9-Remove 10-Find 11-StartAll 12-StopAll 0-Exit"); } private static int readInt(String prompt) { System.out.print(prompt); - try { - return Integer.parseInt(scanner.nextLine().trim()); - } catch (NumberFormatException e) { - return -1; - } + try { return Integer.parseInt(scanner.nextLine().trim()); } catch (Exception e) { return -1; } } private static double readDouble(String prompt) { System.out.print(prompt); - try { - return Double.parseDouble(scanner.nextLine().trim()); - } catch (NumberFormatException e) { - return Double.NaN; - } + try { return Double.parseDouble(scanner.nextLine().trim()); } catch (Exception e) { return Double.NaN; } } + private static String prompt(String msg) { System.out.print(msg); return scanner.nextLine().trim(); } + private static void listSensors(SensorManager manager) { List infos = manager.listSensorInfo(); - if (infos.isEmpty()) { - System.out.println("No sensors registered."); - return; - } - for (String info : infos) System.out.println(info); + infos.forEach(System.out::println); } private static void addTemperatureSensor(SensorManager manager) { - System.out.println("Add TemperatureSensor:"); - String location = prompt("Location: "); - double start = readDouble("Start threshold: "); - double tol = readDouble("Tolerance: "); - double target = readDouble("Target temperature: "); - TemperatureSensor t = new TemperatureSensor("Temperature", location, "Init", start, tol, target, "°C"); + TemperatureSensor t = new TemperatureSensor("Temperature", prompt("Location: "), "Init", readDouble("Start: "), readDouble("Tol: "), readDouble("Target: "), "°C"); manager.addSensor(t); } private static void addWeightSensor(SensorManager manager) { - System.out.println("Add WeightSensor:"); - String location = prompt("Location: "); - double initial = readDouble("Initial weight: "); - double capacity = readDouble("Capacity: "); - WeightSensor w = new WeightSensor("Weight", location, "Init", initial, capacity, "kg"); + WeightSensor w = new WeightSensor("Weight", prompt("Location: "), "Init", readDouble("Initial: "), readDouble("Capacity: "), "kg"); manager.addSensor(w); } - private static void startSensor(SensorManager manager) { - int id = readInt("Sensor ID to start: "); - boolean ok = manager.startSensor(id); - System.out.println(ok ? "Started" : "Start failed"); - } - - private static void stopSensor(SensorManager manager) { - int id = readInt("Sensor ID to stop: "); - boolean ok = manager.stopSensor(id); - System.out.println(ok ? "Stopped" : "Stop failed"); - } - + private static void startSensor(SensorManager manager) { System.out.println(manager.startSensor(readInt("ID: ")) ? "Started" : "Failed"); } + private static void stopSensor(SensorManager manager) { System.out.println(manager.stopSensor(readInt("ID: ")) ? "Stopped" : "Failed"); } private static void readSensorValue(SensorManager manager) { - int id = readInt("Sensor ID to read: "); - Sensor s = manager.findSensorById(id); - if (s == null) { System.out.println("Not found"); return; } - s.readValue(); - } - - private static void toggleAutomatic(SensorManager manager) { - int id = readInt("Sensor ID: "); - int v = readInt("0 = disable, 1 = enable automatic: "); - boolean enabled = v == 1; - boolean ok = manager.setSensorAutomaticMode(id, enabled); - System.out.println(ok ? "Updated" : "Failed"); - } - - private static void setControl(SensorManager manager) { - int id = readInt("Sensor ID: "); - int v = readInt("0 = disable control, 1 = enable: "); - if (v == 0) { - boolean ok = manager.setSensorControl(id, false, 0, 0); - System.out.println(ok ? "Control disabled" : "Failed"); - return; - } - double target = readDouble("Target value: "); - double tol = readDouble("Tolerance: "); - boolean ok = manager.setSensorControl(id, true, target, tol); - System.out.println(ok ? "Control set" : "Failed"); - } - - private static void removeSensor(SensorManager manager) { - int id = readInt("Sensor ID to remove: "); - boolean ok = manager.removeSensor(id); - System.out.println(ok ? "Removed" : "Remove failed"); - } - - private static void findById(SensorManager manager) { - int id = readInt("Sensor ID: "); - Sensor s = manager.findSensorById(id); - if (s == null) System.out.println("Not found"); else System.out.println(s.getSensorInfo()); - } - - private static String prompt(String p) { - System.out.print(p); - return scanner.nextLine().trim(); + Sensor s = manager.findSensorById(readInt("ID: ")); + if (s != null) s.readValue(); } + private static void toggleAutomatic(SensorManager manager) { manager.setSensorAutomaticMode(readInt("ID: "), readInt("0/1: ") == 1); } + private static void setControl(SensorManager manager) { manager.setSensorControl(readInt("ID: "), readInt("0/1: ") == 1, readDouble("Target: "), readDouble("Tol: ")); } + private static void removeSensor(SensorManager manager) { manager.removeSensor(readInt("ID: ")); } + private static void findById(SensorManager manager) { System.out.println(manager.findSensorById(readInt("ID: ")).getSensorInfo()); } } diff --git a/src/main/java/org/Automation/controllers/SensorManager.java b/src/main/java/org/Automation/controllers/SensorManager.java index 707bc2d..0e0e45d 100644 --- a/src/main/java/org/Automation/controllers/SensorManager.java +++ b/src/main/java/org/Automation/controllers/SensorManager.java @@ -44,7 +44,9 @@ public SensorManager() { } } } catch (Exception e) { - System.err.println("Database init failed: " + e.getMessage()); + System.err.println("Database init failed:"); + e.printStackTrace(); + } this.sensorRepo = repo; } diff --git a/src/main/java/org/Automation/database/DatabaseManager.java b/src/main/java/org/Automation/database/DatabaseManager.java index 3751e32..7dbe56b 100644 --- a/src/main/java/org/Automation/database/DatabaseManager.java +++ b/src/main/java/org/Automation/database/DatabaseManager.java @@ -1,161 +1,153 @@ package org.automation.database; -import java.lang.ref.Cleaner; import java.sql.*; -public final class DatabaseManager { +public final class DatabaseManager implements AutoCloseable { - private Connection connection; - private final String url = "jdbc:sqlite:automation.sqlite"; + private Connection connection; + private static final String URL = "jdbc:sqlite:automation.sqlite"; - public boolean connect() { - if (connection != null) return true; + /** + * Connect to SQLite database + */ + public boolean connect() { + if (connection != null) return true; - try { - connection = DriverManager.getConnection(url); - System.out.println("Connection to SQLite established."); - return true; - } catch (SQLException e) { - throw new RuntimeException("Failed to connect", e); - } - } + try { + // 🔴 REQUIRED: force SQLite JDBC driver to load + Class.forName("org.sqlite.JDBC"); - public Connection getConnection() { - return this.connection; - } - - public boolean disconnect() { - try { - if (connection != null) { - connection.close(); - System.out.println("Connection closed."); - } - return true; - } catch (SQLException e) { - throw new RuntimeException("Failed to disconnect", e); + connection = DriverManager.getConnection(URL); + System.out.println("Connection to SQLite established."); + return true; + } catch (Exception e) { + throw new RuntimeException("Failed to connect", e); + } } - } - // ------------ Helper method for parameter binding ------------- - private void bindParams(PreparedStatement pstmt, Object[] params) throws SQLException { - if (params == null) - return; - - for (int i = 0; i < params.length; i++) { - pstmt.setObject(i + 1, params[i]); + public Connection getConnection() { + if (connection == null) { + throw new IllegalStateException("Database not connected"); + } + return connection; } - } - // ---------------- Helper method for SELECT queries ---------------- - private ResultSet executeQuery(String sql, Object[] params) { - try { - PreparedStatement pstmt = connection.prepareStatement(sql); - bindParams(pstmt, params); - return pstmt.executeQuery(); - } catch (SQLException e) { - throw new RuntimeException("Query execution failed: " + e.getMessage(), e); + /** + * Close DB connection + */ + public boolean disconnect() { + try { + if (connection != null && !connection.isClosed()) { + connection.close(); + System.out.println("Connection closed."); + } + connection = null; + return true; + } catch (SQLException e) { + throw new RuntimeException("Failed to disconnect", e); + } } - } - /** - * Safe query helper that maps rows using the provided RowMapper and - * ensures PreparedStatement and ResultSet are closed properly. - */ - public java.util.List query(String tableName, String where, Object[] params, org.automation.database.RowMapper mapper) { - String sql = "SELECT * FROM " + tableName + (where != null && !where.trim().isEmpty() ? " WHERE " + where : ""); - java.util.List result = new java.util.ArrayList<>(); - try (PreparedStatement pstmt = connection.prepareStatement(sql)) { - bindParams(pstmt, params); - try (ResultSet rs = pstmt.executeQuery()) { - while (rs.next()) { - result.add(mapper.mapRow(rs)); + /* ---------------- PARAM BINDING ---------------- */ + + private void bindParams(PreparedStatement pstmt, Object[] params) throws SQLException { + if (params == null) return; + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); } - } - } catch (SQLException e) { - throw new RuntimeException("Query execution failed: " + e.getMessage(), e); } - return result; - } - public T queryOne(String tableName, String where, Object[] params, org.automation.database.RowMapper mapper) { - java.util.List list = query(tableName, where, params, mapper); - return list.isEmpty() ? null : list.get(0); - } + /* ---------------- SELECT HELPERS ---------------- */ + + public java.util.List query( + String tableName, + String where, + Object[] params, + RowMapper mapper + ) { + String sql = "SELECT * FROM " + tableName + + (where != null && !where.trim().isEmpty() ? " WHERE " + where : ""); + + java.util.List result = new java.util.ArrayList<>(); + + try (PreparedStatement pstmt = getConnection().prepareStatement(sql)) { + bindParams(pstmt, params); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + result.add(mapper.mapRow(rs)); + } + } + } catch (SQLException e) { + throw new RuntimeException("Query execution failed", e); + } - /** - * Execute DDL statements such as CREATE TABLE. - */ - public boolean executeDDL(String sql) { - try (Statement stmt = connection.createStatement()) { - stmt.execute(sql); - return true; - } catch (SQLException e) { - throw new RuntimeException("DDL execution failed: " + e.getMessage(), e); + return result; } - } - // ---------------- Helper method for INSERT, UPDATE, DELETE ---------------- - private boolean executeMutator(String sql, Object[] params) { - try (PreparedStatement pstmt = connection.prepareStatement(sql)) { - bindParams(pstmt, params); - return pstmt.executeUpdate() > 0; - } catch (SQLException e) { - throw new RuntimeException("Mutator execution failed: " + e.getMessage(), e); + public T queryOne( + String tableName, + String where, + Object[] params, + RowMapper mapper + ) { + java.util.List list = query(tableName, where, params, mapper); + return list.isEmpty() ? null : list.get(0); } - } - // ------------ Find (SELECT) ------------- - public ResultSet find(String tableName, String where, Object[] params) { - String sql = "SELECT * FROM " + tableName; + /* ---------------- DDL ---------------- */ - if (where != null && !where.trim().isEmpty()) { - sql += " WHERE " + where; + public boolean executeDDL(String sql) { + try (Statement stmt = getConnection().createStatement()) { + stmt.execute(sql); + return true; + } catch (SQLException e) { + throw new RuntimeException("DDL execution failed", e); + } } - return executeQuery(sql, params); - } - - // ------------ INSERT ------------- - public boolean insert(String tableName, String[] columns, Object[] values) { - - if (columns.length != values.length) - throw new IllegalArgumentException("Columns and values length mismatch"); + /* ---------------- MUTATORS ---------------- */ - String cols = String.join(", ", columns); - - // creating placeholders (?) for the statement - String placeholders = ""; - for (int i = 0; i < values.length; i++) { - if (i < values.length - 1) { - placeholders += "?, "; - } else { - placeholders += "?"; + private boolean executeMutator(String sql, Object[] params) { + if (connection == null) connect(); + try (PreparedStatement pstmt = getConnection().prepareStatement(sql)) { + bindParams(pstmt, params); + return pstmt.executeUpdate() > 0; + } catch (SQLException e) { + throw new RuntimeException("Mutator execution failed", e); } - } + } - String sql = "INSERT INTO " + tableName + " (" + cols + ") VALUES (" + placeholders + ")"; - return executeMutator(sql, values); - } + public boolean insert(String tableName, String[] columns, Object[] values) { + if (columns.length != values.length) { + throw new IllegalArgumentException("Columns and values length mismatch"); + } - // ------------ DELETE ------------- - public boolean delete(String tableName, String where, Object[] params) { - String sql = "DELETE FROM " + tableName + - (where != null ? " WHERE " + where : ""); + String cols = String.join(", ", columns); + String placeholders = String.join(", ", java.util.Collections.nCopies(values.length, "?")); - return executeMutator(sql, params); - } + String sql = "INSERT INTO " + tableName + + " (" + cols + ") VALUES (" + placeholders + ")"; - // ------------ UPDATE ------------- - public boolean update(String tableName, String setClause, String where, Object[] params) { + return executeMutator(sql, values); + } - String sql = "UPDATE " + tableName + " SET " + setClause + - (where != null ? " WHERE " + where : ""); + public boolean update(String tableName, String setClause, String where, Object[] params) { + String sql = "UPDATE " + tableName + " SET " + setClause + + (where != null ? " WHERE " + where : ""); + return executeMutator(sql, params); + } - return executeMutator(sql, params); - } + public boolean delete(String tableName, String where, Object[] params) { + String sql = "DELETE FROM " + tableName + + (where != null ? " WHERE " + where : ""); + return executeMutator(sql, params); + } - void Cleaner(){ - disconnect(); - } + /* ---------------- CLEANUP ---------------- */ + + @Override + public void close() { + disconnect(); + } } diff --git a/src/main/java/org/Automation/repositories/SensorRepository.java b/src/main/java/org/Automation/repositories/SensorRepository.java index 373728c..5776f4d 100644 --- a/src/main/java/org/Automation/repositories/SensorRepository.java +++ b/src/main/java/org/Automation/repositories/SensorRepository.java @@ -8,187 +8,150 @@ import org.automation.entities.TemperatureSensor; import org.automation.entities.WeightSensor; -/** - * SensorRepository - * - * - Maps rows to concrete Sensor implementations (TemperatureSensor or WeightSensor) - * - Uses nullable columns for sensor-specific fields and provides sensible defaults - * - Creates a table schema that contains common and sensor-specific columns - */ public class SensorRepository extends Repository { public SensorRepository(DatabaseManager db) { super("Sensor", db); } - /** - * Ensure sensor table exists in DB - */ public boolean ensureTable() { - return db.executeDDL(createTableQuery()); + boolean created = db.executeDDL(createTableQuery()); + if (created) System.out.println("Sensor table ensured/created."); + return created; } - /** - * Persist a sensor into the Sensor table. Inserts only (no upsert for simplicity). - */ public boolean save(Sensor sensor) { if (sensor == null) return false; - String type = sensor.getSensorType(); - // If a row with this sensor ID exists -> UPDATE, otherwise INSERT (omit id to let SQLite autoincrement) - Sensor existing = findOne("id = ?", new Object[]{sensor.getSensorId()}); - try { - if (sensor instanceof TemperatureSensor) { - TemperatureSensor t = (TemperatureSensor) sensor; - if (existing != null) { - String set = "type = ?, location = ?, status = ?, startThreshold = ?, tolerance = ?, targetTemperature = ?, temperatureUnit = ?"; - Object[] params = new Object[]{t.getSensorType(), t.getLocation(), t.getStatus(), t.getStartThreshold(), t.getTemperatureTolerance(), t.getTargetTemperature(), t.getTemperatureUnit(), t.getSensorId()}; - return update(set, "id = ?", params); - } else { - // Insert with explicit id so in-memory id and DB id stay in sync. ID collision is avoided - // because we already checked for an existing row. - String[] cols = new String[]{"id","type","location","status","startThreshold","tolerance","targetTemperature","temperatureUnit"}; - Object[] vals = new Object[]{t.getSensorId(), t.getSensorType(), t.getLocation(), t.getStatus(), t.getStartThreshold(), t.getTemperatureTolerance(), t.getTargetTemperature(), t.getTemperatureUnit()}; - return insert(cols, vals); - } - } else if (sensor instanceof WeightSensor) { - WeightSensor w = (WeightSensor) sensor; - if (existing != null) { - String set = "type = ?, location = ?, status = ?, initialWeight = ?, capacity = ?, weightUnit = ?"; - Object[] params = new Object[]{w.getSensorType(), w.getLocation(), w.getStatus(), w.getCurrentWeight(), w.getMachineCapacity(), w.getWeightUnit(), w.getSensorId()}; - return update(set, "id = ?", params); - } else { - String[] cols = new String[]{"id","type","location","status","initialWeight","capacity","weightUnit"}; - Object[] vals = new Object[]{w.getSensorId(), w.getSensorType(), w.getLocation(), w.getStatus(), w.getCurrentWeight(), w.getMachineCapacity(), w.getWeightUnit()}; - return insert(cols, vals); - } - } else { - if (existing != null) { - String set = "type = ?, location = ?, status = ?"; - Object[] params = new Object[]{sensor.getSensorType(), sensor.getLocation(), sensor.getStatus(), sensor.getSensorId()}; - return update(set, "id = ?", params); - } else { - String[] cols = new String[]{"id","type","location","status"}; - Object[] vals = new Object[]{sensor.getSensorId(), sensor.getSensorType(), sensor.getLocation(), sensor.getStatus()}; - return insert(cols, vals); - } - } - } catch (RuntimeException re) { - throw re; + + boolean exists = findOne("id = ?", new Object[]{sensor.getSensorId()}) != null; + + if (sensor instanceof TemperatureSensor t) { + return exists ? updateTemperature(t) : insertTemperature(t); + } else if (sensor instanceof WeightSensor w) { + return exists ? updateWeight(w) : insertWeight(w); + } else { + return exists ? updateBase(sensor) : insertBase(sensor); } } + /* ---------------- INSERT ---------------- */ + + private boolean insertTemperature(TemperatureSensor t) { + return insert( + new String[]{"type", "location", "status", "startThreshold", "tolerance", "targetTemperature", "temperatureUnit"}, + new Object[]{t.getSensorType(), t.getLocation(), t.getStatus(), t.getStartThreshold(), t.getTemperatureTolerance(), t.getTargetTemperature(), t.getTemperatureUnit()} + ); + } + + private boolean insertWeight(WeightSensor w) { + return insert( + new String[]{"type", "location", "status", "initialWeight", "capacity", "weightUnit"}, + new Object[]{w.getSensorType(), w.getLocation(), w.getStatus(), w.getCurrentWeight(), w.getMachineCapacity(), w.getWeightUnit()} + ); + } + + private boolean insertBase(Sensor s) { + return insert( + new String[]{"type", "location", "status"}, + new Object[]{s.getSensorType(), s.getLocation(), s.getStatus()} + ); + } + + /* ---------------- UPDATE ---------------- */ + + private boolean updateTemperature(TemperatureSensor t) { + return update( + "type = ?, location = ?, status = ?, startThreshold = ?, tolerance = ?, targetTemperature = ?, temperatureUnit = ?", + "id = ?", + new Object[]{t.getSensorType(), t.getLocation(), t.getStatus(), t.getStartThreshold(), t.getTemperatureTolerance(), t.getTargetTemperature(), t.getTemperatureUnit(), t.getSensorId()} + ); + } + + private boolean updateWeight(WeightSensor w) { + return update( + "type = ?, location = ?, status = ?, initialWeight = ?, capacity = ?, weightUnit = ?", + "id = ?", + new Object[]{w.getSensorType(), w.getLocation(), w.getStatus(), w.getCurrentWeight(), w.getMachineCapacity(), w.getWeightUnit(), w.getSensorId()} + ); + } + + private boolean updateBase(Sensor s) { + return update( + "type = ?, location = ?, status = ?", + "id = ?", + new Object[]{s.getSensorType(), s.getLocation(), s.getStatus(), s.getSensorId()} + ); + } + public boolean deleteById(int id) { return delete("id = ?", new Object[]{id}); } - /** - * Map a ResultSet row to a concrete Sensor instance. - * Expects the table to contain columns for both temperature and weight sensors. - */ + /* ---------------- ROW MAPPING ---------------- */ + @Override public Sensor mapRow(ResultSet rs) throws SQLException { - int id = 0; - try { id = rs.getInt("id"); } catch (SQLException ignored) {} - String type = safeGetString(rs, "type", "Unknown"); + int sensorId = rs.getInt("id"); + String type = safeGetString(rs, "type", ""); String location = safeGetString(rs, "location", "Unknown"); String status = safeGetString(rs, "status", "inactive"); - // Temperature-specific columns (may be null for non-temperature rows) - double startThreshold = safeGetDouble(rs, "startThreshold", 0.0); - double tolerance = safeGetDouble(rs, "tolerance", 1.0); - double targetTemperature = safeGetDouble(rs, "targetTemperature", 25.0); - String tempUnit = safeGetString(rs, "temperatureUnit", "°C"); - - // Weight-specific columns (may be null for non-weight rows) - double initialWeight = safeGetDouble(rs, "initialWeight", 0.0); - double capacity = safeGetDouble(rs, "capacity", 100.0); - String weightUnit = safeGetString(rs, "weightUnit", "kg"); - - // Choose concrete type based on the 'type' column (case-insensitive) - if ("temperature".equalsIgnoreCase(type) || type.toLowerCase().contains("temp")) { - return new TemperatureSensor( - id, - type, - location, - status, - startThreshold, - tolerance, - targetTemperature, - tempUnit - ); - } else if ("weight".equalsIgnoreCase(type)) { - return new WeightSensor( - id, - type, - location, - status, - initialWeight, - capacity, - weightUnit + return switch (type.toLowerCase()) { + case "temperature" -> new TemperatureSensor( + sensorId, type, location, status, + safeGetDouble(rs, "startThreshold", 0), + safeGetDouble(rs, "tolerance", 1), + safeGetDouble(rs, "targetTemperature", 25), + safeGetString(rs, "temperatureUnit", "°C") ); - } else { - // Fallback: return a TemperatureSensor with safe defaults to avoid nulls - return new TemperatureSensor( - id, - type, - location, - status, - startThreshold, - tolerance, - targetTemperature, - tempUnit + case "weight" -> new WeightSensor( + sensorId, type, location, status, + safeGetDouble(rs, "initialWeight", 0), + safeGetDouble(rs, "capacity", 100), + safeGetString(rs, "weightUnit", "kg") ); - } + default -> throw new IllegalStateException("Unknown sensor type: " + type); + }; } - /** - * Table schema includes common columns plus nullable sensor-specific columns. - * This keeps a single Sensor table compatible with both TemperatureSensor and WeightSensor. - */ + /* ---------------- SCHEMA ---------------- */ + @Override public String createTableQuery() { return """ CREATE TABLE IF NOT EXISTS Sensor ( - id INTEGER PRIMARY KEY AUTOINCREMENT, + id INTEGER PRIMARY KEY, type TEXT NOT NULL, location TEXT, - machineId INTEGER, status TEXT DEFAULT 'inactive', - - -- Temperature sensor fields (nullable) startThreshold REAL, tolerance REAL, targetTemperature REAL, temperatureUnit TEXT, - - -- Weight sensor fields (nullable) initialWeight REAL, capacity REAL, - weightUnit TEXT, - - FOREIGN KEY(machineId) REFERENCES Machine(id) + weightUnit TEXT ); - """; + """; } - // ----------------------- - // Helper getters with defaults - // ----------------------- - private String safeGetString(ResultSet rs, String column, String defaultValue) { + /* ---------------- HELPERS ---------------- */ + + private String safeGetString(ResultSet rs, String column, String def) { try { - String v = rs.getString(column); - return v == null ? defaultValue : v; + String val = rs.getString(column); + return val != null ? val : def; } catch (SQLException e) { - return defaultValue; + return def; } } - private double safeGetDouble(ResultSet rs, String column, double defaultValue) { + private double safeGetDouble(ResultSet rs, String column, double def) { try { - double v = rs.getDouble(column); - return rs.wasNull() ? defaultValue : v; + double val = rs.getDouble(column); + return rs.wasNull() ? def : val; } catch (SQLException e) { - return defaultValue; + return def; } } } diff --git a/src/main/java/org/Automation/ui/ConsoleApp.java b/src/main/java/org/Automation/ui/ConsoleApp.java index 442e9f6..313b460 100644 --- a/src/main/java/org/Automation/ui/ConsoleApp.java +++ b/src/main/java/org/Automation/ui/ConsoleApp.java @@ -1,37 +1,343 @@ -package org.automation.ui; -import org.automation.engine.SimulationEngine; -import org.automation.controllers.WorkflowController; -import org.automation.utils.Logger; +// package org.automation.ui; -public class ConsoleApp extends ConsoleUI { - //Instance Variables - private SimulationEngine simulationEngine; - private WorkflowController controller; - private Logger logger; - - ConsoleApp(){ - simulationEngine= new SimulationEngine(); - } - - //Instance Methods - public void start() { - simulationEngine.startSimulation(); - - } +// import org.automation.engine.SimulationEngine; +// import org.automation.controllers.WorkflowController; +// import org.automation.controllers.SensorManager; +// import org.automation.entities.Sensor; +// import org.automation.entities.TemperatureSensor; +// import org.automation.entities.WeightSensor; +// import org.automation.utils.Logger; + +// import java.util.List; +// import java.util.Scanner; + +// public class ConsoleApp extends ConsoleUI { +// //Instance Variables +// private SimulationEngine simulationEngine; +// private WorkflowController controller; +// private SensorManager sensorManager; +// private Logger logger; +// private Scanner scanner; +// private boolean running; - public void runMainMenu() { - - } +// public ConsoleApp(){ +// simulationEngine = new SimulationEngine(); +// sensorManager = new SensorManager(); +// logger = new Logger(); +// scanner = new Scanner(System.in); +// running = false; +// } - public void handleUserInput(String input) { - - } +// //Instance Methods +// public void start() { +// running = true; +// printWelcomeMessage(); +// runMainMenu(); +// } + +// public void runMainMenu() { +// while (running) { +// printMainMenu(); +// String input = scanner.nextLine().trim(); +// handleUserInput(input); +// } +// } - public void printWelcomeMessage() { +// private void printMainMenu() { +// System.out.println("\n╔═══════════════════════════════════════════════════════════════╗"); +// System.out.println("║ FACTORY AUTOMATION - SENSOR MANAGEMENT SYSTEM ║"); +// System.out.println("╠═══════════════════════════════════════════════════════════════╣"); +// System.out.println("║ [1] Add New Sensor ║"); +// System.out.println("║ [2] Remove Sensor ║"); +// System.out.println("║ [3] List All Sensors ║"); +// System.out.println("║ [4] View Sensor Details ║"); +// System.out.println("║ [5] Start Sensor ║"); +// System.out.println("║ [6] Stop Sensor ║"); +// System.out.println("║ [7] Start All Sensors ║"); +// System.out.println("║ [8] Stop All Sensors ║"); +// System.out.println("║ [9] Enable Automatic Mode (Single Sensor) ║"); +// System.out.println("║ [10] Disable Automatic Mode (Single Sensor) ║"); +// System.out.println("║ [11] Enable Control Mode (Single Sensor) ║"); +// System.out.println("║ [12] Disable Control Mode (Single Sensor) ║"); +// System.out.println("║ [13] Enable Automatic Mode (All Sensors) ║"); +// System.out.println("║ [14] Disable Automatic Mode (All Sensors) ║"); +// System.out.println("║ [0] Exit ║"); +// System.out.println("╚═══════════════════════════════════════════════════════════════╝"); +// System.out.print("Select an option: "); +// } + +// public void handleUserInput(String input) { +// switch (input) { +// case "1" -> addSensorMenu(); +// case "2" -> removeSensorMenu(); +// case "3" -> listAllSensors(); +// case "4" -> viewSensorDetails(); +// case "5" -> startSensorMenu(); +// case "6" -> stopSensorMenu(); +// case "7" -> startAllSensors(); +// case "8" -> stopAllSensors(); +// case "9" -> enableAutomaticModeMenu(); +// case "10" -> disableAutomaticModeMenu(); +// case "11" -> enableControlModeMenu(); +// case "12" -> disableControlModeMenu(); +// case "13" -> enableAutomaticModeAll(); +// case "14" -> disableAutomaticModeAll(); +// case "0" -> exit(); +// default -> System.out.println("❌ Invalid option. Please try again."); +// } +// } + +// // =========================== +// // Menu Options Implementation +// // =========================== + +// private void addSensorMenu() { +// System.out.println("\n--- Add New Sensor ---"); +// System.out.println("Select sensor type:"); +// System.out.println("[1] Temperature Sensor"); +// System.out.println("[2] Weight Sensor"); +// System.out.print("Choice: "); +// String choice = scanner.nextLine().trim(); - } +// try { +// System.out.print("Enter Sensor ID: "); +// int id = Integer.parseInt(scanner.nextLine().trim()); + +// System.out.print("Enter Location: "); +// String location = scanner.nextLine().trim(); + +// Sensor sensor = null; + +// if (choice.equals("1")) { +// // Temperature Sensor +// System.out.print("Enter Start Threshold: "); +// double startThreshold = Double.parseDouble(scanner.nextLine().trim()); + +// System.out.print("Enter Tolerance: "); +// double tolerance = Double.parseDouble(scanner.nextLine().trim()); + +// System.out.print("Enter Target Temperature: "); +// double targetTemp = Double.parseDouble(scanner.nextLine().trim()); + +// System.out.print("Enter Temperature Unit (°C/°F): "); +// String unit = scanner.nextLine().trim(); + +// sensor = new TemperatureSensor(id, "Temperature", location, "inactive", +// startThreshold, tolerance, targetTemp, unit); +// } else if (choice.equals("2")) { +// // Weight Sensor +// System.out.print("Enter Initial Weight: "); +// double initialWeight = Double.parseDouble(scanner.nextLine().trim()); + +// System.out.print("Enter Capacity: "); +// double capacity = Double.parseDouble(scanner.nextLine().trim()); + +// System.out.print("Enter Weight Unit (kg/lb): "); +// String unit = scanner.nextLine().trim(); + +// sensor = new WeightSensor(id, "Weight", location, "inactive", +// initialWeight, capacity, unit); +// } else { +// System.out.println("❌ Invalid sensor type."); +// return; +// } + +// sensorManager.addSensor(sensor); +// System.out.println("✅ Sensor added successfully and saved to database!"); + +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid input. Please enter valid numbers."); +// } catch (Exception e) { +// System.out.println("❌ Error adding sensor: " + e.getMessage()); +// } +// } - public void exit() { +// private void removeSensorMenu() { +// System.out.println("\n--- Remove Sensor ---"); +// System.out.print("Enter Sensor ID to remove: "); +// try { +// int id = Integer.parseInt(scanner.nextLine().trim()); +// if (sensorManager.removeSensor(id)) { +// System.out.println("✅ Sensor removed successfully from database!"); +// } else { +// System.out.println("❌ Sensor not found."); +// } +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid ID format."); +// } +// } + +// private void listAllSensors() { +// System.out.println("\n╔═════════════════════════════════════════════════════════╗"); +// System.out.println("║ ALL SENSORS LIST ║"); +// System.out.println("╚═════════════════════════════════════════════════════════╝"); - } -} +// List sensorInfo = sensorManager.listSensorInfo(); +// if (sensorInfo.isEmpty()) { +// System.out.println("📭 No sensors found in the system."); +// } else { +// for (int i = 0; i < sensorInfo.size(); i++) { +// System.out.println((i + 1) + ". " + sensorInfo.get(i)); +// } +// } +// } + +// private void viewSensorDetails() { +// System.out.println("\n--- View Sensor Details ---"); +// System.out.print("Enter Sensor ID: "); +// try { +// int id = Integer.parseInt(scanner.nextLine().trim()); +// String info = sensorManager.getSensorInfo(id); +// if (info != null) { +// System.out.println("\n📊 Sensor Details:"); +// System.out.println(info); +// } else { +// System.out.println("❌ Sensor not found."); +// } +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid ID format."); +// } +// } + +// private void startSensorMenu() { +// System.out.println("\n--- Start Sensor ---"); +// System.out.print("Enter Sensor ID: "); +// try { +// int id = Integer.parseInt(scanner.nextLine().trim()); +// if (sensorManager.startSensor(id)) { +// System.out.println("✅ Sensor started successfully!"); +// } else { +// System.out.println("❌ Failed to start sensor. It may already be running or not exist."); +// } +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid ID format."); +// } +// } + +// private void stopSensorMenu() { +// System.out.println("\n--- Stop Sensor ---"); +// System.out.print("Enter Sensor ID: "); +// try { +// int id = Integer.parseInt(scanner.nextLine().trim()); +// if (sensorManager.stopSensor(id)) { +// System.out.println("✅ Sensor stopped successfully!"); +// } else { +// System.out.println("❌ Failed to stop sensor. It may already be stopped or not exist."); +// } +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid ID format."); +// } +// } + +// private void startAllSensors() { +// System.out.println("\n⚙️ Starting all sensors..."); +// sensorManager.startAll(); +// System.out.println("✅ All sensors have been started!"); +// } + +// private void stopAllSensors() { +// System.out.println("\n⚙️ Stopping all sensors..."); +// sensorManager.stopAll(); +// System.out.println("✅ All sensors have been stopped!"); +// } + +// private void enableAutomaticModeMenu() { +// System.out.println("\n--- Enable Automatic Mode ---"); +// System.out.print("Enter Sensor ID: "); +// try { +// int id = Integer.parseInt(scanner.nextLine().trim()); +// if (sensorManager.setSensorAutomaticMode(id, true)) { +// System.out.println("✅ Automatic mode enabled for sensor!"); +// } else { +// System.out.println("❌ Sensor not found."); +// } +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid ID format."); +// } +// } + +// private void disableAutomaticModeMenu() { +// System.out.println("\n--- Disable Automatic Mode ---"); +// System.out.print("Enter Sensor ID: "); +// try { +// int id = Integer.parseInt(scanner.nextLine().trim()); +// if (sensorManager.setSensorAutomaticMode(id, false)) { +// System.out.println("✅ Automatic mode disabled for sensor!"); +// } else { +// System.out.println("❌ Sensor not found."); +// } +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid ID format."); +// } +// } + +// private void enableControlModeMenu() { +// System.out.println("\n--- Enable Control Mode ---"); +// System.out.print("Enter Sensor ID: "); +// try { +// int id = Integer.parseInt(scanner.nextLine().trim()); +// System.out.print("Enter Target Value: "); +// double target = Double.parseDouble(scanner.nextLine().trim()); +// System.out.print("Enter Tolerance: "); +// double tolerance = Double.parseDouble(scanner.nextLine().trim()); + +// if (sensorManager.setSensorControl(id, true, target, tolerance)) { +// System.out.println("✅ Control mode enabled for sensor!"); +// } else { +// System.out.println("❌ Sensor not found."); +// } +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid input format."); +// } +// } + +// private void disableControlModeMenu() { +// System.out.println("\n--- Disable Control Mode ---"); +// System.out.print("Enter Sensor ID: "); +// try { +// int id = Integer.parseInt(scanner.nextLine().trim()); +// if (sensorManager.setSensorControl(id, false, 0, 0)) { +// System.out.println("✅ Control mode disabled for sensor!"); +// } else { +// System.out.println("❌ Sensor not found."); +// } +// } catch (NumberFormatException e) { +// System.out.println("❌ Invalid ID format."); +// } +// } + +// private void enableAutomaticModeAll() { +// System.out.println("\n⚙️ Enabling automatic mode for all sensors..."); +// sensorManager.setAllAutomaticMode(true); +// System.out.println("✅ Automatic mode enabled for all sensors!"); +// } + +// private void disableAutomaticModeAll() { +// System.out.println("\n⚙️ Disabling automatic mode for all sensors..."); +// sensorManager.setAllAutomaticMode(false); +// System.out.println("✅ Automatic mode disabled for all sensors!"); +// } + +// public void printWelcomeMessage() { +// System.out.println("\n"); +// System.out.println("╔════════════════════════════════════════════════════════════════════╗"); +// System.out.println("║ ║"); +// System.out.println("║ 🏭 FACTORY AUTOMATION - SENSOR MANAGEMENT SYSTEM 🏭 ║"); +// System.out.println("║ ║"); +// System.out.println("║ Integrated Database-Driven Sensor Control Interface ║"); +// System.out.println("║ ║"); +// System.out.println("╚════════════════════════════════════════════════════════════════════╝"); +// System.out.println("\n✨ Welcome! All sensors are automatically loaded from the database."); +// System.out.println("💾 All changes are persisted to the database in real-time.\n"); +// } + +// public void exit() { +// System.out.println("\n🛑 Shutting down system..."); +// System.out.println("⏳ Stopping all sensors..."); +// sensorManager.shutdown(5); +// System.out.println("💾 Database disconnected."); +// System.out.println("✅ System shutdown complete. Goodbye! 👋\n"); +// scanner.close(); +// running = false; +// } +// } From 88e5dfb88d89894c13de76e40ac7898bcba405cd Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 25 Dec 2025 01:14:23 +0300 Subject: [PATCH 5/5] Update SensorManager.java --- src/main/java/org/Automation/Controllers/SensorManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/Automation/Controllers/SensorManager.java b/src/main/java/org/Automation/Controllers/SensorManager.java index 707bc2d..0e0e45d 100644 --- a/src/main/java/org/Automation/Controllers/SensorManager.java +++ b/src/main/java/org/Automation/Controllers/SensorManager.java @@ -44,7 +44,9 @@ public SensorManager() { } } } catch (Exception e) { - System.err.println("Database init failed: " + e.getMessage()); + System.err.println("Database init failed:"); + e.printStackTrace(); + } this.sensorRepo = repo; }