Start from robottle with Helidon

This commit is contained in:
Julien Lengrand-Lambert
2019-11-24 10:28:30 +01:00
commit 6f64ad8f32
41 changed files with 1765 additions and 0 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
target/*

59
.gitignore vendored Normal file
View File

@@ -0,0 +1,59 @@
.DS_Store
.idea/shelf
/android.tests.dependencies
/confluence/target
/dependencies/repo
/dist
/local
/gh-pages
/ideaSDK
/clionSDK
/android-studio/sdk
out/
/tmp
workspace.xml
*.versionsBackup
/idea/testData/debugger/tinyApp/classes*
/jps-plugin/testData/kannotator
/js/js.translator/testData/out/
/js/js.translator/testData/out-min/
.gradle/
build/
!**/src/**/build
!**/test/**/build
*.iml
!**/testData/**/*.iml
.idea/libraries/Gradle*.xml
.idea/libraries/Maven*.xml
.idea/artifacts/PILL_*.xml
.idea/artifacts/KotlinPlugin.xml
.idea/modules
.idea/runConfigurations/JPS_*.xml
.idea/runConfigurations/PILL_*.xml
.idea/libraries
.idea/modules.xml
.idea/gradle.xml
.idea/compiler.xml
.idea/inspectionProfiles/profiles_settings.xml
.idea/.name
.idea/artifacts/dist_auto_*
.idea/artifacts/dist.xml
.idea/artifacts/ideaPlugin.xml
.idea/artifacts/kotlinc.xml
.idea/artifacts/kotlin_compiler_jar.xml
.idea/artifacts/kotlin_plugin_jar.xml
.idea/artifacts/kotlin_jps_plugin_jar.xml
.idea/artifacts/kotlin_daemon_client_jar.xml
.idea/artifacts/kotlin_imports_dumper_compiler_plugin_jar.xml
.idea/artifacts/kotlin_main_kts_jar.xml
.idea/artifacts/kotlin_compiler_client_embeddable_jar.xml
.idea/artifacts/kotlin_reflect_jar.xml
kotlin-ultimate/
node_modules/
.rpt2_cache/
libraries/tools/kotlin-test-js-runner/lib/
libraries/tools/kotlin-source-map-loader/lib/
local.properties
*.o
target

19
.idea/$PRODUCT_WORKSPACE_FILE$ generated Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.8</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

15
.idea/deployment.xml generated Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" autoUpload="Always" serverName="raspberry.local" autoUploadExternalChanges="true">
<serverData>
<paths name="raspberry.local">
<serverdata>
<mappings>
<mapping deploy="/" local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
</serverData>
<option name="myAutoUpload" value="ALWAYS" />
</component>
</project>

6
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$" charset="UTF-8" />
</component>
</project>

17
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ElmWorkspace">
<elmProjects />
<settings binDirPath="" />
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

15
.idea/webServers.xml generated Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebServers">
<option name="servers">
<webServer id="d7eae866-388d-4485-8638-0714f981dbf8" name="raspberry.local" url="http://raspberrypi.local">
<fileTransfer host="raspberrypi.local" port="22" rootFolder="/home/pi/projects/therobottle" accessType="SFTP">
<advancedOptions>
<advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" />
</advancedOptions>
<option name="port" value="22" />
</fileTransfer>
</webServer>
</option>
</component>
</project>

44
Dockerfile Normal file
View File

@@ -0,0 +1,44 @@
#
# Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# 1st stage, build the app
FROM maven:3.5.4-jdk-9 as build
WORKDIR /helidon
# Create a first layer to cache the "Maven World" in the local repository.
# Incremental docker builds will always resume after that, unless you update
# the pom
ADD pom.xml .
RUN mvn package -DskipTests
# Do the Maven build!
# Incremental docker builds will resume here when you change sources
ADD src src
RUN mvn package -DskipTests
RUN echo "done!"
# 2nd stage, build the runtime image
FROM openjdk:8-jre-slim
WORKDIR /helidon
# Copy the binary built in the 1st stage
COPY --from=build /helidon/target/therobottle.jar ./
COPY --from=build /helidon/target/libs ./libs
CMD ["java", "-jar", "therobottle.jar"]
EXPOSE 8080

35
README.md Normal file
View File

