Skip to content

Commit

Permalink
Finalized config for the sensor.
Browse files Browse the repository at this point in the history
  • Loading branch information
craig8 committed Oct 2, 2019
1 parent 65bd781 commit 0726409
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 71 deletions.
24 changes: 10 additions & 14 deletions sensor_simulator.config
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
"environmentVariables": [],
"user_input": {
"sensors-config": {
"help": "Enter dictionary of mrid :qto configuration mapping for the sensors",
"help": "Enter dictionary of sensor mrid configuration mapping for the sensors. The example is for the 123 node system",
"help_example": {
"_001cc221-d6e6-485d-bdcc-b84cb643d1ec": {
"nominal-voltage": 100,
"perunit-confidence": 0.01,
"perunit-confidence-rate": 0.95,
"aggregation-interval": 30,
"perunit-drop-rate": 0.01
},
Expand All @@ -30,22 +30,24 @@
},
"default-perunit-confidence-rate": {
"help": "Set the default confidence rate of the equipment",
"help_example": 0.01,
"help_example": 0.95,
"type": "float",
"max_value": 0.1,
"min_value": 0.0,
"default_value": 0.01
"max_value": 1,
"min_value": 0.90,
"default_value": 0.95
},
"default-aggregation-interval": {
"help": "Aggregation period in seconds",
"help_example": 0.01,
"help_example": 30,
"type": "float",
"default_value": 30
},
"default-perunit-drop-rate": {
"help": "Sets the default perunit drop rate for the equipment",
"help_example": 0.01,
"default_value": 0.01,
"default_value": 0.05,
"max_value": 0.10,
"min_value": 0.0,
"type": "float"
},
"passthrough-if-not-specified": {
Expand All @@ -59,12 +61,6 @@
"help_example": 500,
"default_value": 0,
"type": "int"
},
"log-statistics": {
"help": "Writes a log of statistics to the service container",
"help_example": true,
"default_value": true,
"type": "bool"
}
}
}
142 changes: 85 additions & 57 deletions sensors/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,63 @@


