diff --git a/data-consumer/data-consumer.iml b/data-consumer/data-processor.iml similarity index 100% rename from data-consumer/data-consumer.iml rename to data-consumer/data-processor.iml diff --git a/data-consumer/src/main/java/com/artgeektech/iotmicroservices/controller/DataConsumerController.java b/data-consumer/src/main/java/com/artgeektech/iotmicroservices/controller/DataConsumerController.java index 3e0e973..81065c8 100644 --- a/data-consumer/src/main/java/com/artgeektech/iotmicroservices/controller/DataConsumerController.java +++ b/data-consumer/src/main/java/com/artgeektech/iotmicroservices/controller/DataConsumerController.java @@ -3,46 +3,38 @@ import com.artgeektech.iotmicroservices.Constants; import com.artgeektech.iotmicroservices.model.AirData; import com.artgeektech.iotmicroservices.repository.AirDataRepository; +import com.artgeektech.iotmicroservices.service.RuleEngineService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Controller; /** * Created by guang on 11:56 AM 8/18/18. */ -@Component +@Controller public class DataConsumerController { private static final Logger logger = LoggerFactory.getLogger(DataConsumerController.class); @Autowired private AirDataRepository airDataRepository; - @RabbitListener(queues = Constants.QUEUE_NAME) + @Autowired + private RuleEngineService ruleEngineService; + + @RabbitListener(queues = Constants.QUEUE_NAME) // Subscribe to the Message Queue public void process(AirData airData) { logger.info("Received message from MQ '{}'", airData); - // simple rule engine handling - if (airData.getTemperature() > 50) { - triggerActionAlert("Temperature too high!!"); - } - if (airData.getCo2() > 50) { - triggerActionAlert("CO2 too high!!!"); - } - if (airData.getPm2p5() > 50) { - triggerActionAlert("Too much Dust!!!"); - } + // apply rule engine and trigger actions + ruleEngineService.applyRules(airData); // save to DB airDataRepository.save(airData); + logger.info("Saved message to Mongo DB '{}'", airData); logger.info("Total message saved in Mongo DB is: " + airDataRepository.findAll().size()); - - } - - private void triggerActionAlert(String msg) { - System.out.println("\n\n!!!!!Sending the Email Alert: " + msg + "\n\n"); } } diff --git a/data-consumer/src/main/java/com/artgeektech/iotmicroservices/service/RuleEngineService.java b/data-consumer/src/main/java/com/artgeektech/iotmicroservices/service/RuleEngineService.java new file mode 100644 index 0000000..8595f13 --- /dev/null +++ b/data-consumer/src/main/java/com/artgeektech/iotmicroservices/service/RuleEngineService.java @@ -0,0 +1,27 @@ +package com.artgeektech.iotmicroservices.service; + +import com.artgeektech.iotmicroservices.model.AirData; +import org.springframework.stereotype.Component; + +/** + * Created by guang on 7:16 PM 8/21/18. + */ +@Component +public class RuleEngineService { + + public void applyRules(AirData airData) { + if (airData.getTemperature() > 50) { + triggerActionAlert("Temperature too high!!"); + } + if (airData.getCo2() > 50) { + triggerActionAlert("CO2 too high!!!"); + } + if (airData.getPm2p5() > 50) { + triggerActionAlert("Too much Dust!!!"); + } + } + + private void triggerActionAlert(String msg) { + System.out.println("\n\n!!!!!Sending the Email Alert: " + msg + "\n\n"); + } +} diff --git a/data-dashboard/data-dashboard.iml b/data-dashboard/data-dashboard.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/data-dashboard/data-dashboard.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/data-dashboard/pom.xml b/data-dashboard/pom.xml new file mode 100644 index 0000000..6ec9a4d --- /dev/null +++ b/data-dashboard/pom.xml @@ -0,0 +1,15 @@ + + + + iotmicroservices + com.artgeektech + 1.0.0-SNAPSHOT + + 4.0.0 + + datadashboard + + + \ No newline at end of file diff --git a/data-dashboard/src/main/java/DataDashboardController.java b/data-dashboard/src/main/java/DataDashboardController.java new file mode 100644 index 0000000..8fc0f3f --- /dev/null +++ b/data-dashboard/src/main/java/DataDashboardController.java @@ -0,0 +1,7 @@ +package PACKAGE_NAME; + +/** + * Created by guang on 7:51 PM 8/20/18. + */ +public class DataDashboardController { +} diff --git a/data-dashboard/src/main/resources/templates/js/html/dashboard.html b/data-dashboard/src/main/resources/templates/js/html/dashboard.html new file mode 100644 index 0000000..e69de29 diff --git a/data-dashboard/src/main/resources/templates/js/html/dashboard.js b/data-dashboard/src/main/resources/templates/js/html/dashboard.js new file mode 100644 index 0000000..e69de29 diff --git a/data-ingest/pom.xml b/data-ingest/pom.xml index 21415af..c9c862f 100644 --- a/data-ingest/pom.xml +++ b/data-ingest/pom.xml @@ -20,5 +20,19 @@ org.springframework.amqp spring-rabbit + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-jetty + \ No newline at end of file diff --git a/data-ingest/src/main/java/com/artgeektech/iotmicroservices/DataIngestApplication.java b/data-ingest/src/main/java/com/artgeektech/iotmicroservices/DataIngestApplication.java index 2185ca9..af1f16f 100644 --- a/data-ingest/src/main/java/com/artgeektech/iotmicroservices/DataIngestApplication.java +++ b/data-ingest/src/main/java/com/artgeektech/iotmicroservices/DataIngestApplication.java @@ -18,6 +18,11 @@ public Exchange airDataExchange() { return new TopicExchange(Constants.EXCHANGE_NAME); } +// @Bean +// public SimpMessagingTemplate simpMessagingTemplate() { +// return new SimpMessagingTemplate(); +// } + public static void main(String[] args) { SpringApplication.run(DataIngestApplication.class, args); } diff --git a/data-ingest/src/main/java/com/artgeektech/iotmicroservices/controller/DataIngestController.java b/data-ingest/src/main/java/com/artgeektech/iotmicroservices/controller/DataIngestController.java index 684d00e..c4cc4b6 100644 --- a/data-ingest/src/main/java/com/artgeektech/iotmicroservices/controller/DataIngestController.java +++ b/data-ingest/src/main/java/com/artgeektech/iotmicroservices/controller/DataIngestController.java @@ -2,6 +2,7 @@ import com.artgeektech.iotmicroservices.Constants; import com.artgeektech.iotmicroservices.model.AirData; +import com.artgeektech.iotmicroservices.model.AirRawData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Exchange; @@ -9,7 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @@ -17,28 +17,43 @@ @RestController -@RequestMapping("/iot/airdata") public class DataIngestController { private static final Logger logger = LoggerFactory.getLogger(DataIngestController.class); +// @Autowired +// private SimpMessagingTemplate messagingTemplate; + @Autowired private RabbitTemplate rabbitTemplate; @Autowired private Exchange exchange; - @PostMapping("/ingest") - public AirData ingest(@Valid @RequestBody AirData airData) { - preprocess(airData); + @PostMapping("/airdata/ingest") // validate payload from request body + public AirData ingest(@Valid @RequestBody AirRawData rawData) { + // preprocess + AirData airData = preprocess(rawData); + // publish to MQ rabbitTemplate.convertAndSend(exchange.getName(), Constants.ROUTING_KEY, airData); logger.info("ingested data: " + airData.toString()); return airData; } - private void preprocess(AirData airData) { + + + private AirData preprocess(AirRawData rawData) { + AirData airData = new AirData(); + + // add more info from system airData.setTimestamp(new Date()); - airData.setHumidity(Math.round(airData.getHumidity() * 100.0) / 100.0); - airData.setTemperature(Math.round(airData.getTemperature() * 100.0) / 100.0); + + // standardize data format + airData.setHumidity(Math.round(rawData.getHumidity() * 100.0) / 100.0); + airData.setTemperature(Math.round(rawData.getTemperature() * 100.0) / 100.0); + airData.setCo2(Math.round(rawData.getCo2() * 100.0) / 100.0); + airData.setPm2p5(Math.round(rawData.getPm2p5() * 100.0) / 100.0); + + return airData; } } diff --git a/data-ingest/src/main/java/com/artgeektech/iotmicroservices/model/AirData.java b/data-ingest/src/main/java/com/artgeektech/iotmicroservices/model/AirData.java index d33ef5b..68ced4b 100644 --- a/data-ingest/src/main/java/com/artgeektech/iotmicroservices/model/AirData.java +++ b/data-ingest/src/main/java/com/artgeektech/iotmicroservices/model/AirData.java @@ -4,9 +4,6 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Date; @@ -14,26 +11,9 @@ @AllArgsConstructor @NoArgsConstructor public class AirData implements Serializable { - - @NotNull - @Min(1) - @Max(100) private Double temperature; - - @NotNull - @Min(1) - @Max(100) private Double humidity; - - @NotNull - @Min(1) - @Max(100) private Double pm2p5; - - @NotNull - @Min(1) - @Max(100) private Double co2; - private Date timestamp; } diff --git a/data-ingest/src/main/java/com/artgeektech/iotmicroservices/model/AirRawData.java b/data-ingest/src/main/java/com/artgeektech/iotmicroservices/model/AirRawData.java new file mode 100644 index 0000000..70af1cc --- /dev/null +++ b/data-ingest/src/main/java/com/artgeektech/iotmicroservices/model/AirRawData.java @@ -0,0 +1,41 @@ +package com.artgeektech.iotmicroservices.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AirRawData implements Serializable { +// private Double temperature; +// private Double humidity; +// private Double pm2p5; +// private Double co2; + + @NotNull + @Min(1) + @Max(100) + private Double temperature; + + @NotNull + @Min(1) + @Max(100) + private Double humidity; + + @NotNull + @Min(1) + @Max(100) + private Double pm2p5; + + @NotNull + @Min(1) + @Max(100) + private Double co2; + +} diff --git a/data-service/data-service-api-gateway.iml b/data-service/data-service-api-gateway.iml new file mode 100644 index 0000000..78b2cc5 --- /dev/null +++ b/data-service/data-service-api-gateway.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data-service/pom.xml b/data-service/pom.xml new file mode 100644 index 0000000..06f732f --- /dev/null +++ b/data-service/pom.xml @@ -0,0 +1,15 @@ + + + + iotmicroservices + com.artgeektech + 1.0.0-SNAPSHOT + + 4.0.0 + + dataservice + + + \ No newline at end of file diff --git a/data-simulator/src/main/java/com/artgeektech/iotmicroservices/DataSimulatorApplication.java b/data-simulator/src/main/java/com/artgeektech/iotmicroservices/DataSimulatorApplication.java index eca825e..89546e5 100644 --- a/data-simulator/src/main/java/com/artgeektech/iotmicroservices/DataSimulatorApplication.java +++ b/data-simulator/src/main/java/com/artgeektech/iotmicroservices/DataSimulatorApplication.java @@ -10,7 +10,7 @@ public class DataSimulatorApplication { - private static final String resourceUrl = "http://data-ingest:9001/iot/airdata/ingest"; + private static final String resourceUrl = "http://localhost:9001/airdata/ingest"; private static final RestTemplate restTemplate = new RestTemplate(); private static final Random random = new Random(); private static double minVal = 10; @@ -19,12 +19,13 @@ public class DataSimulatorApplication { private static int interval = 1000; public static void main(String[] args) { + timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { AirRawData payload = new AirRawData(genRandom(), genRandom(), genRandom(), genRandom()); HttpEntity request = new HttpEntity<>(payload); - restTemplate.postForObject(resourceUrl, request, AirRawData.class); + restTemplate.postForObject(resourceUrl, request, Object.class); } }, 0, interval); } @@ -32,7 +33,7 @@ public void run() { private static double genRandom() { int randInt = random.nextInt(5); if (randInt == 0) { - return 100; + return 99; } else { return minVal + random.nextDouble() * (maxVal - minVal); } diff --git a/data-simulator/src/main/java/com/artgeektech/iotmicroservices/model/AirRawData.java b/data-simulator/src/main/java/com/artgeektech/iotmicroservices/model/AirRawData.java index 71f36f3..2adaa4c 100644 --- a/data-simulator/src/main/java/com/artgeektech/iotmicroservices/model/AirRawData.java +++ b/data-simulator/src/main/java/com/artgeektech/iotmicroservices/model/AirRawData.java @@ -10,8 +10,8 @@ @AllArgsConstructor @NoArgsConstructor public class AirRawData implements Serializable { - private double temperature; - private double humidity; - private double pm2p5; - private double co2; + private Double temperature; + private Double humidity; + private Double pm2p5; + private Double co2; } diff --git a/docker-rabbitmq/docker-compose.yml b/docker-rabbitmq/docker-compose.yml index 7db4553..3358fd4 100644 --- a/docker-rabbitmq/docker-compose.yml +++ b/docker-rabbitmq/docker-compose.yml @@ -1,6 +1,10 @@ -rabbitmq: - image: rabbitmq:management - container_name: "rabbitmq" - ports: - - 5672:5672 - - 15672:15672 \ No newline at end of file +version: '2' +services: + sonar: + container_name: aiwin-rabbit + image: aiwin/rabbitmq-stomp:latest + ports: + - 61613:61613 + - 15674:15674 + - 15672:15672 + - 5672:5672 \ No newline at end of file diff --git a/service-registry/src/main/resources/application.yml b/service-registry/src/main/resources/application.yml index a3a8810..d4f57ad 100644 --- a/service-registry/src/main/resources/application.yml +++ b/service-registry/src/main/resources/application.yml @@ -7,7 +7,7 @@ server: eureka: instance: - hostname: service-registry # 指定该Eureka实例的主机名 + hostname: localhost # 指定该Eureka实例的主机名 client: registerWithEureka: false fetchRegistry: false diff --git a/service-registry/target/classes/application.yml b/service-registry/target/classes/application.yml index a3a8810..d4f57ad 100644 --- a/service-registry/target/classes/application.yml +++ b/service-registry/target/classes/application.yml @@ -7,7 +7,7 @@ server: eureka: instance: - hostname: service-registry # 指定该Eureka实例的主机名 + hostname: localhost # 指定该Eureka实例的主机名 client: registerWithEureka: false fetchRegistry: false