diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..a1757ae --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/slf4j_simple.xml b/.idea/libraries/slf4j_simple.xml new file mode 100644 index 0000000..16c12c5 --- /dev/null +++ b/.idea/libraries/slf4j_simple.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Properties/simplelogger.properties b/Properties/simplelogger.properties new file mode 100644 index 0000000..d1de872 --- /dev/null +++ b/Properties/simplelogger.properties @@ -0,0 +1,34 @@ +# SLF4J's SimpleLogger configuration file +# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. + +# Default logging detail level for all instances of SimpleLogger. +# Must be one of ("trace", "debug", "info", "warn", or "error"). +# If not specified, defaults to "info". +org.slf4j.simpleLogger.defaultLogLevel=warn + +# Logging detail level for a SimpleLogger instance named "xxxxx". +# Must be one of ("trace", "debug", "info", "warn", or "error"). +# If not specified, the default logging detail level is used. +#org.slf4j.simpleLogger.log.xxxxx= + +# Set to true if you want the current date and time to be included in output messages. +# Default is false, and will output the number of milliseconds elapsed since startup. +#org.slf4j.simpleLogger.showDateTime=false + +# The date and time format to be used in the output messages. +# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. +# If the format is not specified or is invalid, the default format is used. +# The default format is yyyy-MM-dd HH:mm:ss:SSS Z. +#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z + +# Set to true if you want to output the current thread name. +# Defaults to true. +#org.slf4j.simpleLogger.showThreadName=true + +# Set to true if you want the Logger instance name to be included in output messages. +# Defaults to true. +#org.slf4j.simpleLogger.showLogName=true + +# Set to true if you want the last component of the name to be included in output messages. +# Defaults to false. +#org.slf4j.simpleLogger.showShortLogName=false \ No newline at end of file diff --git a/Properties/tinylog.properties b/Properties/tinylog.properties new file mode 100644 index 0000000..c4de797 --- /dev/null +++ b/Properties/tinylog.properties @@ -0,0 +1,9 @@ +//writer = rolling file +writer=console +writer.file=logs/{date:yyyy-MM-dd}.log +writer.format={date:yyyy-MM-dd HH:mm:ss} {level}: {class}.{method}() {message} +writer.policies=daily +writer.buffered=true +writer.charset=UTF-8 +writer.level=info +writer.append=true \ No newline at end of file diff --git a/ProtegeToAASMini.iml b/ProtegeToAASMini.iml index 237d7ef..20fcc54 100644 --- a/ProtegeToAASMini.iml +++ b/ProtegeToAASMini.iml @@ -5,6 +5,7 @@ + @@ -21,5 +22,6 @@ + \ No newline at end of file diff --git a/src/AASMini/AASMini.java b/src/AASMini/AASMini.java index 2aa39a5..49cd18c 100644 --- a/src/AASMini/AASMini.java +++ b/src/AASMini/AASMini.java @@ -4,12 +4,15 @@ import org.pmw.tinylog.Logger; import java.net.InetAddress; import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; -@SuppressWarnings("unused") + +@SuppressWarnings({"FieldCanBeLocal", "UnusedReturnValue"}) public class AASMini { private final String targetIP; private final int targetPort; - private final int msgId = 15; + private final int msgId = 1; private final String zones = "Zone_1,Zone_2,Zone_3,Zone_4"; private Socket socket; @@ -70,21 +73,35 @@ public class AASMini { socket = null; } - //TODO protocol untuk kirim data ke AAS Mini - private void SendPlatNoToAAS(String platNo){ + private String ConvertToString(byte[] data){ + StringBuilder sb = new StringBuilder(); + for (byte b : data){ + if (b>0x1F && b<0x7F) + sb.append((char)b); + else break; + + } + return sb.toString(); + } + + public void SendPlatNoToAAS(String platNo){ String protocol = "BROADCAST;"+msgId+";IND;$PLATNOMOR$="+platNo+";"+zones; byte[] data = protocol.getBytes(); byte[] rplyData = SendAndReceive(data); - String rplyInStr = rplyData.toString(); - String[] rply = rplyInStr.split(";"); - if (rply.length ==2){ - if (rply[0].equals("BROADCAST")){ - if (rply[1].equals("OK")){ - System.out.println("Success sending data to AAS!"); - }else System.out.println("Failed sending data to AAS!"); - }else System.out.println("Wrong format answer!"); - }else System.out.println("Wrong size answer"); + if (rplyData!=null && rplyData.length>0){ + String rplyInStr = ConvertToString(rplyData); + Logger.info("Response from AAS : {}", rplyInStr); + String[] rply = rplyInStr.split(";"); + if (rply.length ==2){ + if (rply[0].equals("BROADCAST")){ + if (rply[1].equals("OK")){ + Logger.info("Success sending data to AAS!"); + }else Logger.info("Failed sending data to AAS!"); + }else Logger.info("Wrong format answer!"); + }else Logger.info("Wrong size answer"); + } else Logger.info("SendAndReceive return null"); + } @@ -101,9 +118,7 @@ public class AASMini { byte[] buffer = new byte[1024]; int read = socket.getInputStream().read(buffer); if (read>0){ - byte[] result = new byte[read]; - System.arraycopy(buffer, 0, result, 0, read); - return result; + return buffer; } } catch (Exception e){ Logger.error("Failed to send data to {}:{}, Error : {}", targetIP, targetPort, e.getMessage()); @@ -113,10 +128,10 @@ public class AASMini { return null; } - /** + /* * Send data to AAS Mini * @param data Data to be sent - */ + private void SendData(byte[] data){ if (IsConnected()){ if (data!=null && data.length>0){ @@ -128,4 +143,5 @@ public class AASMini { } } } + */ } diff --git a/src/Main.java b/src/Main.java index f586efa..dd7a779 100644 --- a/src/Main.java +++ b/src/Main.java @@ -4,34 +4,35 @@ import Web.WebResponse; import Web.WebServer; import org.pmw.tinylog.Logger; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; + public class Main { private static AASMini aas; private static ProtegeGX protegeGX; private static WebServer web; - private static String aasIP = "192.168.10.10"; + private static String aasIP = "localhost"; private static int aasPort = 5000; private static int webPort = 8080; + private final static String currentDirectory = System.getProperty("user.dir"); + public static void main(String[] args) { + ExtractFile("tinylog.properties"); + ExtractFile("simplelogger.properties"); GetArguments(args); Runtime.getRuntime().addShutdownHook(new Thread(() -> { - System.out.println("Shutting down..."); - if (aas!=null) aas.Disconnect(); + Logger.info("Shutting down..."); if (protegeGX!=null) protegeGX.Disconnect(); if (web!=null) web.Stop(); })); - System.out.println("Protege to AAS Mini Connector"); + Logger.info("Protege to AAS Mini Connector"); - protegeGX = new ProtegeGX(); - protegeGX.setOnCardRead(data -> { - Logger.info(data); - if (aas!=null && aas.IsConnected()){ - // send something to AAS - } - }); + protegeGX = new ProtegeGX(5004); + protegeGX.setOnCardRead(data -> SendPlatNomor(data.LastName)); aas = new AASMini(aasIP, aasPort); - aas.Connect(); web = new WebServer(); web.Start(webPort); @@ -47,6 +48,37 @@ public class Main { WebResponse response = new WebResponse(); return response; }); + + Logger.info("Sending dummy to AAS Mini"); + SendPlatNomor("B2902PFO"); + } + + private static void SendPlatNomor(String platNomor){ + if (aas!=null){ + if (aas.Connect()){ + aas.SendPlatNoToAAS(platNomor); + aas.Disconnect(); + } else Logger.error("Failed to connect to AAS Mini"); + } else Logger.error("AAS Mini is not created"); + + } + + private static void ExtractFile(String filename){ + File target = new File(currentDirectory,filename); + if (target.isFile()) { + Logger.info("File {} already exists", filename); + return; + } + + try{ + InputStream in = Main.class.getResourceAsStream("/"+filename); + if (in!=null){ + Files.copy(in, target.toPath()); + Logger.info("File {} extracted", filename); + } else throw new Exception("File not found"); + } catch (Exception e){ + Logger.error("Failed to extract file {}, Message : {}", filename, e.getMessage()); + } } private static void GetArguments(String[] args){ diff --git a/src/ProtegeGX/ProtegeData.java b/src/ProtegeGX/ProtegeData.java index e8e2025..3c8b39e 100644 --- a/src/ProtegeGX/ProtegeData.java +++ b/src/ProtegeGX/ProtegeData.java @@ -1,12 +1,43 @@ package ProtegeGX; +import java.util.regex.Pattern; + public class ProtegeData { - public String ReaderID; - public String CardID; - public String Description; + public String FirstName; + public String LastName; + public String CardNumber; + public int UserNode; + public String DoorName; + public int DoorNode; + public String FieldTime; + public String LoggedTime; @Override public String toString(){ - return "ReaderID = " + ReaderID + ", CardID = " + CardID + ", Description = " + Description; + return String.format("FirstName: %s\nLastName: %s\nCardNumber: %s\nUserNode: %d\nDoorName: %s\nDoorNode: %d\nFieldTime: %s\nLoggedTime: %s", FirstName, LastName, CardNumber, UserNode, DoorName, DoorNode, FieldTime, LoggedTime); + } + + /** + * create EventData object from string data using Regex + * @param data string data + * @return EventData object if valid, or null if invalid + */ + public static ProtegeData Parse(String data){ + final String regex = "User (\\w*) (\\w*) \\(([:0-9]*)\\) \\(UN(\\d+)\\).*?To (.*?) \\(DR(\\d+)\\).*?.*?(.*?).*?(.*?)"; + final Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); + final java.util.regex.Matcher matcher = pattern.matcher(data); + if (matcher.find()){ + ProtegeData event = new ProtegeData(); + event.FirstName = matcher.group(1); + event.LastName = matcher.group(2); + event.CardNumber = matcher.group(3); + event.UserNode = Integer.parseInt(matcher.group(4)); + event.DoorName = matcher.group(5); + event.DoorNode = Integer.parseInt(matcher.group(6)); + event.FieldTime = matcher.group(7); + event.LoggedTime = matcher.group(8); + return event; + } + return null; } } diff --git a/src/ProtegeGX/ProtegeGX.java b/src/ProtegeGX/ProtegeGX.java index 748ab15..cc4f402 100644 --- a/src/ProtegeGX/ProtegeGX.java +++ b/src/ProtegeGX/ProtegeGX.java @@ -1,20 +1,87 @@ package ProtegeGX; +import lombok.Getter; import lombok.Setter; +import org.pmw.tinylog.Logger; +import java.io.IOException; +import java.io.InputStream; +import java.net.ServerSocket; +import java.net.Socket; import java.util.function.Consumer; -@SuppressWarnings("unused") public class ProtegeGX { @Setter private Consumer onCardRead; + private ServerSocket serverSocket; + @Getter private boolean running ; - private void updateCardRead(String data) { - if (onCardRead != null) { - ProtegeData protegeData = new ProtegeData(); - onCardRead.accept(protegeData); + private void UpdateCardData(String data){ + if (data!=null && !data.isEmpty()){ + ProtegeData cardData = ProtegeData.Parse(data); + if (cardData!=null){ + Logger.info("Card data: "+cardData); + if (onCardRead!=null){ + onCardRead.accept(cardData); + } + } + } + + } + + + + public ProtegeGX(int localport) { + running = false; + try{ + serverSocket = new ServerSocket(localport); + running = true; + Logger.info("ProtegeGX server started on port "+localport); + // Thread untuk serverSocket accept client + new Thread(()->{ + while(running){ + try { + Socket client = serverSocket.accept(); + // Thread untuk komunikasi dengan client + new Thread(()->{ + try { + Logger.info("Client connected: "+client.getInetAddress().getHostAddress()); + InputStream in = client.getInputStream(); + while(running){ + byte[] buffer = new byte[1024]; + int bytesRead = in.read(buffer); + if (bytesRead>0){ + String data = new String(buffer, 0, bytesRead); + //Logger.info("Data received: "+data); + UpdateCardData(data); + } + } + client.close(); + Logger.info("Client disconnected: "+client.getInetAddress().getHostAddress()); + } catch (IOException e) { + Logger.error("Failed to read data from client: "+e.getMessage()); + } + + }).start(); + } catch (IOException e) { + Logger.error("Failed to accept client connection: "+e.getMessage()); + } + } + }).start(); + } catch (Exception e){ + Logger.error("Failed to create server socket: "+e.getMessage()); } } + public void Disconnect() { - throw new UnsupportedOperationException("Not supported yet."); + running = false; + if (serverSocket!=null){ + try{ + serverSocket.close(); + Logger.info("ProtegeGX server stopped"); + } catch (Exception e){ + Logger.error("Failed to close server socket: "+e.getMessage()); + } + } + serverSocket = null; } } diff --git a/src/Web/WebServer.java b/src/Web/WebServer.java index 2091e71..4b1132c 100644 --- a/src/Web/WebServer.java +++ b/src/Web/WebServer.java @@ -2,8 +2,11 @@ package Web; import com.google.gson.Gson; import io.javalin.Javalin; +import io.javalin.core.util.JavalinLogger; import io.javalin.http.staticfiles.Location; +import io.javalin.jetty.JettyUtil; import lombok.Setter; +import org.eclipse.jetty.util.log.Log; import java.util.function.Function; @@ -31,8 +34,10 @@ public class WebServer { Javalin app; public WebServer(){ + JettyUtil.disableJettyLogger(); + JavalinLogger.enabled = false; app = Javalin.create(config -> { - config.addStaticFiles("/Web", Location.EXTERNAL); + config.addStaticFiles("/Web", Location.CLASSPATH); }); app.get("/", ctx -> ctx.result("Hello World")); app.get("/aasmini", ctx -> ctx.result("AAS Mini API"));