class Sensors(object):
def __init__(self, gridappsd, read_topic, write_topic, log_statistics=False, user_options=None):
""" initializes sensor objects
def __init__(self, gridappsd, read_topic, write_topic, user_options: dict = None):
"""
Create sensors based upon thee user_options dictionary
The user_options dictionary will have the following structure:
{
"sensors-config": {
"_001cc221-d6e6-485d-bdcc-b84cb643d1ec": {
"nominal-voltage": 100,
"perunit-confidence-rate": 0.01,
"aggregation-interval": 30,
"perunit-drop-rate": 0.01
},
"_0031ff7c-5140-47cf-b750-0146bb3d9024": {},
"_00313f7c-5140-47cf-b750-0146bb3d9024": {
"nominal-voltage": 35
},
"default-perunit-confidence-rate": 0.01,
"default-aggregation-interval": 30,
"default-perunit-drop-rate": 0.01,
"passthrough-if-not-specified": true,
"random-seed": 0,
"log-statistics": false
}
}
sensor_config should be a dictionary of dictionaries to handle
the specification of different parameters for individual sensors
sensor-config - contains a dictionary of sensors the service will enhance with noise and/or
drop from existence based upon parameters.
Each value of the dictionary should have the following attributes specified. If
no value is supplied then the defaults listed in DEFAULT_SENSOR_CONFIG will be used
For each sensor one can specify one or more of the following properties:
- nominal-voltage: The nominal voltage of the sensor
- aggregation-interval: Number of seconds to aggregate for the sensor
- perunit-dropping:
- perunit-confidence:
nominal-voltage - Normal voltage level for the sensor (note this will become
automated when querying for this from blazegraph is
implemented)
perunit-confidence-rate - Confidence level that the mean value is within this range
aggregation-interval - Number of samples to collect before emitting a measurement
perunit-drop-rate - Rate to drop the measurement value
Example sensor_config:
random-seed - A seed to produce reliable results over different runs of the code base
passthrough-if-not-specified - Allows measurements of non-specified sensors to be published to the
sensors output topic without modification.
{
"61A547FB-9F68-5635-BB4C-F7F537FD824E": {
"nominal-voltage": 100,
"perunit-confidence": 0.01
"aggregation-interval": 30,
"perunit-dropping": 0.01
}
...
}
The following values are used as defaults for each sensor listed in sensor-config but does not specify
the value for the parameter
default-perunit-confidence-rate
default-aggregation-interval
default-perunit-drop-rate
:param read_topic:
The topic to listen for measurement data to come through the bus
:param write_topic
The topic to write the measurement data to
:param log_statistics
Boolean: Log statistics to the gridappsd log
:param gridappsd:
The main object used to connect to gridappsd
:param random_seed:
A random seed to be used for reproducible results.
:param sensor_config:
Configuration of the sensors that should have noise to them.
:param user_options:
A dictionary of options to specify how the service will run.
"""
super(Sensors, self).__init__()
if user_options is None:
Expand All @@ -65,7 +84,7 @@ def __init__(self, gridappsd, read_topic, write_topic, log_statistics=False, use
self._logger = self._gappsd.get_logger()
self._read_topic = read_topic
self._write_topic = write_topic
self._log_statistics = log_statistics
self._log_statistics = False

assert self._gappsd, "Invalid gridappsd object specified, cannot be None"
assert self._read_topic, "Invalid read topic specified, cannot be None"
Expand All @@ -74,7 +93,8 @@ def __init__(self, gridappsd, read_topic, write_topic, log_statistics=False, use
sensors_config = user_options.pop("sensors-config", {})
self.passthrough_if_not_specified = user_options.pop('passthrough-if-not-specified', True)
self.default_perunit_confifidence_rate = user_options.get('default-perunit-confidence-rate',
DEFAULT_SENSOR_CONFIG['default-perunit-confidence-rate'])
DEFAULT_SENSOR_CONFIG[
'default-perunit-confidence-rate'])
self.default_drop_rate = user_options.get("default-perunit-drop-rate",
DEFAULT_SENSOR_CONFIG['default-perunit-drop-rate'])
self.default_aggregation_interval = user_options.get("default-aggregation-interval",
Expand All @@ -83,24 +103,26 @@ def __init__(self, gridappsd, read_topic, write_topic, log_statistics=False, use
DEFAULT_SENSOR_CONFIG['default-nominal-voltage'])
for k, v in sensors_config.items():
agg_interval = v.get("aggregation-interval", self.default_aggregation_interval)
perunit_drop_rate = v.get("drop-rate", self.default_drop_rate)
perunit_confidence_rate = v.get('per-unit-confidence-rate', self.default_perunit_confifidence_rate)
perunit_drop_rate = v.get("perunit-drop-rate", self.default_drop_rate)
perunit_confidence_rate = v.get('perunit-confidence-rate', self.default_perunit_confifidence_rate)
nominal_voltage = v.get('nominal-voltage', self.default_nominal_voltage)
self._sensors[k] = Sensor(self._random_seed, nominal_voltage=100, aggregation_interval=agg_interval,
self._sensors[k] = Sensor(self._random_seed, nominal_voltage=nominal_voltage,
aggregation_interval=agg_interval,
perunit_drop_rate=perunit_drop_rate,
perunit_confidence_rate=perunit_confidence_rate)

_log.debug("Created {} sensors".format(len(self._sensors)))

def on_simulation_message(self, headers, message):
""" Listens for simulation messages off the gridappsd message bus
"""
Listen for simulation measurement messages off the gridappsd message bus.
If the message is a measurements message then the function will inspect each
non string value and generate a new value for the passed data.
Each measurement is mapped onto a `Sensor` which determines whether or not the
measurement is published to the sensor output topic or dropped.
:param headers:
:param message:
:return:
Simulation measurement message.
"""