@@ -0,0 +1,35 @@
# Robottle
WIP: Don't expect any structure in there. As of now it's a collection of tryouts without particular shape
A raspberry based sensor to keep my wine cellar under control
## Hardware :
* Raspberry (starting with a Raspberry 1)
* Maybe Piface
* DHT11 4 pins
## Readings
http://www.circuitbasics.com/how-to-set-up-the-dht11-humidity-sensor-on-the-raspberry-pi/
http://sebi.io/general/2018/08/13/Developing-for-Raspberry-Pi.html
https://medium.com/@menchukanton/setup-intellij-idea-for-remote-debugging-java-code-on-a-raspberry-pi-6e9df09dfb95
http://wiringpi.com/download-and-install/
https://github.com/cory-johannsen/gradle-jni-example
## Author
[Julien Lengrand-Lambert](https://github.com/jlengrand)
pi/robottle
raspberrypi
ping raspberrypi.local
http://hirt.se/blog/?p=1116
https://github.com/fauna/faunadb-jvm/blob/master/docs/java.md

68
README_helidon.md Normal file
View File

@@ -0,0 +1,68 @@
# Helidon Quickstart MP Example
This example implements a simple Hello World REST service using MicroProfile.
## Build and run
With JDK8+
```bash
mvn package
java -jar target/therobottle.jar
```
## Exercise the application
```
curl -X GET http://localhost:8080/greet
{"message":"Hello World!"}
curl -X GET http://localhost:8080/greet/Joe
{"message":"Hello Joe!"}
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting
curl -X GET http://localhost:8080/greet/Jose
{"message":"Hola Jose!"}
```
## Try health and metrics
```
curl -s -X GET http://localhost:8080/health
{"outcome":"UP",...
. . .
# Prometheus Format
curl -s -X GET http://localhost:8080/metrics
# TYPE base:gc_g1_young_generation_count gauge
. . .
# JSON Format
curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics
{"base":...
. . .
```
## Build the Docker Image
```
docker build -t therobottle .
```
## Start the application with Docker
```
docker run --rm -p 8080:8080 therobottle:latest
```
Exercise the application as described above
## Deploy the application to Kubernetes
```
kubectl cluster-info # Verify which cluster
kubectl get pods # Verify connectivity to cluster
kubectl create -f app.yaml # Deploy application
kubectl get service therobottle # Verify deployed service
```

50
app.yaml Normal file
View File

@@ -0,0 +1,50 @@
#
# Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
kind: Service
apiVersion: v1
metadata:
name: therobottle
labels:
app: therobottle
spec:
type: NodePort
selector:
app: therobottle
ports:
- port: 8080
targetPort: 8080
name: http
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: therobottle
spec:
replicas: 1
template:
metadata:
labels:
app: therobottle
version: v1
spec:
containers:
- name: therobottle
image: therobottle
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
---

87
pom.xml Normal file
View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.helidon.applications</groupId>
<artifactId>helidon-mp</artifactId>
<version>1.4.0</version>
<relativePath/>
</parent>
<groupId>nl.lengrand</groupId>
<artifactId>therobottle</artifactId>
<name>${project.artifactId}</name>
<properties>
<mainClass>nl.lengrand.therobottle.Main</mainClass>
</properties>
<dependencies>
<dependency>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>helidon-microprofile</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
</dependency>
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>javax.activation-api</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-libs</id>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<executions>
<execution>
<id>make-index</id>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

29
src/main/c/Dht11Driver.c Normal file
View File

@@ -0,0 +1,29 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <jni.h>
#include "../java/nl/lengrand/therobottle/driver/nl_lengrand_therobottle_driver_Dht11Driver.h"
#include "adafruit/Raspberry_Pi_2/pi_2_dht_read.h"
JNIEXPORT void JNICALL Java_Dht11Driver_sayHello(JNIEnv *env, jobject thisObj) {
printf("Hello JNI!\n");
return;
}
JNIEXPORT jfloatArray JNICALL Java_Dht11Driver_getTemperatureAndHumidity(JNIEnv *env, jobject thisObj){
float humidity = 0, temperature = 0;
int sensor = 11; // Make those dynamic one day?
int pin = 4; // Make those dynamic one day?
int res = pi_2_dht_read(sensor, pin, &humidity, &temperature); // Might wanna do something with status one day
jfloat* values = (jfloat *) malloc(2*sizeof(jfloat));
values[0] = temperature;
values[1] = humidity;
jfloatArray outJNIArray = (*env)->NewFloatArray(env, 2);
if (NULL == outJNIArray) return NULL;
(*env)->SetFloatArrayRegion(env, outJNIArray, 0, 2, (const jfloat*)values);
return outJNIArray;
}

26
src/main/c/Makefile Normal file
View File

@@ -0,0 +1,26 @@
JAVA_HOME=/usr/lib/jvm/java-11-openjdk-armhf
INCLUDES = -I. -I../java -Iadafruit/Raspberry_Pi_2 -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
HEADERS = ../java/nl/lengrand/therobottle/driver/nl_lengrand_therobottle_driver_Dht11Driver.h adafruit/common_dht_read.h adafruit/Raspberry_Pi_2/pi_2_mmio.h adafruit/Raspberry_Pi_2/pi_2_dht_read.h
FILES = adafruit/common_dht_read.c adafruit/Raspberry_Pi_2/pi_2_mmio.c adafruit/Raspberry_Pi_2/pi_2_dht_read.c Dht11Driver.c
OUT = libdht11
default: all
all: clean compileJava library
program: Dht11Driver.c
gcc -Wall $(INCLUDES) $(HEADERS) $(FILES) -o $(OUT)
library: Dht11Driver.c
gcc -shared -fPIC -o $(OUT).so $(INCLUDES) $(FILES)
move:
-cp $(OUT).so
compileJava:
-cd ../java/nl/lengrand/therobottle/driver;javac -h . Dht11Driver.java;cd -;
clean:
-rm ../java/nl/lengrand/therobottle/driver/nl_lengrand_therobottle_driver_Dht11Driver.h
-rm -f $(OUT).so
-rm -f $(OUT)

1
src/main/c/README.md Normal file
View File

@@ -0,0 +1 @@
The code contained in the adafruit folder comes from https://github.com/adafruit/Adafruit_Python_DHT and will be used as driver

View File

@@ -0,0 +1,158 @@
// Copyright (c) 2014 Adafruit Industries
// Author: Tony DiCola
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <stdbool.h>
#include <stdlib.h>
#include "pi_2_dht_read.h"
#include "pi_2_mmio.h"
// This is the only processor specific magic value, the maximum amount of time to
// spin in a loop before bailing out and considering the read a timeout. This should
// be a high value, but if you're running on a much faster platform than a Raspberry
// Pi or Beaglebone Black then it might need to be increased.
#define DHT_MAXCOUNT 32000
// Number of bit pulses to expect from the DHT. Note that this is 41 because
// the first pulse is a constant 50 microsecond pulse, with 40 pulses to represent
// the data afterwards.
#define DHT_PULSES 41
int pi_2_dht_read(int type, int pin, float* humidity, float* temperature) {
// Validate humidity and temperature arguments and set them to zero.
if (humidity == NULL || temperature == NULL) {
return DHT_ERROR_ARGUMENT;
}
*temperature = 0.0f;
*humidity = 0.0f;
// Initialize GPIO library.
if (pi_2_mmio_init() < 0) {
return DHT_ERROR_GPIO;
}
// Store the count that each DHT bit pulse is low and high.
// Make sure array is initialized to start at zero.
int pulseCounts[DHT_PULSES*2] = {0};
// Set pin to output.
pi_2_mmio_set_output(pin);
// Bump up process priority and change scheduler to try to try to make process more 'real time'.
set_max_priority();
// Set pin high for ~500 milliseconds.
pi_2_mmio_set_high(pin);
sleep_milliseconds(500);
// The next calls are timing critical and care should be taken
// to ensure no unnecssary work is done below.
// Set pin low for ~20 milliseconds.
pi_2_mmio_set_low(pin);
busy_wait_milliseconds(20);
// Set pin at input.
pi_2_mmio_set_input(pin);
// Need a very short delay before reading pins or else value is sometimes still low.
for (volatile int i = 0; i < 50; ++i) {
}
// Wait for DHT to pull pin low.
uint32_t count = 0;
while (pi_2_mmio_input(pin)) {
if (++count >= DHT_MAXCOUNT) {
// Timeout waiting for response.
set_default_priority();
return DHT_ERROR_TIMEOUT;
}
}
// Record pulse widths for the expected result bits.
for (int i=0; i < DHT_PULSES*2; i+=2) {
// Count how long pin is low and store in pulseCounts[i]
while (!pi_2_mmio_input(pin)) {
if (++pulseCounts[i] >= DHT_MAXCOUNT) {
// Timeout waiting for response.
set_default_priority();
return DHT_ERROR_TIMEOUT;
}
}
// Count how long pin is high and store in pulseCounts[i+1]
while (pi_2_mmio_input(pin)) {
if (++pulseCounts[i+1] >= DHT_MAXCOUNT) {
// Timeout waiting for response.
set_default_priority();
return DHT_ERROR_TIMEOUT;
}
}
}
// Done with timing critical code, now interpret the results.
// Drop back to normal priority.
set_default_priority();
// Compute the average low pulse width to use as a 50 microsecond reference threshold.
// Ignore the first two readings because they are a constant 80 microsecond pulse.
uint32_t threshold = 0;
for (int i=2; i < DHT_PULSES*2; i+=2) {
threshold += pulseCounts[i];
}
threshold /= DHT_PULSES-1;
// Interpret each high pulse as a 0 or 1 by comparing it to the 50us reference.
// If the count is less than 50us it must be a ~28us 0 pulse, and if it's higher
// then it must be a ~70us 1 pulse.
uint8_t data[5] = {0};
for (int i=3; i < DHT_PULSES*2; i+=2) {
int index = (i-3)/16;
data[index] <<= 1;
if (pulseCounts[i] >= threshold) {
// One bit for long pulse.
data[index] |= 1;
}
// Else zero bit for short pulse.
}
// Useful debug info:
//printf("Data: 0x%x 0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3], data[4]);
// Verify checksum of received data.
if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
if (type == DHT11) {
// Get humidity and temp for DHT11 sensor.
*humidity = (float)data[0];
*temperature = (float)data[2];
}
else if (type == DHT22) {
// Calculate humidity and temp for DHT22 sensor.
*humidity = (data[0] * 256 + data[1]) / 10.0f;
*temperature = ((data[2] & 0x7F) * 256 + data[3]) / 10.0f;
if (data[2] & 0x80) {
*temperature *= -1.0f;
}
}
return DHT_SUCCESS;
}
else {
return DHT_ERROR_CHECKSUM;
}
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2014 Adafruit Industries
// Author: Tony DiCola
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef PI_2_DHT_READ_H
#define PI_2_DHT_READ_H
#include "../common_dht_read.h"
// Read DHT sensor connected to GPIO pin (using BCM numbering). Humidity and temperature will be
// returned in the provided parameters. If a successfull reading could be made a value of 0
// (DHT_SUCCESS) will be returned. If there was an error reading the sensor a negative value will
// be returned. Some errors can be ignored and retried, specifically DHT_ERROR_TIMEOUT or DHT_ERROR_CHECKSUM.
int pi_2_dht_read(int sensor, int pin, float* humidity, float* temperature);
#endif

View File

@@ -0,0 +1,71 @@
// Copyright (c) 2014 Adafruit Industries
// Author: Tony DiCola
// Based on code from Gert van Loo & Dom: http://elinux.org/RPi_Low-level_peripherals#GPIO_Code_examples
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "pi_2_mmio.h"
#define GPIO_BASE_OFFSET 0x200000
#define GPIO_LENGTH 4096
volatile uint32_t* pi_2_mmio_gpio = NULL;
int pi_2_mmio_init(void) {
if (pi_2_mmio_gpio == NULL) {
// Check for GPIO and peripheral addresses from device tree.
// Adapted from code in the RPi.GPIO library at:
// http://sourceforge.net/p/raspberry-gpio-python/
FILE *fp = fopen("/proc/device-tree/soc/ranges", "rb");
if (fp == NULL) {
return MMIO_ERROR_OFFSET;
}
fseek(fp, 4, SEEK_SET);
unsigned char buf[4];
if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) {
return MMIO_ERROR_OFFSET;
}
uint32_t peri_base = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0;
uint32_t gpio_base = peri_base + GPIO_BASE_OFFSET;
fclose(fp);
int fd = open("/dev/gpiomem", O_RDWR | O_SYNC);
if (fd == -1) {
// Error opening /dev/gpiomem.
return MMIO_ERROR_DEVMEM;
}
// Map GPIO memory to location in process space.
pi_2_mmio_gpio = (uint32_t*)mmap(NULL, GPIO_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, gpio_base);
close(fd);
if (pi_2_mmio_gpio == MAP_FAILED) {
// Don't save the result if the memory mapping failed.
pi_2_mmio_gpio = NULL;
return MMIO_ERROR_MMAP;
}
}
return MMIO_SUCCESS;
}

