def test_complex_gate_optimization(complex_gate, complex_gate_opt): """Make sure the optimized version runs identically to the unoptimized.""" sg, sg_opt = complex_gate, complex_gate_opt sim1 = SensorGraphSimulator(sg) sim1.stop_condition("run_time 10 minutes") sg.load_constants() sim1.record_trace() sim1.run() sim2 = SensorGraphSimulator(sg_opt) sim2.stop_condition("run_time 10 minutes") sg_opt.load_constants() sim2.record_trace() sim2.run() assert len(sim1.trace) == 0 assert len(sim2.trace) == 0 sim1.step(DataStream.FromString("system input 1034"), 1) sim2.step(DataStream.FromString("system input 1034"), 1) sim1.run() sim2.run() print("Unoptimized Output") for x in sim1.trace: print("%08d %s: %d" % (x.raw_time, DataStream.FromEncoded(x.stream), x.value)) print("\nOptimized Output") for x in sim2.trace: print("%08d %s: %d" % (x.raw_time, DataStream.FromEncoded(x.stream), x.value)) assert len(sim1.trace) == 4 assert len(sim2.trace) == 4 assert sim1.trace == sim2.trace #Check that number of trigger streamer commands is same for optimized and unoptimized trigger_nodes = [ node for node in complex_gate.nodes if node.func_name == 'trigger_streamer' ] trigger_nodes_opt = [ node for node in complex_gate_opt.nodes if node.func_name == 'trigger_streamer' ] assert len(trigger_nodes) == len(trigger_nodes_opt)
def inspect_virtual(self, stream_id): """Inspect the last value written into a virtual stream. Args: stream_id (int): The virtual stream was want to inspect. Returns: (int, int): An error code and the stream value. """ stream = DataStream.FromEncoded(stream_id) if stream.buffered: return [ pack_error(ControllerSubsystem.SENSOR_LOG, SensorLogError.VIRTUAL_STREAM_NOT_FOUND), 0 ] try: reading = self.storage.inspect_last(stream, only_allocated=True) return [Error.NO_ERROR, reading.value] except StreamEmptyError: return [Error.NO_ERROR, 0] except UnresolvedIdentifierError: return [ pack_error(ControllerSubsystem.SENSOR_LOG, SensorLogError.VIRTUAL_STREAM_NOT_FOUND), 0 ]
def stream_name(stream_id): """Map a stream id to a human readable name. The mapping process is as follows: If the stream id is globally known, its global name is used as <name> otherwise a string representation of the stream is used as <name>. In both cases the hex representation of the stream id is appended as a number: <name> (0x<stream id in hex>) Args: stream_id (int): An integer stream id. Returns: str: The nice name of the stream. """ name = _STREAM_NAME_MAP.get(stream_id) if name is None: name = str(DataStream.FromEncoded(stream_id)) return "{} (0x{:04X})".format(name, stream_id)
def FromBinary(cls, record_data, record_count=1): """Create an UpdateRecord subclass from binary record data. This should be called with a binary record blob (NOT including the record type header) and it will decode it into a SetConstantRecord. Args: record_data (bytearray): The raw record data that we wish to parse into an UpdateRecord subclass NOT including its 8 byte record header. record_count (int): The number of records included in record_data. Raises: ArgumentError: If the record_data is malformed and cannot be parsed. Returns: SetConstantRecord: The decoded reflash tile record. """ _cmd, address, _resp_length, payload = cls._parse_rpc_info(record_data) try: value, encoded_stream = struct.unpack("<LH", payload) stream = DataStream.FromEncoded(encoded_stream) except ValueError: raise ArgumentError("Could not parse set_constant payload", payload=payload) return SetConstantRecord(stream, value, address=address)
def count_matching(self, selector, offset=0): """Count the number of readings matching selector. Args: selector (DataStreamSelector): The selector that we want to count matching readings for. offset (int): The starting offset that we should begin counting at. Returns: int: The number of matching readings. """ if selector.output: data = self.streaming_data elif selector.buffered: data = self.storage_data else: raise ArgumentError( "You can only pass a buffered selector to count_matching", selector=selector) count = 0 for i in range(offset, len(data)): reading = data[i] stream = DataStream.FromEncoded(reading.stream) if selector.matches(stream): count += 1 return count
def test_user_tick_optimization(usertick_gate, usertick_gate_opt): """Make sure the optimized version runs identically to the unoptimized.""" sg, sg_opt = usertick_gate, usertick_gate_opt for node in sg_opt.nodes: print(node) sim1 = SensorGraphSimulator(sg) sim1.stop_condition("run_time 10 minutes") sg.load_constants() sim1.record_trace() sim1.run() sim2 = SensorGraphSimulator(sg_opt) sim2.stop_condition("run_time 10 minutes") sg_opt.load_constants() sim2.record_trace() sim2.run() assert len(sim1.trace) == 0 assert len(sim2.trace) == 0 sim1.step(DataStream.FromString("system input 1034"), 1) sim2.step(DataStream.FromString("system input 1034"), 1) sim1.run() sim2.run() print("Unoptimized Output") for x in sim1.trace: print("%08d %s: %d" % (x.raw_time, DataStream.FromEncoded(x.stream), x.value)) print("\nOptimized Output") for x in sim2.trace: print("%08d %s: %d" % (x.raw_time, DataStream.FromEncoded(x.stream), x.value)) assert len(sim1.trace) == 4 assert len(sim2.trace) == 4 assert sim1.trace == sim2.trace
def process_input(self, encoded_stream, value): """Process or drop a graph input. This must not be called directly from an RPC but always via a deferred task. """ if not self.enabled: return stream = DataStream.FromEncoded(encoded_stream) reading = IOTileReading(self.get_timestamp(), encoded_stream, value) self.graph.process_input(stream, reading, None) #FIXME: add in an rpc executor for this device. self.process_streamers()
def push(self, value): """Store a new value for the given stream. Args: value (IOTileReading): The value to store. The stream parameter must have the correct value """ stream = DataStream.FromEncoded(value.stream) if stream.stream_type == DataStream.OutputType: if len(self.streaming_data) == self.streaming_length: raise StorageFullError('Streaming buffer full') self.streaming_data.append(value) else: if len(self.storage_data) == self.storage_length: raise StorageFullError('Storage buffer full') self.storage_data.append(value)
def push(self, stream_id, timestamp, value): """Push a value to a stream. Args: stream_id (int): The stream we want to push to. timestamp (int): The raw timestamp of the value we want to store. value (int): The 32-bit integer value we want to push. Returns: int: Packed 32-bit error code. """ stream = DataStream.FromEncoded(stream_id) reading = IOTileReading(stream_id, timestamp, value) try: self.storage.push(stream, reading) return Error.NO_ERROR except StorageFullError: return pack_error(ControllerSubsystem.SENSOR_LOG, SensorLogError.RING_BUFFER_FULL)
def process_input(self, encoded_stream, value): """Process or drop a graph input. This method asynchronously queued an item to be processed by the sensorgraph worker task in _reset_vector. It must be called from inside the emulation loop and returns immediately before the input is processed. """ if not self.enabled: return if isinstance(encoded_stream, str): stream = DataStream.FromString(encoded_stream) encoded_stream = stream.encode() elif isinstance(encoded_stream, DataStream): stream = encoded_stream encoded_stream = stream.encode() else: stream = DataStream.FromEncoded(encoded_stream) reading = IOTileReading(self.get_timestamp(), encoded_stream, value) self._inputs.put_nowait((stream, reading))