commit 16/06/2025

This commit is contained in:
2025-06-17 13:38:09 +07:00
parent fd1b53e3a8
commit f2bb3500c9
26 changed files with 227 additions and 24 deletions

View File

@@ -1,21 +0,0 @@
<component name="libraryTable">
<library name="digitalpetri.modbus.master.tcp" type="repository">
<properties maven-id="com.digitalpetri.modbus:modbus-master-tcp:1.2.2" />
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/modbus-master-tcp-1.2.2.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/modbus-codec-1.2.2.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/modbus-core-1.2.2.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-buffer-4.1.105.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-codec-4.1.105.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-common-4.1.105.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-transport-4.1.105.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-resolver-4.1.105.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-channel-fsm-0.9.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/strict-machine-0.7.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/metrics-core-3.1.5.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/slf4j-api-1.7.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@@ -0,0 +1,21 @@
<component name="libraryTable">
<library name="digitalpetri.modbus.tcp" type="repository">
<properties maven-id="com.digitalpetri.modbus:modbus-tcp:2.1.0" />
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/modbus-tcp-2.1.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/modbus-2.1.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-buffer-4.1.116.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-common-4.1.116.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-codec-4.1.116.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-transport-4.1.116.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-handler-4.1.116.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-resolver-4.1.116.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-transport-native-unix-common-4.1.116.Final.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-channel-fsm-1.0.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/strict-machine-1.0.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/slf4j-api-2.0.16.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

11
.idea/libraries/sun_mail_jakarta.xml generated Normal file
View File

@@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="sun.mail.jakarta" type="repository">
<properties maven-id="com.sun.mail:jakarta.mail:2.0.1" />
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/jakarta.mail-2.0.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jakarta.activation-2.0.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@@ -8,12 +8,13 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="io.javalin" level="project" />
<orderEntry type="library" name="digitalpetri.modbus.master.tcp" level="project" />
<orderEntry type="library" name="projectlombok.lombok" level="project" />
<orderEntry type="library" name="google.code.gson" level="project" />
<orderEntry type="library" name="tinylog.impl" level="project" />
<orderEntry type="library" name="tinylog.api" level="project" />
<orderEntry type="library" name="hivemq.mqtt.client" level="project" />
<orderEntry type="library" name="xerial.sqlite.jdbc" level="project" />
<orderEntry type="library" name="sun.mail.jakarta" level="project" />
<orderEntry type="library" name="digitalpetri.modbus.tcp" level="project" />
</component>
</module>

Binary file not shown.

Binary file not shown.

BIN
lib/jakarta.mail-1.6.3.jar Normal file

Binary file not shown.

BIN
lib/jakarta.mail-2.0.1.jar Normal file

Binary file not shown.

BIN
lib/modbus-2.1.0.jar Normal file

Binary file not shown.

Binary file not shown.

BIN
lib/modbus-tcp-2.1.0.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/slf4j-api-2.0.16.jar Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -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();
}));
}
}

63
src/mail/SMTPSender.java Normal file
View File

@@ -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);
}
}
}

View File

@@ -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;
}
}