View File

@@ -0,0 +1,62 @@
// Copyright (c) 2014 Adafruit Industries
// Author: Tony DiCola
// Based on code from Gert van Loo & Dom: http://elinux.org/RPi_Low-level_peripherals#GPIO_Code_examples
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Simple fast memory-mapped GPIO library for the Raspberry Pi.
#ifndef PI_2_MMIO_H
#define PI_2_MMIO_H
#include <stdint.h>
#define MMIO_SUCCESS 0
#define MMIO_ERROR_DEVMEM -1
#define MMIO_ERROR_MMAP -2
#define MMIO_ERROR_OFFSET -3
extern volatile uint32_t* pi_2_mmio_gpio;
int pi_2_mmio_init(void);
static inline void pi_2_mmio_set_input(const int gpio_number) {
// Set GPIO register to 000 for specified GPIO number.
*(pi_2_mmio_gpio+((gpio_number)/10)) &= ~(7<<(((gpio_number)%10)*3));
}
static inline void pi_2_mmio_set_output(const int gpio_number) {
// First set to 000 using input function.
pi_2_mmio_set_input(gpio_number);
// Next set bit 0 to 1 to set output.
*(pi_2_mmio_gpio+((gpio_number)/10)) |= (1<<(((gpio_number)%10)*3));
}
static inline void pi_2_mmio_set_high(const int gpio_number) {
*(pi_2_mmio_gpio+7) = 1 << gpio_number;
}
static inline void pi_2_mmio_set_low(const int gpio_number) {
*(pi_2_mmio_gpio+10) = 1 << gpio_number;
}
static inline uint32_t pi_2_mmio_input(const int gpio_number) {
return *(pi_2_mmio_gpio+13) & (1 << gpio_number);
}
#endif

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2014 Adafruit Industries
// Author: Tony DiCola
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <errno.h>
#include <sched.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include "common_dht_read.h"
void busy_wait_milliseconds(uint32_t millis) {
// Set delay time period.
struct timeval deltatime;
deltatime.tv_sec = millis / 1000;
deltatime.tv_usec = (millis % 1000) * 1000;
struct timeval walltime;
// Get current time and add delay to find end time.
gettimeofday(&walltime, NULL);
struct timeval endtime;
timeradd(&walltime, &deltatime, &endtime);
// Tight loop to waste time (and CPU) until enough time as elapsed.
while (timercmp(&walltime, &endtime, <)) {
gettimeofday(&walltime, NULL);
}
}
void sleep_milliseconds(uint32_t millis) {
struct timespec sleep;
sleep.tv_sec = millis / 1000;
sleep.tv_nsec = (millis % 1000) * 1000000L;
while (clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, &sleep) && errno == EINTR);
}
void set_max_priority(void) {
struct sched_param sched;
memset(&sched, 0, sizeof(sched));
// Use FIFO scheduler with highest priority for the lowest chance of the kernel context switching.
sched.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &sched);
}
void set_default_priority(void) {
struct sched_param sched;
memset(&sched, 0, sizeof(sched));
// Go back to default scheduler with default 0 priority.
sched.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &sched);
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2014 Adafruit Industries
// Author: Tony DiCola
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef COMMON_DHT_READ_H
#define COMMON_DHT_READ_H
#include <stdint.h>
// Define errors and return values.
#define DHT_ERROR_TIMEOUT -1
#define DHT_ERROR_CHECKSUM -2
#define DHT_ERROR_ARGUMENT -3
#define DHT_ERROR_GPIO -4
#define DHT_SUCCESS 0
// Define sensor types.
#define DHT11 11
#define DHT22 22
#define AM2302 22
// Busy wait delay for most accurate timing, but high CPU usage.
// Only use this for short periods of time (a few hundred milliseconds at most)!
void busy_wait_milliseconds(uint32_t millis);
// General delay that sleeps so CPU usage is low, but accuracy is potentially bad.
void sleep_milliseconds(uint32_t millis);
// Increase scheduling priority and algorithm to try to get 'real time' results.
void set_max_priority(void);
// Drop scheduling priority back to normal/default.
void set_default_priority(void);
#endif

