diff --git a/src/Main.java b/src/Main.java index 3cc656e..cb138c6 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,7 +1,10 @@ +import database.ContactInputData; import database.Database; import gpio.NanopiGpio; import mqtt.MqttClient; import org.tinylog.Logger; +import pa.VX3K; +import pa.VX3KPseudoContactInput; //TIP To Run code, press or // click the icon in the gutter. @@ -10,13 +13,21 @@ public class Main { private static MqttClient mqttClient; private static NanopiGpio gpio; private static Database db; + private static VX3K vx3K; // Application entry point public static void main(String[] args) { Logger.info("Application started"); + + // initialize config config = new config(); + + // initialize database db = new Database(); //db.contactInputDataList.forEach(System.out::println); + // initialize VX3K + vx3K = new VX3K(config.getVX3KTargetIP(), config.getVX3KTargetPort()); + // Initialize the GPIO pins gpio = new NanopiGpio(); // cek di https://wiki.friendlyelec.com/wiki/index.php/NanoPi_Duo20 @@ -29,17 +40,41 @@ public class Main { }, pinStatus -> { // TODO Handle pin status updates here Logger.info("Gpio {}, Description {}, status updated to {}", pinStatus.getGpioNumber(), pinStatus.getDescription(), pinStatus.getStatus()); - // MQTT publish pin status update - if (mqttClient != null && mqttClient.isConnected()) mqttClient.Publish(config.getMQTT_Topic(), config.getMQTT_ClientID(), - String.format("Gpio %d, Description %s, Status %d", pinStatus.getGpioNumber(), pinStatus.getDescription(), pinStatus.getStatus()), - published -> { - if (published) { - Logger.info("Pin status update published successfully."); + + ContactInputData cib = db.GetContactInputData(pinStatus.getDescription()); + if (cib!=null){ + if (cib.isEnableMQTT()){ + // MQTT publish pin status update + if (mqttClient != null && mqttClient.isConnected()) mqttClient.Publish(config.getMQTT_Topic(), config.getMQTT_ClientID(), + String.format("Gpio %d, Description %s, Status %d", pinStatus.getGpioNumber(), pinStatus.getDescription(), pinStatus.getStatus()), + published -> { + if (published) { + Logger.info("Pin status update published successfully."); + } else { + Logger.error("Failed to publish pin status update."); + } + }); + } + if (cib.isEnableEmail()){ + // Email notification can be added here + } + if (cib.isEnableVX3K()){ + // VX3K Broadcast here + VX3KPseudoContactInput cmd = new VX3KPseudoContactInput(cib.getVX3KFrameID(), cib.getVX3KContactID(), pinStatus.getStatus()==1); + vx3K.PseudoContactInput(cmd, result -> { + if (result.success()){ + Logger.info("VX3K PseudoContactInput successfully executed."); } else { - Logger.error("Failed to publish pin status update."); + Logger.error("VX3K PseudoContactInput failed to execute, Message {}.", result.message()); } }); - // Email notification can be added here + } + if (cib.isEnableModbus()){ + // update Modbus Register here + } + } + + }, diff --git a/src/Somecodes.java b/src/Somecodes.java index 454659c..786e12c 100644 --- a/src/Somecodes.java +++ b/src/Somecodes.java @@ -1,7 +1,17 @@ import com.google.gson.Gson; + + public class Somecodes { public static String currentdirectory = System.getProperty("user.dir"); public static Gson gson = new Gson(); + + + + + + + + } diff --git a/src/database/Database.java b/src/database/Database.java index 33fc6bb..5ee3ce8 100644 --- a/src/database/Database.java +++ b/src/database/Database.java @@ -15,7 +15,6 @@ import java.util.List; public class Database { private static final String DB_NAME = "jdbc:sqlite:database.sqlite"; - private final int maxContactInputData = 16; // Maximum number of contact input data public final List contactInputDataList = new ArrayList<>(); public Database(){ CreateContactInputDataTable(); @@ -83,6 +82,8 @@ public class Database { return loadedData; } + + /** * Load ContactInputData into the contactInputDataList. * @param loadedData List of ContactInputData objects loaded from the database. @@ -90,6 +91,8 @@ public class Database { @SuppressWarnings("ExtractMethodRecommender") private void LoadContactInputDataList(List loadedData){ contactInputDataList.clear(); + // Maximum number of contact input data + int maxContactInputData = 16; for (int i = 1; i <= maxContactInputData; i++){ final int contactID = i; ContactInputData cid = loadedData.stream() @@ -151,6 +154,24 @@ public class Database { return false; } + /** + * Get ContactInputData by ID + * @param contactID ID to get + * @return ContactInputData object if exists, or null if not exists + */ + public ContactInputData GetContactInputData(int contactID){ + return contactInputDataList.stream().filter(x -> x.getContactID() == contactID).findFirst().orElse(null); + } + + /** + * Get ContactInputData by description + * @param description Description to get + * @return ContactInputData if exists, or null if not exists + */ + public ContactInputData GetContactInputData(String description){ + return contactInputDataList.stream().filter(x -> x.getDescription().equals(description)).findFirst().orElse(null); + } + /** * Clear the content of a specific ContactInputData entry in the database. * @param contactID The ID of the ContactInputData entry to be cleared. diff --git a/src/pa/VX3K.java b/src/pa/VX3K.java new file mode 100644 index 0000000..4d64cea --- /dev/null +++ b/src/pa/VX3K.java @@ -0,0 +1,43 @@ +package pa; + + +import lombok.NonNull; +import org.tinylog.Logger; + +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; +import java.util.function.Consumer; + +public class VX3K { + private final InetSocketAddress inetSocketAddress; + + public VX3K(String ipaddress, int port){ + this.inetSocketAddress = new InetSocketAddress(ipaddress, port); + } + + public void PseudoContactInput(VX3KPseudoContactInput input, @NonNull Consumer<@NonNull VX3KCommandResult> callback){ + + try(SocketChannel channel = SocketChannel.open(inetSocketAddress)){ + channel.write(input.getBuffer()); + input.getBuffer().clear(); + int read = channel.read(input.getBuffer()); + if (read>0){ + short command = input.getBuffer().getShort(0); + short responsecode = input.getBuffer().getShort(1); + if (command == input.getCommandID()){ + if (responsecode == 0x0000){ + callback.accept(new VX3KCommandResult(input.getCommandID(), true, "success")); + } else callback.accept(new VX3KCommandResult(input.getCommandID(), false , "Code:"+responsecode )); + } else callback.accept(new VX3KCommandResult(input.getCommandID(), false, "Invalid CommandID reply")); + } else callback.accept( new VX3KCommandResult(input.getCommandID(), false, "Read 0")); + } catch (Exception e){ + Logger.error("PseudoContactInput failed, Message {}", e.getMessage()); + callback.accept(new VX3KCommandResult(input.getCommandID(), false, "Exception")); + } + + } + + + + +} diff --git a/src/pa/VX3KCommandResult.java b/src/pa/VX3KCommandResult.java new file mode 100644 index 0000000..56900a4 --- /dev/null +++ b/src/pa/VX3KCommandResult.java @@ -0,0 +1,4 @@ +package pa; + +public record VX3KCommandResult(int command, boolean success, String message) { +} diff --git a/src/pa/VX3KPseudoContactInput.java b/src/pa/VX3KPseudoContactInput.java new file mode 100644 index 0000000..80f9382 --- /dev/null +++ b/src/pa/VX3KPseudoContactInput.java @@ -0,0 +1,23 @@ +package pa; + +import lombok.Getter; + +import java.nio.ByteBuffer; + +public class VX3KPseudoContactInput{ + private final @Getter int CommandID; + private final @Getter ByteBuffer buffer; + public VX3KPseudoContactInput(int FrameID, int contactID, boolean isON){ + final int length = 8 + 6; // 8 bytes header + 6 bytes payload + CommandID = 0x1001; + buffer = ByteBuffer.allocate(length); + buffer.putShort((short) CommandID); + buffer.putShort((short) 0); + buffer.putShort((short) length); + buffer.putShort((short) 0x8000); + buffer.putShort((short) FrameID); + buffer.putShort((short) contactID); + buffer.putShort((short)(isON ? 1 : 0)); + } + +}