Skip to content

Commit

Permalink
Merge pull request #18 from scaleoutsoftware/br-dev
Browse files Browse the repository at this point in the history
Br dev
  • Loading branch information
ripleyb authored May 2, 2024
2 parents 4065bc6 + 34b74fc commit 055e2f3
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 99 deletions.
2 changes: 1 addition & 1 deletion Core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group 'com.scaleoutsoftware.digitaltwin'
version '3.0.6'
version '3.0.7'

sourceCompatibility = JavaVersion.VERSION_12

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.scaleoutsoftware.digitaltwin.core;

import java.time.Duration;
import java.util.Date;

/**
* The SimulationController interface is used to interact with the running DigitalTwin simulation.
Expand Down Expand Up @@ -64,6 +65,20 @@ public interface SimulationController {
*/
SendingResult delay(Duration duration);

/**
* <p>
* Delay simulation processing for this DigitalTwin instance, indefinitely.
* </p>
*
* <p>
* Simulation processing will be delayed until this instance is run with {@link SimulationController#runThisTwin()}.
* </p>
*
* @return {@link SendingResult#Handled} if the delay was processed or {@link SendingResult#NotHandled}
* if the delay was not processed.
*/
SendingResult delayIndefinitely();

/**
* <p>
* Asynchronously send a JSON serialized message to a DigitalTwin instance that will be processed by the DigitalTwin
Expand Down Expand Up @@ -144,9 +159,20 @@ public interface SimulationController {
*/
SendingResult deleteThisInstance();

/**
* Run this instance during this simulation step. The instance will be run using the models {@link SimulationProcessor#processModel(ProcessingContext, DigitalTwinBase, Date)}
* implementation.
*
* This will cause the simulation sub-system to run this instance regardless of the instances current
* {@link DigitalTwinBase#NextSimulationTime}.
*/
void runThisInstance();

/**
* Stop the simulation.
* @return a {@link SimulationStatus#InstanceRequestedStop}.
*/
SimulationStatus stopSimulation();


}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,83 @@

