Skip to content

Commit

Permalink
Adding resources
Browse files Browse the repository at this point in the history
  • Loading branch information
Jesse Yates committed Sep 7, 2020
0 parents commit 5ccf7cf
Show file tree
Hide file tree
Showing 9 changed files with 519 additions and 0 deletions.
38 changes: 38 additions & 0 deletions event-generators/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Event Generators

Generate realistic device and pricing events, modeled on real-world events.

## Running

Show the help
```
java -jar target/event-generators-1.0-SNAPSHOT-jar-with-dependencies.jar -h
java -jar target/event-generators-1.0-SNAPSHOT-jar-with-dependencies.jar --help
```

Run the device event generator

```
java -jar target/event-generators-1.0-SNAPSHOT-jar-with-dependencies.jar \
events --target http://host:1234/my/path
````

Run the pricing generator

```
java -jar target/event-generators-1.0-SNAPSHOT-jar-with-dependencies.jar \
pricing --target http://host:1234/my/path
````

Event generators can also be run with an optional `--seed` flag, that takes a long, to set the
random seed used for generating events, enabling a reproducable stream of events.

See `--help` for more information.

## Building the generators

```
mvn clean package
```

Creates a `-jar-with-dependencies.jar` in the `target/` directory, which can then be run.
98 changes: 98 additions & 0 deletions event-generators/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>dist-grid</artifactId>
<groupId>com.jesseyates.manning</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>event-generators</artifactId>

<properties>
<jcommander.version>1.78</jcommander.version>
<okhttp.version>4.5.0</okhttp.version>
<jackson.version>2.7.2</jackson.version>
<commons-lang3.version>3.10</commons-lang3.version>
</properties>


<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.jesseyates.manning.Generator</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>${jcommander.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.jesseyates.manning;

import com.beust.jcommander.Parameter;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public abstract class BaseGenerator {

@Parameter(names = {"-t", "--target"}, description = "Destination to send the events",
required = true)
private String path;

@Parameter(names = {"--debug"}, description = "Enable debugging of requests and responses")
private boolean debug;

@Parameter(names = {"-w", "--wait-interval-ms"},
description = "Milliseconds to wait between making successive requests")
protected long waitMillis = 500;

protected final Random r = new Random(319009871);
protected List<Integer> regions = new ArrayList<>();

{
for (int i = 0; i < 1000; i++) {
regions.add(r.nextInt(10000));
}
}

public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");

protected static ObjectMapper MAPPER = new ObjectMapper();

protected OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.callTimeout(10, TimeUnit.SECONDS)
.build();


public void run() throws Exception {
while (true) {
nextEvent();
Thread.sleep(waitMillis);
}
}

protected abstract void nextEvent() throws Exception;

protected String post(Object body) throws Exception {
return post(body, Optional.empty());
}

protected String post(Object body, Optional<String> suffix) throws Exception {
return post(MAPPER.writeValueAsString(body), suffix);
}

protected String post(String json, Optional<String> suffix) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
String url = suffix.map(s -> path + "/" + s).orElse(path);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
if (debug) {
System.out.println("Sending: " + request + " : " + json);
}
try (Response response = client.newCall(request).execute()) {
if (debug) {
System.out.println("Got response: " + response);
}
return response.body().string();
}
}

protected int intInRange(int lower, int upper) {
return Ranges.intInRange(r, lower, upper);
}

protected float floatInRange(Float lower, Float upper) {
return Ranges.floatInRange(r, lower, upper);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.jesseyates.manning;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;


@Parameters(commandDescription = "Run the device event generator")
public class EventGenerator extends BaseGenerator {

@Parameter(names = {"-n", "--num-devices"}, description = "Number of unique devices")
protected int deviceCount = 1_000;

@Parameter(names = {"-e", "--max-events-per-request"},
description = "Maximum number of events per request")
protected int maxEvents = 100;

@Parameter(names = {"-b", "--bad-events"}, description = "Corrupt the events")
private boolean badEvents;
@Parameter(names = {"-m", "--missing-region"},
description = "Events are missing the region identifier")
private boolean missingRegion;

private static UUID randomUUID(Random r) {
// copy the UUID logic, but use our own random
byte[] data = new byte[16];
r.nextBytes(data);
data[6] &= 0x0f; /* clear version */
data[6] |= 0x40; /* set to version 4 */
data[8] &= 0x3f; /* clear variant */
data[8] |= 0x80; /* set to IETF variant */
long msb = 0;
long lsb = 0;
for (int i = 0; i < 8; i++)
msb = (msb << 8) | (data[i] & 0xff);
for (int i = 8; i < 16; i++)
lsb = (lsb << 8) | (data[i] & 0xff);
return new UUID(msb, lsb);
}

private final Map<UUID, Integer> deviceToRegion = new HashMap<>();

// setup devices and regions
{
for (int i = 0; i < deviceCount; i++) {
int region = regions.get(r.nextInt(regions.size()));
deviceToRegion.put(randomUUID(r), region);
}
}

private final Map<String, Pair<Object, Object>> events = new HashMap<>();

// setup event field values
{
events.put("charging", new ImmutablePair<>(-1000, 1000));
events.put("charging_source", new ImmutablePair<>("solar", "utility"));
events.put("current_capacity", new ImmutablePair<>(0, 13_000));
// other fields like a real device would send
events.put("moduleL_temp", new ImmutablePair<>(-5, 225));
events.put("moduleR_temp", new ImmutablePair<>(-5, 225));
events.put("processor1_temp", new ImmutablePair<>(-5, 225));
events.put("processor2_temp", new ImmutablePair<>(-5, 225));
events.put("processor3_temp", new ImmutablePair<>(-5, 225));
events.put("processor4_temp", new ImmutablePair<>(-5, 225));
events.put("inverter_state", new ImmutablePair<>(0, 15));
events.put("SoC_regulator", new ImmutablePair<>(26.0f, 29.6f));
}

@Override
public void nextEvent() throws Exception {
UUID[] ids = deviceToRegion.keySet().toArray(new UUID[0]);
UUID id = ids[r.nextInt(ids.length - 1)];

int count = Math.max(1, r.nextInt(maxEvents));
List<Object> events = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
Object event = getEvent(id);
if (badEvents && r.nextBoolean()) {
event = "garbage{\"/}" + MAPPER.writeValueAsString(event);
}
events.add(event);
}

// then we convert them to be one event per line
String message = events.stream().map(event -> {
try {
return MAPPER.writeValueAsString(event);
} catch (IOException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.joining("\n"));

post(message, Optional.of(id.toString()));
}

private Map<String, Object> getEvent(UUID id) {
Map<String, Object> event = new HashMap<>();
int region = deviceToRegion.get(id);
event.put("device_id", id.toString());
if (missingRegion && r.nextBoolean()) {
event.put("region", region);
}
for (Map.Entry<String, Pair<Object, Object>> entry : events.entrySet()) {
Pair<Object, Object> range = entry.getValue();
Object value = null;
if (range.getKey() instanceof Integer) {
value = intInRange((Integer) range.getKey(), (Integer) range.getValue());
} else if (range.getKey() instanceof Float) {
value = floatInRange((Float)range.getKey(), (Float) range.getValue());
} else if (range.getKey() instanceof String) {
value = r.nextBoolean() ? range.getKey() : range.getValue();
}
event.put(entry.getKey(), value);
}

return event;
}
}
Loading

0 comments on commit 5ccf7cf

Please sign in to comment.