Files
ErhaCam/src/main/java/id/co/gtc/erhacam/CaptureView.java
rdkartono d2e7d1155d revisi 09/05/2025
Detectors.java :
* scaleFactor 1.05 become 1.2
* minNeighbors 3 become 5
* haarcascade_frontalface_default.xml become haarcascade_frontalface_alt.xml
CaptureView.java :
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(devicenumber) become OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(CAP_DSHOW+devicenumber);

MainApplication.java :
stage.setTitle("MultiCam Capture App for ERHA 09052025-001")
2025-05-09 08:51:13 +07:00

1203 lines
52 KiB
Java

package id.co.gtc.erhacam;
import BASS.AudioPlayer;
import BASS.PlaybackStatus;
import Camera.*;
import Config.CameraConfigEnum;
import Config.SomeCodes;
import Database.PhotoReviewClass;
import Database.Sqlite;
import ErhaAPI.ErhaAPI;
import ErhaAPI.BarcodeResullt;
import ErhaAPI.PhotoResult;
import ErhaAPI.PatientRecord;
import ErhaAPI.UploadResult;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.stage.DirectoryChooser;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.scene.control.Alert.AlertType;
import org.bytedeco.javacv.OpenCVFrameGrabber;
import org.bytedeco.javacv.VideoInputFrameGrabber;
import org.bytedeco.opencv.opencv_core.Rect;
import org.bytedeco.opencv.opencv_core.Size;
import org.tinylog.Logger;
import static Config.SomeCodes.*;
import static org.bytedeco.opencv.global.opencv_videoio.CAP_DSHOW;
public class CaptureView {
@FXML
private AnchorPane CaptureViewAnchor;
@FXML
private AnchorPane cam1;
@FXML
private AnchorPane cam2;
@FXML
private AnchorPane cam3;
@FXML
private AnchorPane cam4;
@FXML
private AnchorPane cam5;
@FXML
private AnchorPane controlpane;
private Cameradetail image1, image2, image3, image4, image5;
@FXML
private TextArea barcodeData;
@FXML
private TextArea medicalRecordID;
@FXML
private TextArea PatientName;
@FXML
private Button TakePhotoButton;
private AudioPlayer audioPlayer;
private String audio_posisikan_muka = "posisikan_wajah.wav";
private String audio_scan_barcode = "scan_barcode.wav";
private String audio_pengambilan_gagal = "pengambilan_gagal.wav";
private String audio_pengambilan_berhasil = "pengambilan_berhasil.wav";
private String audio_upload_gagal = "upload_gagal.wav";
private String audio_countdown = "countdown321.wav";
private String audio_camera_shutter = "camera-shutter-click-01.wav";
private String audio_tahan_posisi = "tahan_posisi.wav";
private String audio_data_barcode_tidak_ditemukan = "data_barcode_tidak_ditemukan.wav";
private String audio_hubungi_staf_kami = "hubungistafkami.wav";
private List<String> cams;
private final AtomicBoolean[] have_face = new AtomicBoolean[5];
private final AtomicBoolean[] have_eyes = new AtomicBoolean[5];
private final AtomicBoolean[] have_profile = new AtomicBoolean[5];
private final AtomicBoolean isTakingPhoto = new AtomicBoolean(false);
private final ErhaAPI erhaAPI = new ErhaAPI(false);
// for timeout 180 detik
private final int timeout = 180;
ScheduledExecutorService timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> runningTask = null;
private void TakePhotoButtonChange(){
boolean disabled = false;
String barcode = barcodeData.getText();
String medicalrecord = medicalRecordID.getText();
String patientname = PatientName.getText();
if (barcode.isBlank()) disabled = true;
if (medicalrecord.isBlank()) disabled = true;
if (patientname.isBlank()) disabled = true;
TakePhotoButton.setDisable(disabled);
}
@FXML
private void ChangeDirectory(){
DirectoryChooser dc = new DirectoryChooser();
dc.setTitle("Select Directory");
String path = dc.showDialog(null).getAbsolutePath();
config.SetPhotoDirectory(path);
config.Save();
}
private void trigger_autofocus(Cameradetail image) {
if (image!=null){
if (image.isCapturing()){
image.setAutoFocus(false);
Wait(5);
image.setFocus(0.7);
Wait(5);
image.setAutoFocus(true);
Wait(5);
}
}
}
@FXML
private void AutoFocus() {
trigger_autofocus(image1);
trigger_autofocus(image2);
trigger_autofocus(image3);
trigger_autofocus(image4);
trigger_autofocus(image5);
}
@FXML
private void TakePhotos(){
String directory = config.getPhotoDirectory();
String prefix = RemoveSpaces(medicalRecordID.getText()) ;
boolean has_face = Arrays.stream(have_face).anyMatch(AtomicBoolean::get);
if (ValidDirectory(directory)){
if (ValidMedicalRecordId(prefix)){
isTakingPhoto.set(true);
if (has_face){
AutoCloseAlert.show("Pengambilan Foto", "Tahan Posisi Anda", "Proses ini kurang lebih 3 detik", 5, null);
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_tahan_posisi)){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_tahan_posisi, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
}
@Override
public void onPlaybackFinished(String filename) {
Wait(500);
audioPlayer.PlayFile(audio_countdown, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
}
@Override
public void onPlaybackFinished(String filename) {
take_photo_lanjutan(directory, prefix);
}
@Override
public void onPlaybackFailure(String filename) {
}
});
}
@Override
public void onPlaybackFailure(String filename) {
}
});
}
}
} else {
AutoCloseAlert.show("Error", "No Face Detected", "No Face Detected", 5, null);
isTakingPhoto.set(false);
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_posisikan_muka)) {
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_posisikan_muka, null);
}
}
}
} else {
System.out.println("Prefix invalid, not taking photo");
isTakingPhoto.set(false);
//AutoCloseAlert.show("QR Code Not Available", "", "Please scan QR before continue", 5, s -> AutoCloseAlert.show("Scan Barcode", "Silahkan Scan Barcode Anda", "Arahkan kertas barcode ke kamera", 0, null));
if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){
if (AutoCloseAlert.banner_01!=null){
System.out.println("Showing banner 01 because prefix invalid");
AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null);
}
}
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_scan_barcode)) {
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_scan_barcode, null);
}
}
}
} else {
System.out.println("Photo Directory invalid, not taking photo");
isTakingPhoto.set(false);
AutoCloseAlert.show("Invalid Photo Directory","Photo Directory not set", "Please set photo directory at Setting", 5, s -> AutoCloseAlert.show("Setting", "Setting", "Please set photo directory", 0, null));
}
}
static class CallablePhotoResult implements Callable<PhotoResult> {
private final String directory;
private final String prefix;
private final Cameradetail image;
public CallablePhotoResult(String directory, String prefix, Cameradetail image) {
this.directory = directory;
this.prefix = prefix;
this.image = image;
}
@Override
public PhotoResult call() {
if (image!=null){
image.RemapROI(0.1, 0.3, false);
double sharpness = CalculateSharpness(image.getGrayMat());
image.setSharpness_indicator(sharpness);
PhotoResult p = image.TakePhoto(directory, prefix);
p.setSharpscore(sharpness);
if (ValidFile(p.getFullres())){
if (ValidFile(p.getCompressedfile())){
return p;
}
}
}
return null;
}
}
AtomicInteger takephoto_failure_counter = new AtomicInteger(0);
private void take_photo_lanjutan(String directory, String prefix){
audioPlayer.PlayFile(audio_camera_shutter, null);
Size thumbsize = new Size(160,120);
PhotoReviewClass prc = new PhotoReviewClass();
prc.setPrefix(prefix);
long nanostart = System.nanoTime(); // for performance measurement
ExecutorService executor = Executors.newFixedThreadPool(5);
CallablePhotoResult task1 = new CallablePhotoResult(directory, prefix, image1);
CallablePhotoResult task2 = new CallablePhotoResult(directory, prefix, image2);
CallablePhotoResult task3 = new CallablePhotoResult(directory, prefix, image3);
CallablePhotoResult task4 = new CallablePhotoResult(directory, prefix, image4);
CallablePhotoResult task5 = new CallablePhotoResult(directory, prefix, image5);
PhotoResult p1 = null;
PhotoResult p2 = null;
PhotoResult p3 = null;
PhotoResult p4 = null;
PhotoResult p5 = null;
try{
Future<PhotoResult> f1 = executor.submit(task1);
Future<PhotoResult> f2 = executor.submit(task2);
Future<PhotoResult> f3 = executor.submit(task3);
Future<PhotoResult> f4 = executor.submit(task4);
Future<PhotoResult> f5 = executor.submit(task5);
p1 = f1.get();
p2 = f2.get();
p3 = f3.get();
p4 = f4.get();
p5 = f5.get();
} catch (Exception e){
Logger.error("Error TakePhotos: " + e.getMessage());
} finally {
executor.shutdown();
}
String timestamp = SomeCodes.GetDateTimeString();
System.out.println("Creating timestamp: "+timestamp);
// check for blurred image
double score1=-1, score2=-1, score3=-1, score4=-1, score5=-1;
if (p1!=null){
score1 = p1.getSharpscore();
}
if (p2!=null){
score2 = p2.getSharpscore();
}
if (p3!=null){
score3 = p3.getSharpscore();
}
if (p4!=null){
score4 = p4.getSharpscore();
}
if (p5!=null){
score5 = p5.getSharpscore();
}
System.out.println("Sharpness score: "+score1+", "+score2+", "+score3+", "+score4+", "+score5);
double lowest = FindLowestValue(score1,score2,score3,score4,score5);
if (lowest<=config.getSharpnessThreshold()){
// jelek
String culprit = "";
if (lowest==score1) culprit = "camera 1";
else if (lowest==score2) culprit = "camera 2";
else if (lowest==score3) culprit = "camera 3";
else if (lowest==score4) culprit = "camera 4";
else if (lowest==score5) culprit = "camera 5";
String lowest_string = String.format("%.2f", lowest);
AutoCloseAlert.show("Take Photos Failed", "Blurred Image Detected", "Blurred Image Detected at "+culprit+" with sharpness score "+lowest_string, 5, s -> isTakingPhoto.set(false));
takephoto_failure_counter.incrementAndGet();
if (audioPlayer!=null && audioPlayer.isInited()){
if (takephoto_failure_counter.get() < 2){
if (!Objects.equals(audio_pengambilan_gagal, audioPlayer.getCurrentFile())){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_pengambilan_gagal, null);
}
} else {
if (!Objects.equals(audio_hubungi_staf_kami, audioPlayer.getCurrentFile())){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_hubungi_staf_kami, null);
}
}
}
return;
} else {
// bagus
takephoto_failure_counter.set(0);
}
Rect bestroi = null;
Rect reducedroi = null;
if (p1!=null && p1.getBestROI()!=null){
bestroi = p1.getBestROI();
reducedroi = p1.getReducedROI();
}
if (p2!=null && p2.getBestROI()!=null){
if (bestroi==null)
bestroi = p2.getBestROI();
else if (p2.getBestROI().area()>bestroi.area())
bestroi = p2.getBestROI();
if (reducedroi==null)
reducedroi = p2.getReducedROI();
else if (p2.getReducedROI().area()>reducedroi.area())
reducedroi = p2.getReducedROI();
}
if (p3!=null && p3.getBestROI()!=null){
if (bestroi==null)
bestroi = p3.getBestROI();
else if (p3.getBestROI().area()>bestroi.area())
bestroi = p3.getBestROI();
if (reducedroi==null)
reducedroi = p3.getReducedROI();
else if (p3.getReducedROI().area()>reducedroi.area())
reducedroi = p3.getReducedROI();
}
if (p4!=null && p4.getBestROI()!=null){
if (bestroi==null)
bestroi = p4.getBestROI();
else if (p4.getBestROI().area()>bestroi.area())
bestroi = p4.getBestROI();
if (reducedroi==null)
reducedroi = p4.getReducedROI();
else if (p4.getReducedROI().area()>reducedroi.area())
reducedroi = p4.getReducedROI();
}
if (p5!=null && p5.getBestROI()!=null){
if (bestroi==null)
bestroi = p5.getBestROI();
else if (p5.getBestROI().area()>bestroi.area())
bestroi = p5.getBestROI();
if (reducedroi==null)
reducedroi = p5.getReducedROI();
else if (p5.getReducedROI().area()>reducedroi.area())
reducedroi = p5.getReducedROI();
}
if (p1!=null){
prc.setFileLeft90(ValidFile(p1.getFullres()) ? p1.getFullres() : "");
prc.setCompressedLeft90(ValidFile(p1.getCompressedfile()) ? p1.getCompressedfile() : "");
if (ValidFile(p1.getFullcrop())){
// ada crop
prc.setCroppedLeft90(p1.getFullcrop());
} else {
String xx = image1.CropBestMat(directory,prefix, bestroi);
if (ValidFile(xx)) prc.setCroppedLeft90(xx);
}
if (ValidFile(p1.getCompressedcrop())){
prc.setCompressedCropLeft90(p1.getCompressedcrop());
} else {
String xx =image1.CropReducedMat(directory,prefix, reducedroi);
if (ValidFile(xx)) prc.setCompressedCropLeft90(xx);
}
String thumb1 = MakeThumbfile(p1.getFullres(), thumbsize);
if (ValidFile(thumb1)) prc.setThumbLeft90(thumb1);
}
if (p2!=null){
prc.setFileLeft45(ValidFile(p2.getFullres()) ? p2.getFullres() : "");
prc.setCompressedLeft45(ValidFile(p2.getCompressedfile()) ? p2.getCompressedfile() : "");
if (ValidFile(p2.getFullcrop())){
// ada crop
prc.setCroppedLeft45(p2.getFullcrop());
} else {
String xx = image2.CropBestMat(directory,prefix, bestroi);
if (ValidFile(xx)) prc.setCroppedLeft45(xx);
}
if (ValidFile(p2.getCompressedcrop())){
prc.setCompressedCropLeft45(p2.getCompressedcrop());
} else {
String xx = image2.CropReducedMat(directory,prefix, reducedroi);
if (ValidFile(xx)) prc.setCompressedCropLeft45(xx);
}
String thumb2 = MakeThumbfile(p2.getFullres(), thumbsize);
if (ValidFile(thumb2)) prc.setThumbLeft45(thumb2);
}
if (p3!=null){
prc.setFileCenter(ValidFile(p3.getFullres()) ? p3.getFullres() : "");
prc.setCompressedCenter(ValidFile(p3.getCompressedfile()) ? p3.getCompressedfile() : "");
if (ValidFile(p3.getFullcrop())){
// ada crop
prc.setCroppedCenter(p3.getFullcrop());
} else {
String xx = image3.CropBestMat(directory,prefix, bestroi);
if (ValidFile(xx)) prc.setCroppedCenter(xx);
}
if (ValidFile(p3.getCompressedcrop())){
prc.setCompressedCropCenter(p3.getCompressedcrop());
} else {
String xx = image3.CropReducedMat(directory,prefix, reducedroi);
if (ValidFile(xx)) prc.setCompressedCropCenter(xx);
}
String thumb3 = MakeThumbfile(p3.getFullres(), thumbsize);
if (ValidFile(thumb3)) prc.setThumbCenter(thumb3);
}
if (p4!=null){
prc.setFileRight45(ValidFile(p4.getFullres()) ? p4.getFullres() : "");
prc.setCompressedRight45(ValidFile(p4.getCompressedfile()) ? p4.getCompressedfile() : "");
if (ValidFile(p4.getFullcrop())){
// ada crop
prc.setCroppedRight45(p4.getFullcrop());
} else {
String xx = image4.CropBestMat(directory,prefix, bestroi);
if (ValidFile(xx)) prc.setCroppedRight45(xx);
}
if (ValidFile(p4.getCompressedcrop())){
prc.setCompressedCropRight45(p4.getCompressedcrop());
} else {
String xx = image4.CropReducedMat(directory,prefix, reducedroi);
if (ValidFile(xx)) prc.setCompressedCropRight45(xx);
}
String thumb4 = MakeThumbfile(p4.getFullres(), thumbsize);
if (ValidFile(thumb4)) prc.setThumbRight45(thumb4);
}
if (p5!=null){
prc.setFileRight90(ValidFile(p5.getFullres()) ? p5.getFullres() : "");
prc.setCompressedRight90(ValidFile(p5.getCompressedfile()) ? p5.getCompressedfile() : "");
if (ValidFile(p5.getFullcrop())){
// ada crop
prc.setCroppedRight90(p5.getFullcrop());
} else {
String xx = image5.CropBestMat(directory,prefix, bestroi);
if (ValidFile(xx)) prc.setCroppedRight90(xx);
}
if (ValidFile(p5.getCompressedcrop())){
prc.setCompressedCropRight90(p5.getCompressedcrop());
} else {
String xx= image5.CropReducedMat(directory,prefix, reducedroi);
if (ValidFile(xx)) prc.setCompressedCropRight90(xx);
}
String thumb5 = MakeThumbfile(p5.getFullres(), thumbsize);
if (ValidFile(thumb5)) prc.setThumbRight90(thumb5);
}
long duration = (System.nanoTime() - nanostart) / 1000000; // in milliseconds
System.out.println("TakePhotos duration: "+duration+" ms");
String[] files = prc.compressed();
if (files.length>0){
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_pengambilan_berhasil)){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_pengambilan_berhasil, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
}
@Override
public void onPlaybackFinished(String filename) {
AutoCloseAlert.showpictures(prc.compressed(),3, (s)->{
if (AutoCloseAlert.banner_02!=null) {
System.out.println("Showing banner 02 after photo taken");
AutoCloseAlert.showbanner(AutoCloseAlert.banner_02, 0, null);
UploadFiles(prc, prefix);
if (runningTask!=null) runningTask.cancel(false);
}
});
}
@Override
public void onPlaybackFailure(String filename) {
}
});
}
}
} else {
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_pengambilan_gagal)){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_pengambilan_gagal, null);
}
}
clear();
if (runningTask!=null) runningTask.cancel(false);
}
}
private void UploadFiles(PhotoReviewClass prc, String prefix){
String[] files = prc.compressed();
if (files.length>0){
InsertSQL(prc);
Task<Void> uploadtask = new Task<>() {
@Override
protected Void call() {
int totalfiles = files.length;
int counter = 0;
for (String ff : files) {
UploadResult ur = erhaAPI.Upload_File(prefix, ff,true);
if (ur != null) {
if (ur.message.startsWith("Record has been created")) {
counter++;
updateMessage("Upload success for " + ff);
} else updateMessage("Upload failed for " + ff+", Message : "+ur.message);
} else updateMessage("Upload failed for " + ff+" because UploadResult is null");
}
if (counter == totalfiles) {
super.succeeded();
} else super.failed();
return null;
}
};
uploadtask.messageProperty().addListener((obs, oldval, newval)-> {
System.out.println("UploadTask message: "+newval);
Logger.info(newval);
});
uploadtask.setOnSucceeded(e-> {
System.out.println("UploadTask succeeded");
clear();
});
uploadtask.setOnFailed(e-> {
System.out.println("UploadTask failed");
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_upload_gagal)){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_upload_gagal, null);
}
}
AutoCloseAlert.show("Upload Failed", "Upload Failed", "Upload Failed", 5, s -> clear());
});
Thread uploadThread = new Thread(uploadtask);
uploadThread.setName("UploadThread MedicalRecordID="+prefix);
uploadThread.setDaemon(true);
uploadThread.start();
} else ShowAlert(AlertType.ERROR, "Error", "No Photos Taken", "No Photos Taken, please check camera");
}
@FXML
public void initialize(){
audio_posisikan_muka = ExtractResource("/posisikan_wajah.wav");
audio_pengambilan_berhasil = ExtractResource("/pengambilan_berhasil.wav");
audio_pengambilan_gagal = ExtractResource("/pengambilan_gagal.wav");
audio_scan_barcode = ExtractResource("/scan_barcode.wav");
audio_upload_gagal = ExtractResource("/upload_gagal.wav");
audio_countdown = ExtractResource("/countdown321.wav");
audio_camera_shutter = ExtractResource("/camera-shutter-click-01.wav");
audio_tahan_posisi = ExtractResource("/tahan_posisi.wav");
audio_data_barcode_tidak_ditemukan = ExtractResource("/data_barcode_tidak_ditemukan.wav");
audio_hubungi_staf_kami = ExtractResource("/hubungistafkami.wav");
//tambahan 19/03/2025
barcodeData.textProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("barcodeData changed from ["+oldValue+"] to ["+newValue+"]");
if (ValidBarCode(newValue)){
if (Objects.equals(AutoCloseAlert.banner_01, AutoCloseAlert.shownBanner)){
System.out.println("Close banner 01");
AutoCloseAlert.close();
}
} else {
if (AutoCloseAlert.banner_01!=null){
System.out.println("Show banner 01");
AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0, null);
}
}
TakePhotoButtonChange();
});
medicalRecordID.textProperty().addListener((observable, oldValue, newValue) -> TakePhotoButtonChange());
PatientName.textProperty().addListener((observable, oldValue, newValue) -> TakePhotoButtonChange());
audioPlayer = new AudioPlayer(1,48000);
Logger.info("Audio Player : "+(audioPlayer.isInited()? "Inited" : "Not Inited"));
cams = null;
try{
String[] xxx = VideoInputFrameGrabber.getDeviceDescriptions();
if (xxx!=null && xxx.length>0){
cams = Arrays.asList(xxx);
}
} catch (Exception e){
Logger.error("Error getting camera list: "+e.getMessage());
}
LoadCameraDetail(cam1, 1, CameraConfigEnum.CameraConfigLeft90);
LoadCameraDetail(cam2, 2, CameraConfigEnum.CameraConfigLeft45);
LoadCameraDetail(cam3, 3, CameraConfigEnum.CameraConfigCenter);
LoadCameraDetail(cam4, 4, CameraConfigEnum.CameraConfigRight45);
LoadCameraDetail(cam5, 5, CameraConfigEnum.CameraConfigRight90);
Platform.runLater(()->{
if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){
if (AutoCloseAlert.banner_01!=null){
System.out.println("Showing banner 01 for first time");
AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null);
}
}
int indexleft90=-1;
int indexleft45=-1;
int indexcenter=-1;
int indexright45=-1;
int indexright90;
if (cams!=null && !cams.isEmpty()){
String camleft90 = config.getCameraLeft90();
if (ValidString(camleft90)){
int[] indexes = FindIndexes(cams, camleft90);
if (indexes.length>0){
indexleft90 = FindFirstIndex(cams, camleft90);
Logger.info("Left90 Index: "+indexleft90);
if (indexleft90!=-1){
final int finalindex = indexleft90;
Thread c1 = new Thread(()-> SetupCameraWithController(image1, camleft90, finalindex));
c1.setName("SetupCameraWithController "+camleft90);
c1.setDaemon(true);
c1.start();
}
}
}
String camleft45 = config.getCameraLeft45();
if (ValidString(camleft45)){
int[] indexes = FindIndexes(cams, camleft45);
if (indexes.length>0){
indexleft45 = FindFirstIndex(cams, camleft45, indexleft90);
Logger.info("Left45 Index: "+indexleft45);
if (indexleft45!=-1) {
final int finalindex = indexleft45;
Thread c2 = new Thread(()-> SetupCameraWithController(image2, camleft45, finalindex));
c2.setName("SetupCameraWithController "+camleft45);
c2.setDaemon(true);
c2.start();
}
}
}
String camcenter = config.getCameraCenter();
if (ValidString(camcenter)){
int[] indexes = FindIndexes(cams, camcenter);
if (indexes.length>0){
indexcenter = FindFirstIndex(cams, camcenter, indexleft90, indexleft45);
Logger.info("Center Index: "+indexcenter);
if (indexcenter!=-1) {
final int finalindex = indexcenter;
Thread c3 = new Thread(()-> SetupCameraWithController(image3, camcenter, finalindex));
c3.setName("SetupCameraWithController "+camcenter);
c3.setDaemon(true);
c3.start();
}
}
}
String camright45 = config.getCameraRight45();
if (ValidString(camright45)){
int[] indexes = FindIndexes(cams, camright45);
if (indexes.length>0){
indexright45 = FindFirstIndex(cams, camright45, indexleft90, indexleft45, indexcenter);
Logger.info("Right45 Index: "+indexright45);
if (indexright45!=-1) {
final int finalindex = indexright45;
Thread c4 = new Thread(()-> SetupCameraWithController(image4, camright45, finalindex));
c4.setName("SetupCameraWithController "+camright45);
c4.setDaemon(true);
c4.start();
}
}
}
String camright90 = config.getCameraRight90();
if (ValidString(camright90)){
int[] indexes = FindIndexes(cams, camright90);
if (indexes.length>0){
indexright90 = FindFirstIndex(cams, camright90, indexleft90, indexleft45, indexcenter, indexright45);
Logger.info("Right90 Index: "+indexright90);
if (indexright90!=-1) {
final int finalindex = indexright90;
Thread c5 = new Thread(()-> SetupCameraWithController(image5, camright90, finalindex));
c5.setName("SetupCameraWithController "+camright90);
c5.setDaemon(true);
c5.start();
}
}
}
}
});
}
private void clear(){
isTakingPhoto.set(false);
TextAreaSetText(medicalRecordID,"");
TextAreaSetText(PatientName,"");
TextAreaSetText(barcodeData,"");
}
public void Unload(){
if (image1!=null) {
image1.StopLiveView();
}
if (image2!=null) {
image2.StopLiveView();
}
if (image3!=null) {
image3.StopLiveView();
}
if (image4!=null) {
image4.StopLiveView();
}
if (image5!=null) {
image5.StopLiveView();
}
config.Save();
}
AtomicBoolean anti_bawel = new AtomicBoolean(false);
private void SetupCameraWithController(Cameradetail image, String cameraname, int devicenumber){
if (image!=null){
String title = switch(image.getCameraConfigEnum()){
case CameraConfigCenter -> "03";
case CameraConfigLeft45 -> "02";
case CameraConfigLeft90 -> "01";
case CameraConfigRight45 -> "04";
case CameraConfigRight90 -> "05";
};
final AtomicBoolean _have_face = switch (image.getCameraConfigEnum()){
case CameraConfigCenter -> have_face[2];
case CameraConfigLeft45 -> have_face[1];
case CameraConfigLeft90 -> have_face[0];
case CameraConfigRight45 -> have_face[3];
case CameraConfigRight90 -> have_face[4];
};
final AtomicBoolean _have_profile = switch (image.getCameraConfigEnum()){
case CameraConfigCenter -> have_profile[2];
case CameraConfigLeft45 -> have_profile[1];
case CameraConfigLeft90 -> have_profile[0];
case CameraConfigRight45 -> have_profile[3];
case CameraConfigRight90 -> have_profile[4];
};
final AtomicBoolean _have_eye = switch (image.getCameraConfigEnum()){
case CameraConfigCenter -> have_eyes[2];
case CameraConfigLeft45 -> have_eyes[1];
case CameraConfigLeft90 -> have_eyes[0];
case CameraConfigRight45 -> have_eyes[3];
case CameraConfigRight90 -> have_eyes[4];
};
Platform.runLater(()-> image.setCameraTitle(title));
if (devicenumber!=-1){
// revisi 09/05/2025 dari new OpenCVFrameGrabber(devicenumber)
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(CAP_DSHOW+devicenumber);
// default
int livewidth = 640;
int liveheight = 480;
int photowidth = 640;
int photoheight = 480;
int reducewidth = 640;
int reduceheight = 480;
// mode1 selalu paling tinggi
if (cameraname.contains("ACER QHD")){
photowidth = AcerQHD.ModeBest.getWidth();
photoheight = AcerQHD.ModeBest.getHeight();
livewidth = AcerQHD.ModeLive.getWidth();
liveheight = AcerQHD.ModeLive.getHeight();
} else if (cameraname.contains("AVerVision M15W")){
photowidth = AverVisionM15W.ModeBest.getWidth();
photoheight = AverVisionM15W.ModeBest.getHeight();
livewidth = AverVisionM15W.ModeLive.getWidth();
liveheight = AverVisionM15W.ModeLive.getHeight();
} else if (cameraname.contains("Arducam IMX477")){
// mode1 terbaik 16:9
// mode5 terbaik 4:3
photowidth = ArducamIMX477.ModeBest.getWidth();
photoheight = ArducamIMX477.ModeBest.getHeight();
livewidth = ArducamIMX477.ModeLive.getWidth();
liveheight = ArducamIMX477.ModeLive.getHeight();
} else if (cameraname.contains("OBSBOT Meet 2")){
photowidth = ObsbotMeet2.ModeBest.getWidth();
photoheight = ObsbotMeet2.ModeBest.getHeight();
livewidth = ObsbotMeet2.ModeLive.getWidth();
liveheight = ObsbotMeet2.ModeLive.getHeight();
reducewidth = ObsbotMeet2.Mode3.getWidth();
reduceheight = ObsbotMeet2.Mode3.getHeight();
}
image.SetGrabber(devicenumber,grabber, livewidth,liveheight,photowidth,photoheight,reducewidth,reduceheight, true);
boolean use_face_detector = true;
boolean use_qr_detector = true;
LiveCamEvent lce = new LiveCamEvent() {
@Override
public void onDetectedQRCode(String barCode) {
barCode = RemoveSpaces(barCode);
if (ValidBarCode(barCode)){
String prefix = barcodeData.getText();
if (!barCode.equals(prefix)){
final String finalbarCode = barCode;
TextAreaSetText(barcodeData, finalbarCode);
Task<PatientRecord> checkpatientID = new Task<>() {
@Override
protected PatientRecord call() throws Exception {
BarcodeResullt br = erhaAPI.Validate_Barcode(finalbarCode,true);
if (br!=null){
if (br.message.startsWith("Records found")){
if (br.data!=null && br.data.length>0){
PatientRecord pr = br.data[0];
if (!pr.medical_record_detail_id.isBlank()){
if (!pr.name.isBlank()){
super.succeeded();
return pr;
} else {
Logger.error("Barcode ",finalbarCode," PatientRecord name is empty");
throw new Exception("Barcode "+finalbarCode+" PatientRecord name kosong");
}
} else {
Logger.error("Barcode ",finalbarCode," PatientRecord medical_record_detail_id is empty");
throw new Exception("Data dengan barcode "+finalbarCode+", PatientRecord medical_record_detail_id kosong");
}
} else {
Logger.error("Record associated with barcode ",finalbarCode," is empty");
throw new Exception("Data dengan barcode "+finalbarCode+" ditemukan di server, tetapi kosong");
}
} else {
Logger.error("Record associated with barcode ",finalbarCode," is not found");
throw new Exception("Data dengan barcode "+finalbarCode+" tidak ditemukan di server");
}
} else {
Logger.error("BarcodeResullt with barcode ",finalbarCode," is null");
throw new Exception("BarcodeResult dengan barcode "+finalbarCode+" menghasilkan null");
}
}
};
checkpatientID.setOnSucceeded(event -> {
PatientRecord pr = checkpatientID.getValue();
if (pr!=null){
int medrecid = toInt(pr.medical_record_detail_id);
TextAreaSetText(medicalRecordID,""+medrecid);
TextAreaSetText(PatientName, pr.name);
runningTask = timeoutExecutor.schedule(()->{
// timeout
System.out.println("runningTask timeout after "+timeout+" seconds");
clear();
}, timeout, TimeUnit.SECONDS);
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_posisikan_muka)) {
if (!anti_bawel.get()){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_posisikan_muka, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
anti_bawel.set(true);
}
@Override
public void onPlaybackFinished(String filename) {
anti_bawel.set(false);
Thread antibawel = new Thread(()->{
try {
Thread.sleep(15*1000);
} catch (InterruptedException ignored) {
}
anti_bawel.set(false);
});
antibawel.setName("anti bawel");
antibawel.setDaemon(true);
antibawel.start();
}
@Override
public void onPlaybackFailure(String filename) {
anti_bawel.set(false);
}
});
}
}
}
}
});
checkpatientID.setOnFailed(event -> {
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_data_barcode_tidak_ditemukan)) {
audioPlayer.StopCurrentPlayback();
audioPlayer.PlayFile(audio_data_barcode_tidak_ditemukan,null);
}
}
Task<?> failed = (Task<?>) event.getSource();
Throwable t = failed.getException();
final String message = t.getMessage();
System.out.println("checkpatientID.setOnFailed message : "+message);
AutoCloseAlert.show("Data Tidak Ditemukan", "Pastikan data barcode anda benar", message , 5, s -> clear());
});
Thread checkpatientIDThread = new Thread(checkpatientID);
checkpatientIDThread.setName("CheckPatientID barcode="+barCode);
checkpatientIDThread.setDaemon(true);
checkpatientIDThread.start();
}
}
}
@Override
public void onFrontalFaceDetector(boolean hasface, int width, int height) {
if (hasface!= _have_face.get()){
_have_face.set(hasface);
if (!hasface) {
_have_eye.set(false);
}
//update_status(image);
// instruksi scan barcode ketika welcomeUI masih muncul dan ada muka terdeteksi
if (hasface && Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_scan_barcode)) {
audioPlayer.StopCurrentPlayback();
if (anti_bawel.get()) return;
audioPlayer.PlayFile(audio_scan_barcode, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
anti_bawel.set(true);
}
@Override
public void onPlaybackFinished(String filename) {
Thread antibawel = new Thread(()->{
try {
Thread.sleep(15*1000);
} catch (InterruptedException ignored) {
}
anti_bawel.set(false);
});
antibawel.setName("anti bawel");
antibawel.setDaemon(true);
antibawel.start();
}
@Override
public void onPlaybackFailure(String filename) {
anti_bawel.set(false);
}
});
}
}
}
}
}
@Override
public void onProfileFaceDetector(boolean hasface, int width, int height) {
if (hasface!= _have_profile.get()){
_have_profile.set(hasface);
if (!hasface) {
_have_eye.set(false);
}
//update_status(image);
}
}
@Override
public void onEyeDetector(boolean hasEye) {
if (_have_face.get() || _have_profile.get()) _have_eye.set(hasEye);
//update_status(image);
}
@Override
public void onLog(String log) {
String ss = String.format("[%s] : %s", title, log);
Logger.info(ss);
}
@Override
public void onBlink(int counter) {
}
@Override
public void onDoubleBlink(int counter) {
System.out.println("Double Blink detected at camera "+title+" delay= "+counter);
if (audioPlayer!=null && audioPlayer.isPlaying()) return; // let audio finish playback
if (isTakingPhoto.get()) return; // other camera is taking picture
// revisi 08/04/2025
TakePhotos();
}
@Override
public void onStartCapturing() {
}
@Override
public void onIntervalUpdate() {
update_status(image);
}
private void update_status(Cameradetail image){
String sb = "Camera Started, " +
image.getRealWidth() +
"x" +
image.getRealHeight() +
"@" +
image.getLiveFPS();
image.setCameraStatus(sb);
}
};
image.setCameraStatus("Camera Starting");
if (image.StartLiveView(lce, title, use_qr_detector, use_face_detector)){
//TODO Start Live View berhasil, apa lagi yang mau dikerjakan ?
} else image.setCameraStatus("Unable to Set Grabber");
} else image.setCameraStatus("Camera not found, please check setting");
}
}
private void LoadCameraDetail(AnchorPane cam, int camid, CameraConfigEnum cc){
try{
have_face[camid-1] = new AtomicBoolean(false);
have_eyes[camid-1] = new AtomicBoolean(false);
have_profile[camid-1] = new AtomicBoolean(false);
FXMLLoader loader = new FXMLLoader(getClass().getResource("cameradetail.fxml"));
AnchorPane child = loader.load();
cam.getChildren().clear();
cam.getChildren().add(child);
cam.widthProperty().addListener(observable ->{
//System.out.println("Width property, cam "+camid+" width="+cam.getWidth()+" height="+cam.getHeight());
if (cam.getWidth()!=0) {
if (cam.getHeight()!=0){
AnchorPane.setTopAnchor(child, 0.0);
AnchorPane.setBottomAnchor(child, 0.0);
AnchorPane.setLeftAnchor(child, 0.0);
AnchorPane.setRightAnchor(child, 0.0);
}
}
});
cam.heightProperty().addListener(observable -> {
//System.out.println("Height property, cam "+camid+" width="+cam.getWidth()+" height="+cam.getHeight());
if (cam.getWidth()!=0) {
if (cam.getHeight()!=0){
AnchorPane.setTopAnchor(child, 0.0);
AnchorPane.setBottomAnchor(child, 0.0);
AnchorPane.setLeftAnchor(child, 0.0);
AnchorPane.setRightAnchor(child, 0.0);
}
}
});
switch(camid){
case 1:
image1 = loader.getController();
image1.setCameraConfigEnum(cc);
break;
case 2:
image2 = loader.getController();
image2.setCameraConfigEnum(cc);
break;
case 3:
image3 = loader.getController();
image3.setCameraConfigEnum(cc);
break;
case 4:
image4 = loader.getController();
image4.setCameraConfigEnum(cc);
break;
case 5:
image5 = loader.getController();
image5.setCameraConfigEnum(cc);
break;
}
} catch (Exception e){
Logger.error("Error LoadCameraDetail: " + e.getMessage());
}
}
private void InsertSQL(PhotoReviewClass prc){
Sqlite sql = new Sqlite();
sql.Insert(prc);
}
}