class Constants {
public static final int MAX_TIMER_COUNT = 5;
/**
*
* Returns a hash that is suitable for inserting an object into a hash-based collection.
* <p>
* -----------------------------------------------------------------------------
* MurmurHash3 was written by Austin Appleby, and is placed in the public
* domain. The author hereby disclaims copyright to this source code.
*
* Note - The x86 and x64 versions do _not_ produce the same results, as the
* algorithms are optimized for their respective platforms. You can still
* compile and run any of them on any platform, but your performance with the
* non-native version will be less than optimal.
* Original code from:
* https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
* -----------------------------------------------------------------------------
*
* This implementation is tweaked to initialize with a hard-coded seed and to return a long instead of a
* 32-bit unsigned integer (since Java doesn't easily support unsigned integers).
* @param data The byte array to be hashed.
* @return Hash code for the array, with values ranging from 0 to 4,294,967,295.
*/
static long getHash(byte[] data) {
if(data == null) {
throw new IllegalArgumentException("Hash data was null.");
}

final int seed = 947203; // Scaleout's implementation-specific seed.
final int c1 = 0xcc9e2d51;
final int c2 = 0x1b873593;

int len = data.length;
int h1 = seed;
int roundedEnd = len & 0xfffffffc; // round down to 4 byte block

for (int i = 0; i < roundedEnd; i += 4) {
// little endian load order
int k1 = (data[i] & 0xff) | ((data[i + 1] & 0xff) << 8) | ((data[i + 2] & 0xff) << 16) | (data[i + 3] << 24);
k1 *= c1;
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
k1 *= c2;

h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13);
h1 = h1 * 5 + 0xe6546b64;
}

// tail (leftover bytes that didn't fit into a 4-byte block)
int k1 = 0;

switch (len & 0x03) {
case 3:
k1 = (data[roundedEnd + 2] & 0xff) << 16;
// fallthrough
case 2:
k1 |= (data[roundedEnd + 1] & 0xff) << 8;
// fallthrough
case 1:
k1 |= (data[roundedEnd] & 0xff);
k1 *= c1;
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
}

// finalization
h1 ^= len;

// fmix(h1);
h1 ^= h1 >>> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >>> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >>> 16;

// Other languages want to represent the hash as an unsigned int, but java doesn't easily have
// unsigned types. So we move the signed integer's bits into an "unsigned" long, which
// is big enough to hold all the positive values of an unsigned int.
return h1 & 0x00000000ffffffffL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ String getId() {
return _id;
}

String getModel() {return _model;}

void setNextSimulationTime(long nextSimulationTime) {
_nextSimulationTime = nextSimulationTime;
handleResetNextSimulationTime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import com.scaleoutsoftware.digitaltwin.core.ProcessingContext;
import com.scaleoutsoftware.digitaltwin.core.SimulationProcessor;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Objects;

class SimulationEventTwinImpl extends SimulationEvent {
SimulationProcessor _processor;
Expand Down Expand Up @@ -63,4 +65,17 @@ void handleResetNextSimulationTime() {
base.NextSimulationTime = _nextSimulationTime;
_proxy.setInstance(base);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SimulationEventTwinImpl that = (SimulationEventTwinImpl) o;
return this._proxy.getInstance().getId().compareTo(that._id) == 0 && this._proxy.getInstance().getModel().compareTo(that._model) == 0;
}

@Override
public int hashCode() {
return (int)Constants.getHash(_id.getBytes(StandardCharsets.UTF_8));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public SimulationScheduler(String modelName,
_modelName = modelName;
_simulationProcessor = modelProcessor;
for(int i = 0; i < NUM_SIMULATION_WORKERS; i++) {
_workers.add(new SimulationWorker(i, _modelName, _simulationProcessor, digitalTwinClass, executor));
_workers.add(new SimulationWorker(i, _modelName, _simulationProcessor, digitalTwinClass, executor, this));
}
}

Expand Down Expand Up @@ -146,87 +146,14 @@ void stopTimer(String modelName, String id, String timerName) {
worker.stopTimer(modelName, id, timerName);
}

private int findSlotId(String id) {
return (int)((getHash(id.getBytes(StandardCharsets.UTF_8))) % (long)NUM_SIMULATION_WORKERS);
void runThisInstance(String model, String id) throws WorkbenchException {
SimulationWorker worker = _workers.get(findSlotId(id));
worker.runThisInstance(model, id);
}

/**
*
* Returns a hash that is suitable for inserting an object into a hash-based collection.
* <p>
* -----------------------------------------------------------------------------
* MurmurHash3 was written by Austin Appleby, and is placed in the public
* domain. The author hereby disclaims copyright to this source code.
*
* Note - The x86 and x64 versions do _not_ produce the same results, as the
* algorithms are optimized for their respective platforms. You can still
* compile and run any of them on any platform, but your performance with the
* non-native version will be less than optimal.
* Original code from:
* https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
* -----------------------------------------------------------------------------
*
* This implementation is tweaked to initialize with a hard-coded seed and to return a long instead of a
* 32-bit unsigned integer (since Java doesn't easily support unsigned integers).
* @param data The byte array to be hashed.
* @return Hash code for the array, with values ranging from 0 to 4,294,967,295.
*/
static long getHash(byte[] data) {
if(data == null) {
throw new IllegalArgumentException("Hash data was null.");
}

final int seed = 947203; // Scaleout's implementation-specific seed.
final int c1 = 0xcc9e2d51;
final int c2 = 0x1b873593;

int len = data.length;
int h1 = seed;
int roundedEnd = len & 0xfffffffc; // round down to 4 byte block

for (int i = 0; i < roundedEnd; i += 4) {
// little endian load order
int k1 = (data[i] & 0xff) | ((data[i + 1] & 0xff) << 8) | ((data[i + 2] & 0xff) << 16) | (data[i + 3] << 24);
k1 *= c1;
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
k1 *= c2;

h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13);
h1 = h1 * 5 + 0xe6546b64;
}

// tail (leftover bytes that didn't fit into a 4-byte block)
int k1 = 0;

switch (len & 0x03) {
case 3:
k1 = (data[roundedEnd + 2] & 0xff) << 16;
// fallthrough
case 2:
k1 |= (data[roundedEnd + 1] & 0xff) << 8;
// fallthrough
case 1:
k1 |= (data[roundedEnd] & 0xff);
k1 *= c1;
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
}

// finalization
h1 ^= len;
private int findSlotId(String id) {
return (int)((Constants.getHash(id.getBytes(StandardCharsets.UTF_8))) % (long)NUM_SIMULATION_WORKERS);
}

// fmix(h1);
h1 ^= h1 >>> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >>> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >>> 16;

// Other languages want to represent the hash as an unsigned int, but java doesn't easily have
// unsigned types. So we move the signed integer's bits into an "unsigned" long, which
// is big enough to hold all the positive values of an unsigned int.
return h1 & 0x00000000ffffffffL;
}
}
Loading

0 comments on commit 055e2f3

Please sign in to comment.