diff --git a/src/main/java/com/litoralregas/backend/BackendApplication.java b/src/main/java/com/litoralregas/backend/BackendApplication.java index 85634f2..529a210 100644 --- a/src/main/java/com/litoralregas/backend/BackendApplication.java +++ b/src/main/java/com/litoralregas/backend/BackendApplication.java @@ -1,12 +1,13 @@ package com.litoralregas.backend; +import com.litoralregas.backend.acquisition.scheduler.AcquisitionSchedulerProperties; import com.litoralregas.backend.modbus.ModbusConnectionProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; @SpringBootApplication -@EnableConfigurationProperties(ModbusConnectionProperties.class) +@EnableConfigurationProperties({ModbusConnectionProperties.class, AcquisitionSchedulerProperties.class}) public class BackendApplication { public static void main(String[] args) { diff --git a/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionRuntimeStatus.java b/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionRuntimeStatus.java new file mode 100644 index 0000000..594250f --- /dev/null +++ b/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionRuntimeStatus.java @@ -0,0 +1,66 @@ +package com.litoralregas.backend.acquisition.scheduler; + +import java.time.Instant; + +public class AcquisitionRuntimeStatus { + + private boolean running; + + private Instant lastStartedAt; + + private Instant lastFinishedAt; + + private Integer lastSuccessfulReads; + + private Integer lastFailedReads; + + private String lastError; + + public boolean isRunning() { + return running; + } + + public void setRunning(boolean running) { + this.running = running; + } + + public Instant getLastStartedAt() { + return lastStartedAt; + } + + public void setLastStartedAt(Instant lastStartedAt) { + this.lastStartedAt = lastStartedAt; + } + + public Instant getLastFinishedAt() { + return lastFinishedAt; + } + + public void setLastFinishedAt(Instant lastFinishedAt) { + this.lastFinishedAt = lastFinishedAt; + } + + public Integer getLastSuccessfulReads() { + return lastSuccessfulReads; + } + + public void setLastSuccessfulReads(Integer lastSuccessfulReads) { + this.lastSuccessfulReads = lastSuccessfulReads; + } + + public Integer getLastFailedReads() { + return lastFailedReads; + } + + public void setLastFailedReads(Integer lastFailedReads) { + this.lastFailedReads = lastFailedReads; + } + + public String getLastError() { + return lastError; + } + + public void setLastError(String lastError) { + this.lastError = lastError; + } +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerConfig.java b/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerConfig.java new file mode 100644 index 0000000..9c4f87f --- /dev/null +++ b/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerConfig.java @@ -0,0 +1,19 @@ +package com.litoralregas.backend.acquisition.scheduler; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +@Configuration +public class AcquisitionSchedulerConfig { + + @Bean + public TaskScheduler acquisitionTaskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(1); + scheduler.setThreadNamePrefix("acquisition-scheduler-"); + scheduler.initialize(); + return scheduler; + } +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerProperties.java b/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerProperties.java new file mode 100644 index 0000000..00613f5 --- /dev/null +++ b/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerProperties.java @@ -0,0 +1,26 @@ +package com.litoralregas.backend.acquisition.scheduler; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "litoralregas.acquisition.scheduler") +public class AcquisitionSchedulerProperties { + + private boolean enabled = false; + private long fixedDelayMillis = 3000; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public long getFixedDelayMillis() { + return fixedDelayMillis; + } + + public void setFixedDelayMillis(long fixedDelayMillis) { + this.fixedDelayMillis = fixedDelayMillis; + } +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerService.java b/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerService.java new file mode 100644 index 0000000..81eff12 --- /dev/null +++ b/src/main/java/com/litoralregas/backend/acquisition/scheduler/AcquisitionSchedulerService.java @@ -0,0 +1,80 @@ +package com.litoralregas.backend.acquisition.scheduler; + +import com.litoralregas.backend.acquisition.polling.AcquisitionPollResult; +import com.litoralregas.backend.acquisition.block.BlockPollingService; +import jakarta.annotation.PostConstruct; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.concurrent.atomic.AtomicBoolean; + +@Service +public class AcquisitionSchedulerService { + + private final BlockPollingService blockPollingService; + private final AcquisitionSchedulerProperties properties; + private final TaskScheduler taskScheduler; + + private final AcquisitionRuntimeStatus runtimeStatus = new AcquisitionRuntimeStatus(); + + private final AtomicBoolean polling = new AtomicBoolean(false); + + public AcquisitionSchedulerService( + BlockPollingService blockPollingService, + AcquisitionSchedulerProperties properties, + TaskScheduler taskScheduler + ) { + this.blockPollingService = blockPollingService; + this.properties = properties; + this.taskScheduler = taskScheduler; + } + + @PostConstruct + public void startScheduler() { + if (!properties.isEnabled()) { + System.out.println("Acquisition scheduler disabled."); + return; + } + + System.out.println("Starting acquisition scheduler."); + + taskScheduler.scheduleWithFixedDelay( + this::safePoll, + properties.getFixedDelayMillis() + ); + } + + public AcquisitionRuntimeStatus getRuntimeStatus() { + return runtimeStatus; + } + + private void safePoll() { + if (!polling.compareAndSet(false, true)) { + System.out.println("Skipping acquisition cycle because previous cycle is still running."); + return; + } + + runtimeStatus.setRunning(true); + runtimeStatus.setLastStartedAt(Instant.now()); + runtimeStatus.setLastError(null); + + try { + AcquisitionPollResult result = blockPollingService.pollOnceByBlocks(); + + runtimeStatus.setLastSuccessfulReads(result.successfulReads()); + runtimeStatus.setLastFailedReads(result.failedReads()); + + } catch (Exception exception) { + runtimeStatus.setLastError(exception.getMessage()); + + exception.printStackTrace(); + + } finally { + runtimeStatus.setRunning(false); + runtimeStatus.setLastFinishedAt(Instant.now()); + + polling.set(false); + } + } +} \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 02cabd0..4fdbaad 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -21,4 +21,8 @@ litoralregas: port: 533 timeout-millis: 500 max-attempts: 3 - retry-delay-millis: 1000 \ No newline at end of file + retry-delay-millis: 1000 + acquisition: + scheduler: + enabled: true + fixed-delay-millis: 3000 \ No newline at end of file