configured_sensors = set(self._sensors.keys())
Expand All @@ -112,7 +134,7 @@ def on_simulation_message(self, headers, message):
if self.passthrough_if_not_specified:
measurement_out = deepcopy(message['message']['measurements'])

timestep = message['message']['timestamp']
timestamp = message['message']['timestamp']

# Loop over the configured sensor andding measurements for each of them
for mrid in configured_sensors:
Expand All @@ -129,16 +151,23 @@ def on_simulation_message(self, headers, message):
if prop in ('measurement_mrid', 'angle'):
new_measurement[prop] = value
continue
new_value = self._sensors[mrid].get_new_value(timestep, value)

new_value = self._sensors[mrid].get_new_value(timestamp, value)
if new_value is None:
new_measurement = None
break

new_measurement[prop] = new_value

measurement_out[mrid] = new_measurement
if new_measurement is not None:
measurement_out[mrid] = new_measurement

if len(measurement_out) > 0:
message['message']['measurements'] = measurement_out
if self._log_statistics:
self._log_sensors()

message['message']['measurements'] = measurement_out
_log.debug(f"Sending to: {self._write_topic}\nmessage: {message}")
if self._log_statistics:
self._log_sensors()
self._gappsd.send(self._write_topic, json.dumps(message))
self._gappsd.send(self._write_topic, json.dumps(message))

def _log_sensors(self):
for s in self._sensors:
Expand All @@ -153,21 +182,20 @@ def main_loop(self):


class Sensor(object):
def __init__(self, seed, nominal_voltage, aggregation_interval, perunit_drop_rate, perunit_confidence_rate):
def __init__(self, random_seed, nominal_voltage, aggregation_interval, perunit_drop_rate, perunit_confidence_rate):
"""
An object modeling an individual sensor.
:param gridappsd:
:param seed:
:param nominal:
:param perunit_dropping:
:param perunit_confidence:
:param interval:
:param input_topic:
:param random_seed:
:param nominal_voltage:
:param aggregation_interval:
:param perunit_drop_rate:
:param perunit_confidence_rate:
"""
self._nominal = nominal_voltage
self._perunit_dropping = perunit_drop_rate
self._stddev = nominal_voltage * perunit_confidence_rate / 1.96 # for normal distribution
self._seed = seed
self._seed = random_seed
self._interval = aggregation_interval
# Set default - Uninitialized values for internal properties.
self._n = 0
Expand All @@ -177,7 +205,7 @@ def __init__(self, seed, nominal_voltage, aggregation_interval, perunit_drop_rat
self._max = 0
self._initialized = False

random.seed(seed)
random.seed(random_seed)
_log.debug(self)

def __repr__(self):
Expand Down Expand Up @@ -221,8 +249,8 @@ def reset_interval(self, t, val):
self._n = 1
self._tstart = t
self._average = val
self._min = val # sys.float_info.max
self._max = val # -sys.float_info.max
self._min = val # sys.float_info.max
self._max = val # -sys.float_info.max

def add_sample(self, t, val):
if not self._initialized:
Expand All @@ -249,7 +277,7 @@ def take_range_sample(self, t):
if drop <= self.perunit_dropping:
self.reset_interval(t, mean_val)
return None, None, None
ret = (mean_val + random.gauss(0.0, self._stddev), # TODO (Tom, Andy, Andy): do we want the same error on each?
ret = (mean_val + random.gauss(0.0, self._stddev), # TODO (Tom, Andy, Andy): do we want the same error on each?
self._min + random.gauss(0.0, self._stddev),
self._max + random.gauss(0.0, self._stddev))
self.reset_interval(t, mean_val)
Expand All @@ -270,7 +298,7 @@ def take_inst_sample(self, t):

def get_new_value(self, t, value):
self.add_sample(t, value)
if self.ready_to_sample (t):
if self.ready_to_sample(t):
return self.take_inst_sample(t)
return None

Expand Down

0 comments on commit 0726409

Please sign in to comment.