4
src/main/c/compile.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
rm testAdafruit;
gcc -Wall -I. -Iadafruit/Raspberry_Pi_2 adafruit/common_dht_read.c adafruit/Raspberry_Pi_2/pi_2_mmio.c adafruit/Raspberry_Pi_2/pi_2_dht_read.c testAdafruit.c -o testAdafruit

30
src/main/c/testAdafruit.c Normal file
View File

@@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include "adafruit/Raspberry_Pi_2/pi_2_dht_read.h"
int main( void )
{
printf( "Raspberry Pi 2 DHT11 test program\n" );
int i;
int res;
float humidity = 0, temperature = 0;
int sensor = 11;
int pin = 4;
for (i = 1; i < 15; ++i){
res = pi_2_dht_read(sensor, pin, &humidity, &temperature);
if(res == 0){
printf("Temperature : %f\n", temperature);
printf("Humidity : %f\n", humidity);
}
else{
printf("Error when reading from sensor : %d\n", res);
}
sleep(1);
}
return 0;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.lengrand.therobottle;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import io.helidon.common.CollectionsHelper;
/**
* Simple Application that produces a greeting message.
*/
@ApplicationScoped
@ApplicationPath("/")
public class GreetApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
return CollectionsHelper.setOf(GreetResource.class);
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.lengrand.therobottle;
import java.util.Collections;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.json.Json;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* A simple JAX-RS resource to greet you. Examples:
*
* Get default greeting message:
* curl -X GET http://localhost:8080/greet
*
* Get greeting message for Joe:
* curl -X GET http://localhost:8080/greet/Joe
*
* Change greeting
* curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Howdy"}' http://localhost:8080/greet/greeting
*
* The message is returned as a JSON object.
*/
@Path("/greet")
@RequestScoped
public class GreetResource {
private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
/**
* The greeting message provider.
*/
private final GreetingProvider greetingProvider;
/**
* Using constructor injection to get a configuration property.
* By default this gets the value from META-INF/microprofile-config
*
* @param greetingConfig the configured greeting message
*/
@Inject
public GreetResource(GreetingProvider greetingConfig) {
this.greetingProvider = greetingConfig;
}
/**
* Return a wordly greeting message.
*
* @return {@link JsonObject}
*/
@SuppressWarnings("checkstyle:designforextension")
@GET
@Produces(MediaType.APPLICATION_JSON)
public JsonObject getDefaultMessage() {
return createResponse("World");
}
/**
* Return a greeting message using the name that was provided.
*
* @param name the name to greet
* @return {@link JsonObject}
*/
@SuppressWarnings("checkstyle:designforextension")
@Path("/{name}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public JsonObject getMessage(@PathParam("name") String name) {
return createResponse(name);
}
/**
* Set the greeting to use in future messages.
*
* @param jsonObject JSON containing the new greeting
* @return {@link Response}
*/
@SuppressWarnings("checkstyle:designforextension")
@Path("/greeting")
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateGreeting(JsonObject jsonObject) {
if (!jsonObject.containsKey("greeting")) {
JsonObject entity = JSON.createObjectBuilder()
.add("error", "No greeting provided")
.build();
return Response.status(Response.Status.BAD_REQUEST).entity(entity).build();
}
String newGreeting = jsonObject.getString("greeting");
greetingProvider.setMessage(newGreeting);
return Response.status(Response.Status.NO_CONTENT).build();
}
private JsonObject createResponse(String who) {
String msg = String.format("%s %s!", greetingProvider.getMessage(), who);
return JSON.createObjectBuilder()
.add("message", msg)
.build();
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.lengrand.therobottle;
import java.util.concurrent.atomic.AtomicReference;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
/**
* Provider for greeting message.
*/
@ApplicationScoped
public class GreetingProvider {
private final AtomicReference<String> message = new AtomicReference<>();
/**
* Create a new greeting provider, reading the message from configuration.
*
* @param message greeting to use
*/
@Inject
public GreetingProvider(@ConfigProperty(name = "app.greeting") String message) {
this.message.set(message);
}
String getMessage() {
return message.get();
}
void setMessage(String message) {
this.message.set(message);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.lengrand.therobottle;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import io.helidon.microprofile.server.Server;
/**
* The application main class.
*/
public final class Main {
/**
* Cannot be instantiated.
*/
private Main() { }
/**
* Application main entry point.
* @param args command line arguments
* @throws IOException if there are problems reading logging properties
*/
public static void main(final String[] args) throws IOException {
// load logging configuration
setupLogging();
// start the server
Server server = startServer();
System.out.println("http://localhost:" + server.port() + "/greet");
}
/**
* Start the server.
* @return the created {@link Server} instance
*/
static Server startServer() {
// Server will automatically pick up configuration from
// microprofile-config.properties
// and Application classes annotated as @ApplicationScoped
return Server.create().start();
}
/**
* Configure logging from logging.properties file.
*/
private static void setupLogging() throws IOException {
try (InputStream is = Main.class.getResourceAsStream("/logging.properties")) {
LogManager.getLogManager().readConfiguration(is);
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.lengrand.therobottle;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import io.helidon.microprofile.server.RoutingPath;
import io.helidon.security.SecurityContext;
import io.helidon.webserver.Routing;
import io.helidon.webserver.Service;
/**
* TODO javadoc.
*/
@ApplicationScoped
@RoutingPath("/reactive")
public class ReactiveService implements Service {
@Inject
private SecurityContext securityContext;
@Override
public void update(Routing.Rules rules) {
rules.get("/", (req, res) -> res.send("Context: " + securityContext));
}
}

View File

@@ -0,0 +1,27 @@
package nl.lengrand.therobottle;
public class Robottle {
// public static void main(String[] args) throws InterruptedException, ExecutionException {
// Dht11Driver theDriver = new Dht11Driver();
// theDriver.sayHello();
//
// FaunaApi api = new FaunaApi();
// Thread.sleep(1000);
//
//// Let's see if it runs until tomorrow!
// while(true){
// float [] res = theDriver.getTemperatureAndHumidity();
// if(res[0] != 0 && res[1] != 0){
// System.out.println("Sending " + res[0] + " " + res[1]);
// api.add(res);
// api.update(res);
// }
// else{
// System.out.println("Incorrect data!" + res[0] + " " + res[1]);
// }
// Thread.sleep(10000);
// }
// }
}

View File

@@ -0,0 +1,12 @@
package nl.lengrand.therobottle.driver;
public class Dht11Driver {
static {
System.loadLibrary("dht11");
}
public native void sayHello();
public native float[] getTemperatureAndHumidity();
}

View File

@@ -0,0 +1,65 @@
package nl.lengrand.therobottle.fauna;
import com.faunadb.client.FaunaClient;
import com.faunadb.client.query.Language;
import com.faunadb.client.types.Value;
import java.util.concurrent.ExecutionException;
import static com.faunadb.client.query.Language.*;
public class Connection {
public static final String API_KEY_NAME = "ROBOTTLE_KEY";
public final static String DB_NAME = "robottle";
public final static String COLLECTION_NAME = "sensors";
public final static String UPDATE_DEVICE_NAME = "robottle_00";
public final static String INDEX_NAME = "sensor_data";
public final static String INDEX_ALL_NAME = "all_sensors";
private FaunaClient client;
public Connection(){
}
private boolean isInit(){
return client != null;
}
public void init(){
try {
this.client = createConnection();
} catch (ExecutionException | InterruptedException | ConnectionException e) {
System.out.println("Problem while initializing connection with FaunaDb");
e.printStackTrace();
}
}
private String getKey() {
return System.getenv(API_KEY_NAME);
}
private String getDbKey(FaunaClient adminClient) throws InterruptedException, ExecutionException {
Value keyResults = adminClient.query(
CreateKey(Obj("database", Database(Language.Value(DB_NAME)), "role", Language.Value("server")))
).get();
return keyResults.at("secret").to(String.class).get();
}
public FaunaClient createConnection() throws ExecutionException, InterruptedException, ConnectionException {
FaunaClient adminClient = this.createAdminConnection();
return adminClient.newSessionClient(getDbKey(adminClient));
}
public FaunaClient createAdminConnection() throws ConnectionException {
if(getKey() == null) throw new ConnectionException("Local API key not found " + API_KEY_NAME );
return FaunaClient.builder()
.withSecret(getKey())
.build();
}
public FaunaClient getClient(){
return client;
}
}

View File

@@ -0,0 +1,7 @@
package nl.lengrand.therobottle.fauna;
public class ConnectionException extends Exception {
public ConnectionException(String errorMessage) {
super(errorMessage);
}
}

View File

@@ -0,0 +1,47 @@
package nl.lengrand.therobottle.fauna;
import com.faunadb.client.query.Language;
import com.faunadb.client.types.Value;
import java.util.concurrent.ExecutionException;
import static com.faunadb.client.query.Language.*;
import static fauna.Connection.*;
public class FaunaApi {
private Connection connection;
public FaunaApi(){
this.connection = new Connection();
connection.init();
}
public void add(float[] res) throws ExecutionException, InterruptedException {
Value addDataResult = connection.getClient().query(
Create(
Collection(Language.Value(COLLECTION_NAME)),
Obj("data",
Obj( "temperature", Language.Value(res[0]), "humidity", Language.Value(res[1]) )
)
)
).get();
System.out.println("Added sensor data to collection " + COLLECTION_NAME + ":\n " + addDataResult + "\n");
}
public void update(float[] res) throws ExecutionException, InterruptedException {
Value updateDataResult =
connection.getClient().query(
Update(
Select(Language.Value("ref"), Get(Match(Index(INDEX_NAME), Language.Value(UPDATE_DEVICE_NAME)))),
Obj("data",
Obj(
"temperature", Language.Value(res[0]),
"humidity", Language.Value(res[1])
)
)
)
).get();
System.out.println("Updated sensor data from collection " + COLLECTION_NAME + ":\n " + updateDataResult + "\n");
}
}

View File

@@ -0,0 +1,107 @@
package nl.lengrand.therobottle.fauna.setup;
import com.faunadb.client.FaunaClient;
import com.faunadb.client.types.Value;
import fauna.Connection;
import fauna.ConnectionException;
import java.util.concurrent.ExecutionException;
import static com.faunadb.client.query.Language.*;
import static fauna.Connection.*;
/*
Used to create the database and indexes. Should be run only once!
*/
public class FaunaDbSetup {
public static void main(String[] args) throws ExecutionException, InterruptedException, ConnectionException {
System.out.println("Setting up Database " + DB_NAME);
Connection connection = new Connection();
FaunaClient adminClient = connection.createAdminConnection();
createDatabase(adminClient);
Thread.sleep(1000);
FaunaClient dbClient = connection.createConnection();
createCollection(dbClient);
Thread.sleep(1000);
createIndex(dbClient);
Thread.sleep(1000);
createAllIndex(dbClient);
Thread.sleep(1000);
createInitialValue(dbClient);
adminClient.close();
System.out.println("Client closed");
}
private static String getDbKey(FaunaClient adminClient) throws InterruptedException, ExecutionException {
Value keyResults = adminClient.query(
CreateKey(Obj("database", Database(Language.Value(DB_NAME)), "role", Language.Value("server")))
).get();
return keyResults.at("secret").to(String.class).get();
}
private static void createIndex(FaunaClient dbClient) throws InterruptedException, ExecutionException {
Value indexResults = dbClient.query(
CreateIndex(
Obj("name", Language.Value(INDEX_NAME),
"source", Collection(Language.Value(COLLECTION_NAME)),
"unique", Language.Value(true),
"terms", Arr(Obj("field", Arr(Language.Value("data"), Language.Value("name"))))
)
)
).get();
System.out.println("Create name index for " + DB_NAME + ":\n " + indexResults + "\n");
}
private static void createAllIndex(FaunaClient dbClient) throws InterruptedException, ExecutionException {
Value indexResults = dbClient.query(
CreateIndex(
Obj("name", Language.Value(INDEX_ALL_NAME),
"source", Collection(Language.Value(COLLECTION_NAME))
)
)
).get();
System.out.println("Create all index for " + DB_NAME + ":\n " + indexResults + "\n");
}
private static void createCollection(FaunaClient dbClient) throws InterruptedException, ExecutionException {
Value collectionResults = dbClient.query(
CreateCollection(
Obj("name", Language.Value(COLLECTION_NAME), "history_days", null)
)
).get();
System.out.println("Create Collection for " + DB_NAME + ":\n " + collectionResults + "\n");
}
private static void createDatabase(FaunaClient adminClient) {
adminClient.query(
CreateDatabase(
Obj("name", Language.Value(DB_NAME))
)
);
System.out.println("Db created");
}
public static void createInitialValue(FaunaClient client) throws ExecutionException, InterruptedException {
Value init = client.query(
Create(
Collection(Language.Value(COLLECTION_NAME)),
Obj("data",
Obj("name", Language.Value(UPDATE_DEVICE_NAME))
)
)
).get();
System.out.println("Added initial item to collection " + COLLECTION_NAME + ":\n " + init + "\n");
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Quickstart MicroProfile example.
*/
package nl.lengrand.therobottle;

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
version="2.0"
bean-discovery-mode="annotated">
</beans>

View File

@@ -0,0 +1,22 @@
#
# Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Application properties. This is the default greeting
app.greeting=Hello
# Microprofile server properties
server.port=8080
server.host=0.0.0.0

View File

@@ -0,0 +1,37 @@
#
# Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Example Logging Configuration File
# For more information see $JAVA_HOME/jre/lib/logging.properties
# Send messages to the console
handlers=io.helidon.common.HelidonConsoleHandler
# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread
java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n
# Global logging level. Can be overridden by specific loggers
.level=INFO
# Component specific log levels
#io.helidon.webserver.level=INFO
#io.helidon.config.level=INFO
#io.helidon.security.level=INFO
#io.helidon.microprofile.level=INFO
#io.helidon.common.level=INFO
#io.netty.level=INFO
#org.glassfish.jersey.level=INFO
#org.jboss.weld=INFO

View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.lengrand.therobottle;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.spi.CDI;
import javax.json.JsonObject;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.helidon.microprofile.server.Server;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
class MainTest {
private static Server server;
@BeforeAll
public static void startTheServer() throws Exception {
server = Main.startServer();
}
@Test
void testHelloWorld() {
Client client = ClientBuilder.newClient();
JsonObject jsonObject = client
.target(getConnectionString("/greet"))
.request()
.get(JsonObject.class);
Assertions.assertEquals("Hello World!", jsonObject.getString("message"),
"default message");
jsonObject = client
.target(getConnectionString("/greet/Joe"))
.request()
.get(JsonObject.class);
Assertions.assertEquals("Hello Joe!", jsonObject.getString("message"),
"hello Joe message");
Response r = client
.target(getConnectionString("/greet/greeting"))
.request()
.put(Entity.entity("{\"greeting\" : \"Hola\"}", MediaType.APPLICATION_JSON));
Assertions.assertEquals(204, r.getStatus(), "PUT status code");
jsonObject = client
.target(getConnectionString("/greet/Jose"))
.request()
.get(JsonObject.class);
Assertions.assertEquals("Hola Jose!", jsonObject.getString("message"),
"hola Jose message");
r = client
.target(getConnectionString("/metrics"))
.request()
.get();
Assertions.assertEquals(200, r.getStatus(), "GET metrics status code");
r = client
.target(getConnectionString("/health"))
.request()
.get();
Assertions.assertEquals(200, r.getStatus(), "GET health status code");
}
@AfterAll
static void destroyClass() {
CDI<Object> current = CDI.current();
((SeContainer) current).close();
}
private String getConnectionString(String path) {
return "http://localhost:" + server.port() + path;
}
}

View File

@@ -0,0 +1,22 @@
#
# Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Override configuration to use a random port for the unit tests
config_ordinal=1000
# Microprofile server properties
server.port=-1
server.host=0.0.0.0