diff --git a/.idea/libraries/digitalpetri_modbus_master_tcp.xml b/.idea/libraries/digitalpetri_modbus_master_tcp.xml deleted file mode 100644 index 067876d..0000000 --- a/.idea/libraries/digitalpetri_modbus_master_tcp.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/digitalpetri_modbus_tcp.xml b/.idea/libraries/digitalpetri_modbus_tcp.xml new file mode 100644 index 0000000..cc61f4a --- /dev/null +++ b/.idea/libraries/digitalpetri_modbus_tcp.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/sun_mail_jakarta.xml b/.idea/libraries/sun_mail_jakarta.xml new file mode 100644 index 0000000..20a025d --- /dev/null +++ b/.idea/libraries/sun_mail_jakarta.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/FA_Gateway_Java.iml b/FA_Gateway_Java.iml index eecfc80..1404e67 100644 --- a/FA_Gateway_Java.iml +++ b/FA_Gateway_Java.iml @@ -8,12 +8,13 @@ - + + \ No newline at end of file diff --git a/lib/jakarta.activation-1.2.1.jar b/lib/jakarta.activation-1.2.1.jar new file mode 100644 index 0000000..fa6aad2 Binary files /dev/null and b/lib/jakarta.activation-1.2.1.jar differ diff --git a/lib/jakarta.activation-2.0.1.jar b/lib/jakarta.activation-2.0.1.jar new file mode 100644 index 0000000..521c7c4 Binary files /dev/null and b/lib/jakarta.activation-2.0.1.jar differ diff --git a/lib/jakarta.mail-1.6.3.jar b/lib/jakarta.mail-1.6.3.jar new file mode 100644 index 0000000..71ac5b9 Binary files /dev/null and b/lib/jakarta.mail-1.6.3.jar differ diff --git a/lib/jakarta.mail-2.0.1.jar b/lib/jakarta.mail-2.0.1.jar new file mode 100644 index 0000000..17e07cc Binary files /dev/null and b/lib/jakarta.mail-2.0.1.jar differ diff --git a/lib/modbus-2.1.0.jar b/lib/modbus-2.1.0.jar new file mode 100644 index 0000000..a997135 Binary files /dev/null and b/lib/modbus-2.1.0.jar differ diff --git a/lib/modbus-slave-tcp-1.2.2.jar b/lib/modbus-slave-tcp-1.2.2.jar new file mode 100644 index 0000000..835c0b6 Binary files /dev/null and b/lib/modbus-slave-tcp-1.2.2.jar differ diff --git a/lib/modbus-tcp-2.1.0.jar b/lib/modbus-tcp-2.1.0.jar new file mode 100644 index 0000000..a626fea Binary files /dev/null and b/lib/modbus-tcp-2.1.0.jar differ diff --git a/lib/netty-buffer-4.1.116.Final.jar b/lib/netty-buffer-4.1.116.Final.jar new file mode 100644 index 0000000..b6f71eb Binary files /dev/null and b/lib/netty-buffer-4.1.116.Final.jar differ diff --git a/lib/netty-channel-fsm-1.0.0.jar b/lib/netty-channel-fsm-1.0.0.jar new file mode 100644 index 0000000..8067c49 Binary files /dev/null and b/lib/netty-channel-fsm-1.0.0.jar differ diff --git a/lib/netty-codec-4.1.116.Final.jar b/lib/netty-codec-4.1.116.Final.jar new file mode 100644 index 0000000..bf763bf Binary files /dev/null and b/lib/netty-codec-4.1.116.Final.jar differ diff --git a/lib/netty-common-4.1.116.Final.jar b/lib/netty-common-4.1.116.Final.jar new file mode 100644 index 0000000..ee26424 Binary files /dev/null and b/lib/netty-common-4.1.116.Final.jar differ diff --git a/lib/netty-handler-4.1.105.Final.jar b/lib/netty-handler-4.1.105.Final.jar new file mode 100644 index 0000000..959cfd7 Binary files /dev/null and b/lib/netty-handler-4.1.105.Final.jar differ diff --git a/lib/netty-handler-4.1.116.Final.jar b/lib/netty-handler-4.1.116.Final.jar new file mode 100644 index 0000000..b0dc2b7 Binary files /dev/null and b/lib/netty-handler-4.1.116.Final.jar differ diff --git a/lib/netty-resolver-4.1.116.Final.jar b/lib/netty-resolver-4.1.116.Final.jar new file mode 100644 index 0000000..9dd8a80 Binary files /dev/null and b/lib/netty-resolver-4.1.116.Final.jar differ diff --git a/lib/netty-transport-4.1.116.Final.jar b/lib/netty-transport-4.1.116.Final.jar new file mode 100644 index 0000000..4afc4cc Binary files /dev/null and b/lib/netty-transport-4.1.116.Final.jar differ diff --git a/lib/netty-transport-native-unix-common-4.1.105.Final.jar b/lib/netty-transport-native-unix-common-4.1.105.Final.jar new file mode 100644 index 0000000..5523194 Binary files /dev/null and b/lib/netty-transport-native-unix-common-4.1.105.Final.jar differ diff --git a/lib/netty-transport-native-unix-common-4.1.116.Final.jar b/lib/netty-transport-native-unix-common-4.1.116.Final.jar new file mode 100644 index 0000000..75f9010 Binary files /dev/null and b/lib/netty-transport-native-unix-common-4.1.116.Final.jar differ diff --git a/lib/slf4j-api-2.0.16.jar b/lib/slf4j-api-2.0.16.jar new file mode 100644 index 0000000..cbb5448 Binary files /dev/null and b/lib/slf4j-api-2.0.16.jar differ diff --git a/lib/strict-machine-1.0.0.jar b/lib/strict-machine-1.0.0.jar new file mode 100644 index 0000000..16b4526 Binary files /dev/null and b/lib/strict-machine-1.0.0.jar differ diff --git a/src/Main.java b/src/Main.java index cb138c6..9ca833f 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,6 +1,8 @@ import database.ContactInputData; import database.Database; import gpio.NanopiGpio; +import mail.SMTPSender; +import modbus.ModbusTCPServer; import mqtt.MqttClient; import org.tinylog.Logger; import pa.VX3K; @@ -14,6 +16,8 @@ public class Main { private static NanopiGpio gpio; private static Database db; private static VX3K vx3K; + private static SMTPSender mailsender; + private static ModbusTCPServer modbusServer; // Application entry point public static void main(String[] args) { Logger.info("Application started"); @@ -28,6 +32,12 @@ public class Main { // initialize VX3K vx3K = new VX3K(config.getVX3KTargetIP(), config.getVX3KTargetPort()); + // initialize Email Sender + mailsender = new SMTPSender(config.getEmail_SMTPServer(), config.getEmail_SMTPPort(), true, config.getEmail_SMTPUsername(), config.getEmail_SMTPPassword()); + + modbusServer = new ModbusTCPServer(config.getModbus_MasterIP(), config.getModbus_Port(), 1000); + modbusServer.Start(); + // Initialize the GPIO pins gpio = new NanopiGpio(); // cek di https://wiki.friendlyelec.com/wiki/index.php/NanoPi_Duo20 @@ -38,7 +48,6 @@ public class Main { Logger.error("GPIO pins closed or failed to open."); } }, pinStatus -> { - // TODO Handle pin status updates here Logger.info("Gpio {}, Description {}, status updated to {}", pinStatus.getGpioNumber(), pinStatus.getDescription(), pinStatus.getStatus()); ContactInputData cib = db.GetContactInputData(pinStatus.getDescription()); @@ -57,6 +66,15 @@ public class Main { } if (cib.isEnableEmail()){ // Email notification can be added here + String subject = String.format("Fire Alarm Gateway - Pin %d Status Update", pinStatus.getGpioNumber()); + String body = String.format("Pin %d with description '%s' has changed status to %d.", pinStatus.getGpioNumber(), pinStatus.getDescription(), pinStatus.getStatus()); + mailsender.SendEmail(config.getEmail_SMTPFrom(), cib.getEmailRecipient(), subject, body, sent -> { + if (sent) { + Logger.info("Email notification sent successfully."); + } else { + Logger.error("Failed to send email notification."); + } + }); } if (cib.isEnableVX3K()){ // VX3K Broadcast here @@ -70,7 +88,9 @@ public class Main { }); } if (cib.isEnableModbus()){ - // update Modbus Register here + if (modbusServer!=null){ + modbusServer.SetRegister(cib.getContactID(), pinStatus.getStatus()); + } } } @@ -108,6 +128,7 @@ public class Main { Runtime.getRuntime().addShutdownHook(new Thread(() -> { if (gpio!=null && gpio.IsOpened()) gpio.Close(); if (mqttClient!=null && mqttClient.isConnected()) mqttClient.Disconnect(); + if (modbusServer!=null && modbusServer.isRunning()) modbusServer.Stop(); })); } } \ No newline at end of file diff --git a/src/mail/SMTPSender.java b/src/mail/SMTPSender.java new file mode 100644 index 0000000..b74d760 --- /dev/null +++ b/src/mail/SMTPSender.java @@ -0,0 +1,63 @@ +package mail; + +import jakarta.mail.*; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import lombok.NonNull; + +import java.util.Properties; +import java.util.function.Consumer; + +public class SMTPSender { + final Properties props; + final String username; + final String password; + + /** + * Constructor for SMTPSender + * @param smtpHost the SMTP server host + * @param port the SMTP server port + * @param useSSL true to use SSL, false to use STARTTLS + * @param username the username for SMTP authentication + * @param password the password for SMTP authentication + */ + public SMTPSender(String smtpHost, int port, boolean useSSL, String username, String password){ + props = new Properties(); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.starttls.enable", String.valueOf(useSSL)); // Enable STARTTLS + props.put("mail.smtp.host", smtpHost); + props.put("mail.smtp.port", String.valueOf(port)); + this.username = username; + this.password = password; + } + + /** + * Sends an email using the configured SMTP settings. + * + * @param fromEmail the sender's email address + * @param toEmail the recipient's email address + * @param Subject the subject of the email + * @param body the body of the email + * @param success a callback that receives true if the email was sent successfully, false otherwise + */ + public void SendEmail(String fromEmail, String toEmail, String Subject, String body, @NonNull Consumer<@NonNull Boolean> success){ + try{ + Session session = Session.getInstance(props, new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(fromEmail)); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail)); + message.setSubject(Subject); + message.setText(body); + + Transport.send(message); + success.accept(true); + } catch (Exception e){ + success.accept(false); + } + + } +} diff --git a/src/modbus/ModbusTCPServer.java b/src/modbus/ModbusTCPServer.java new file mode 100644 index 0000000..3b61500 --- /dev/null +++ b/src/modbus/ModbusTCPServer.java @@ -0,0 +1,107 @@ +package modbus; + +import com.digitalpetri.modbus.exceptions.ModbusResponseException; +import com.digitalpetri.modbus.exceptions.UnknownUnitIdException; +import com.digitalpetri.modbus.pdu.ReadHoldingRegistersRequest; +import com.digitalpetri.modbus.pdu.ReadHoldingRegistersResponse; +import com.digitalpetri.modbus.server.ModbusRequestContext; +import com.digitalpetri.modbus.server.ModbusServices; +import com.digitalpetri.modbus.server.ModbusTcpServer; +import com.digitalpetri.modbus.tcp.server.NettyTcpServerTransport; +import lombok.Getter; +import org.tinylog.Logger; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; +import java.util.concurrent.atomic.AtomicIntegerArray; + + +public class ModbusTCPServer { + private @Getter boolean isRunning = false; + private ModbusTcpServer server; + + private final NettyTcpServerTransport transport; + private final ModbusServices services; + private final AtomicIntegerArray holdingregister; + + /** + * Creates a Modbus TCP server that listens on the specified bind address and port. + * + * @param bindAddress the IP address to bind the server to + * @param port the port number to listen on + * @param holdingRegisterSize size of the holding register array + */ + public ModbusTCPServer(String bindAddress, int port, int holdingRegisterSize) { + this.holdingregister = new AtomicIntegerArray(holdingRegisterSize); + transport = NettyTcpServerTransport.create(cfg ->{ + cfg.bindAddress = bindAddress; + cfg.port = port; + }); + services = new ModbusServices() { + @SuppressWarnings("RedundantThrows") + @Override + public ReadHoldingRegistersResponse readHoldingRegisters(ModbusRequestContext context, int unitId, ReadHoldingRegistersRequest request) throws ModbusResponseException, UnknownUnitIdException { + ByteBuffer result = ByteBuffer.allocate(request.quantity()*2); + ShortBuffer xx = result.asShortBuffer(); + for (int i = 0; i < request.quantity(); i++) { + int value = holdingregister.get(request.address() + i); + xx.put((short) value); + } + return new ReadHoldingRegistersResponse(result.array()); + } + }; + } + + /** + * Sets the value of a holding register at the specified address. + * @param address the address of the holding register (0-based index) + * @param value the value to set (should be in the range of a 16-bit integer) + */ + public void SetRegister(int address, int value) { + if (address < 0 || address >= holdingregister.length()) { + throw new IndexOutOfBoundsException("Address out of bounds: " + address); + } + holdingregister.set(address, (short) value); + } + + /** + * Start the Modbus TCP server. + * @return true if the server started successfully, false otherwise. + */ + public boolean Start(){ + isRunning = false; + try{ + var newserver = ModbusTcpServer.create(transport, services); + newserver.start(); + server = newserver; + isRunning = true; + Logger.info("Modbus TCP server started"); + } catch (Exception e){ + Logger.error("Failed to start Modbus TCP server: {}", e.getMessage()); + } + return isRunning; + } + + /** + * Stop the Modbus TCP server. + */ + public void Stop(){ + if (isRunning){ + if (server!=null){ + try { + server.stop(); + Logger.info("Modbus TCP server stopped successfully."); + } catch (Exception e) { + Logger.error("Failed to stop Modbus TCP server: {}", e.getMessage()); + } + server = null; + } else { + Logger.warn("Modbus TCP server instance is null, cannot stop."); + } + + } else { + Logger.warn("Modbus TCP server is not running."); + } + isRunning = false; + } +}