class Orocos::Log::Replay
Class for loading and replaying pocolog (Rock) log files.
This class creates objects whose API is compatible with {Orocos::TaskContext} and {Orocos::OutputPort}, using the log data.
By default, all tasks that are present in the log files provided to {open} can be resolved using the orocos name service. If this behaviour is unwanted, call {#deregister_tasks} after {.open} or {#load} was called. To do it on a task-by-task basis, do the following after the call to {.open} or {#load}
replay.name_service.deregister 'task_name'
Attributes
The actual replay speed
This is updated during replay, and reflects the actual replay speed
@return [Float]
array of stream annotations
last replayed sample
name of the log file which holds the logged properties is converted into Regexp
@return [Orocos::Local] a local nameservice on which the log tasks
are registered. It is added to the global name service with
{#register_tasks} and removed with {#unregister_tasks}
@return [Orocos::Async::Local] a local async nameservice on which the log tasks
are registered. It is added to the global name service with
{#register_tasks} and removed with {#unregister_tasks}
Measure of time synchronization during replay
This is updated during replay to reflect how fast the replay actually is. This is the difference (in seconds) between the replay time that we should have and the replay time that we actually have
In practice, negative values mean that the replayed samples are behind the simulated times, and positive values mean that the replayed samples are replayed to fast
@return [Float]
set it to true if processing of qt events is needed during synced replay
array of all replayed annotaions this array is filled after {align} was called
array of all replayed ports
this array is filled after {align} was called
array of all replayed properties this array is filled after {align} was called
desired replay speed = 1 –> record time
@return [Hash<String,#call>] a mapping from a typelib type name to
an object that allows to extract the timestamp from a value of that type
@see {timestamp}
If true, the alignment algorithm is going to use the sample time for alignment. Otherwise, it uses the time at which the sample got written on disk (logical time)
See also time_source
The streams that are actually replayed
@return [Array<Pocolog::DataStream>]
Public Class Methods
Creates a new instance of Replay
If a path is givien load is called after creation to load the log files.
# File lib/orocos/log/replay.rb, line 190 def initialize(*path) if !path.empty? raise ArgumentError, "Replay.new(*path) is deprecated, use Replay.open(*path) to create and load files at the same time" end @default_timestamp = nil @timestamps = Hash.new @tasks = Hash.new @annotations = Array.new @current_annotations = Hash.new @speed = 1 @replayed_ports = Array.new @replayed_properties = Array.new @replayed_objects = Array.new @used_streams = Array.new @stream = nil @current_sample = nil @process_qt_events = false @log_config_file = Replay::log_config_file @namespace = '' reset_time_sync time_sync end
# File lib/orocos/log/replay.rb, line 174 def self.open(*path) replay = new replay.load(*path) replay rescue ArgumentError => e Orocos.error "Cannot load logfiles" raise e rescue Pocolog::Logfiles::MissingPrologue => e Orocos.error "Wrong log format" raise e end
Public Instance Methods
Adds the given time intervals as LogMarkers
@param [Array<Array<Time>>] intervals The intervals @param [String] comment Comment of the log markers
# File lib/orocos/log/replay.rb, line 772 def add_intervals_as_log_markers(intervals,comment) markers = log_markers # fill @markers from log file intervals.each do |interval| markers << LogMarker.new(interval.first,:start,-1,comment) markers << LogMarker.new(interval.last,:stop,-1,comment) end markers.sort! do |a,b| a.time <=> b.time end markers end
# File lib/orocos/log/replay.rb, line 457 def advance if(@stream) return @stream.advance else throw "Stream is not initialized yet" end end
Aligns all streams which have at least:
-
one reader
-
or one connections
-
or track set to true.
After calling this method no more ports can be tracked.
#time_source is passed through to the StreamAligner. It can be used to override the global time_source parameter. See time_source for available values.
# File lib/orocos/log/replay.rb, line 400 def align( time_source = self.time_source ) @replayed_ports = Array.new @used_streams = Array.new @replayed_annotations = Array.new if !replay? Log.warn "No ports are selected. Assuming that all ports shall be replayed." Log.warn "Connect port(s) or set their track flag to true to get rid of this message." track(true) end #get all properties which shall be replayed each_task do |task| if task.used? task.port("state").tracked=true if task.has_port?("state") task.properties.values.each do |property| property.tracked = true next if property.stream.empty? @replayed_properties << property end end end #get all streams which shall be replayed each_port do |port| if port.used? next if port.stream.empty? @replayed_ports << port end end Log.info "Aligning streams --> all ports which are unused will not be loaded!!!" if @replayed_properties.empty? && @replayed_ports.empty? raise "No log data are replayed. All selected streams are empty." end # If we do have something to replay, then add the annotations as # well annotations.each do |annotation| next if annotation.stream.empty? @replayed_annotations << annotation end @replayed_objects = @replayed_properties + @replayed_ports + @replayed_annotations @used_streams = @replayed_objects.map(&:stream) Log.info "Replayed Ports:" @replayed_ports.each {|port| Log.info PP.pp(port,"")} #join streams @stream = Pocolog::StreamAligner.new(time_source, *@used_streams) @stream.rewind reset_time_sync return step end
# File lib/orocos/log/replay.rb, line 513 def aligned? return @stream != nil end
# File lib/orocos/log/replay.rb, line 588 def calc_statistics(time = current_time) time = current_time @base_time ||= time @start_time ||= Time.now required_delta = (time - @base_time)/@speed actual_delta = Time.now - @start_time @out_of_sync_delta = @time_sync_proc.call(time,actual_delta,required_delta) @actual_speed = required_delta/actual_delta*@speed end
Clears all reader buffers. This is usefull if you are changing the replay direction.
# File lib/orocos/log/replay.rb, line 1036 def clear_reader_buffers @tasks.each_value do |task| task.clear_reader_buffers end end
close the log file, deregister from name service and also close all available streams
# File lib/orocos/log/replay.rb, line 494 def close deregister_tasks # TODO close all streams end
Tries to connect all input ports of the OROCOS task to simulated OutputPorts
Parameter:¶ ↑
*task => task to connect to
*port_mappings => hash to define port mappings {src_port_name => dst_port_name}
*ports_ignored => array of port names which shall be ignored
# File lib/orocos/log/replay.rb, line 326 def connect_to(task,port_mappings = Hash.new ,port_policies = Hash.new,ports_ignored = Array.new) #convenience block to do connect_to(task,:auto_ignore) if port_mappings == :auto_ignore ports_ignored = port_mappings port_mappings = Hash.new end ports_ignored = Array.new(ports_ignored) #start task if necessary if task.state == :PRE_OPERATIONAL task.configure end if task.state == :STOPPED task.start end #to have a better user interface the hash is inverted #port1 connect_to port2 is written as ('port1' => 'port2') port_mappings = port_mappings.invert task.each_port do |port| if port.to_orocos_port.kind_of?(Orocos::InputPort) && !ports_ignored.include?(port.name) target_port = find_port(port.type_name,port_mappings[port.name]||port.name) if target_port target_port.connect_to(port,port_policies[port.name]) elsif !ports_ignored.include? :auto_ignore raise ArgumentError, "cannot find an output port for #{port.name}" else Log.warn "No input port can be found for output port #{port.full_name}." end end end end
The current annotations
This is an aggregated version of annotations, where the value for each key is the last value known (i.e. the value from the last annotation with that key that has a timestamp lower than the current time)
# File lib/orocos/log/replay.rb, line 142 def current_annotations annotations.inject(Hash.new) do |current, ann| current.merge(ann.current_state) end end
returns the last port which recieved data
# File lib/orocos/log/replay.rb, line 684 def current_port if @current_sample stream_idx = @current_sample[0] replayed_ports[stream_idx] end end
returns the current data of the current sample
# File lib/orocos/log/replay.rb, line 692 def current_sample_data if @current_sample sample_info = @current_sample[2] sample_info[0].read_one_raw_data_sample(sample_info[1]) end end
# File lib/orocos/log/replay.rb, line 576 def current_time stream_idx, time, sample_info = @current_sample return if !time stream_type = @stream.stream_by_index(stream_idx).type if getter = (timestamps[stream_type.name] || default_timestamp) data = sample_info[0].read_one_raw_data_sample(sample_info[1]) getter[data] else time end end
Sets a code block to calculate the default timestamp duricng replay.
# File lib/orocos/log/replay.rb, line 279 def default_timestamp(&block) if block_given? then @default_timestamp = block else @default_timestamp end end
deregister the local name service again
# File lib/orocos/log/replay.rb, line 486 def deregister_tasks if @name_service Orocos::name_service.delete @name_service end end
The total duration of the replayed data, in seconds
@return [Float]
# File lib/orocos/log/replay.rb, line 520 def duration intervals = used_streams.map { |s| s.info.interval_lg } min = intervals.map(&:first).min max = intervals.map(&:last).max if min && max max - min else 0 end end
Iterates through all simulated ports.
# File lib/orocos/log/replay.rb, line 816 def each_port(&block) @tasks.each_value do |task| task.each_port(&block) end end
Iterates through all simulated tasks
# File lib/orocos/log/replay.rb, line 854 def each_task (&block) @tasks.each_value do |task| yield(task) if block_given? end end
Returns true if the end of file is reached.
# File lib/orocos/log/replay.rb, line 876 def eof? return @stream.eof? end
exports all aligned stream to a new log file if no start and end index is given all data are exported otherwise the data are truncated according to the given global indexes the block is called for each sample to update a progress bar
# File lib/orocos/log/replay.rb, line 1046 def export_to_file(file,start_index=0,end_index=0,&block) @stream.export_to_file(file,start_index,end_index,&block) end
Extracts time intervals from the log file where the given code block returns true.
For each sample the given code block is called with the current port and sample as parameter. After all samples were replayed the generated result vector (true is interpreted as 1) is filtered with a box filter of the given size. The returned intervals are these intervals where the filtered result vector is equal or bigger than min_val
@param [Time] start_time Start time of the interval which is replayed (nil = start of the log file) @param [Time] end_time End time of the interval which is replayed (nil = end of the log file) @param [Float] min_val Min value of the filtered result vector to be regarded as inlayer @param [Float] kernel_size Filter kernel size of the box filter in seconds @yield [reader,sample] @yieldparam reader the data reader of the port from which the
sample has been read
@yieldparam sample the data sample @yieldreturn [Boolean]
@return [Array<Array<Time>>] extracted intervals
# File lib/orocos/log/replay.rb, line 720 def extract_intervals(start_time=nil,end_time=nil, min_val=0.8,kernel_size=5.0,&block) #replay given intervals and collect results of the code block result,times = [],[] start_time ||= begin rewind time end seek(start_time) begin if block.call(current_port,current_sample_data) result << 1 else result << 0 end times << time end while(step && (!end_time || time <= end_time)) #filter result vector idx,sum,size = 0,0,0 filtered = result.map do |e| while times[idx+size] - times[idx] < kernel_size && idx+size < times.size-1 size += 1 sum += result[idx+size] end val = if size > 0 sum/size else 0 end sum -= result[idx] idx += 1 size -= 1 val end #extract intervals intervals,start = [],nil filtered.each_with_index do |e,i| if e >= min_val start ||= times[i] elsif start intervals << [start,times[i]] if times[i]-start >= kernel_size start = nil end end intervals end
# File lib/orocos/log/replay.rb, line 465 def first_sample_pos(stream) @stream.first_sample_pos(stream) end
returns the full name of a task reachable under the given name
# File lib/orocos/log/replay.rb, line 361 def full_name(name) # use full name name = name.to_s return name if @tasks.has_key?(name) # use namespace of replay name2 = map_to_namespace(name) return name2 if @tasks.has_key?(name2) # use all namespaces t = @tasks.find do |key,task| task.basename == name end t.first if t end
Extracts time intervals from the log file where the given code block returns true and adds these interval as log markers to the log replay instance.
@param [String] comment Comment of the log markers @param [Float] min_val Min value of the filtered result vector to be regarded as inlayer @param [Float] kernel_size Filter kernel size of the box filter in seconds @yield [reader,sample] @yieldparam reader the data reader of the port from which the
sample has been read
@yieldparam sample the data sample @yieldreturn [Boolean]
@return [Array<Array<Time>>] extracted intervals @see #extract_intervals @see #add_intervals_as_log_markers
# File lib/orocos/log/replay.rb, line 800 def generate_log_markers(comment,min_val=0.8,kernel_size=5.0,&block) intervals = extract_intervals(nil,nil, min_val,kernel_size,&block) add_intervals_as_log_markers(intervals,comment) rewind intervals end
# File lib/orocos/log/replay.rb, line 848 def has_task?(name) name = map_to_namespace name @tasks.has_key?(name.to_s) end
# File lib/orocos/log/replay.rb, line 469 def last_sample_pos(stream) @stream.last_sample_pos(stream) end
Loads a log files and creates TaskContexts which simulates the recorded tasks. You can either specify a single file or a hole directory. If you want to load more than one directory or file simultaneously you can use an array.
Logs that share the same basename, will be joined such that the streams within the logs appear continous.
a collection of files/directories can be given as arguments, followed by a typelib registry and/or an options hash. The options can be given as:
:registry - same as providing the registry directly
:multifile - set to :last if you don't want merging of logs with the same
basename
# File lib/orocos/log/replay.rb, line 972 def load(*paths) paths.flatten! raise ArgumentError, "No log file was given" if paths.empty? logreg = nil if paths.last.kind_of?(Typelib::Registry) logreg = paths.pop end opts = {} if paths.last.kind_of?(Hash) opts = paths.pop logreg = opts[:registry] if opts[:registry] end paths.each do |path| #check if path is a directory path = File.expand_path(path) if File.directory?(path) all_files = Dir.enum_for(:glob, File.join(path, '*.*.log')) by_basename = all_files.inject(Hash.new) do |h, path| split = path.match(/^(.*)\.(\d+)\.log$/) if split basename, number = split[1], Integer(split[2]) h[basename] ||= Array.new h[basename][number] = path h else Orocos.warn "invalid log file name #{path}. Expecting: /^(.*)\.(\d+)\.log$/" h end end if by_basename.empty? Orocos.warn "empty directory: #{path}" next end by_basename.each_value do |files| if opts[:multifile] == :last files = files[-1,1] end args = files.compact.map do |path| File.open(path) end args << logreg logfile = Pocolog::Logfiles.new(*args.compact) load_log_file(logfile, files.first) end elsif File.file?(path) file = Pocolog::Logfiles.open(path, logreg) load_log_file(file, path) else raise ArgumentError, "Can not load log file: #{path} is neither a directory nor a file" end end raise ArgumentError, "Nothing was loaded from the following log files #{paths.join("; ")}" if @tasks.empty? #register task on the local name server register_tasks end
Loads all the streams defined in the provided log file
# File lib/orocos/log/replay.rb, line 947 def load_log_file(file, path) Log.info " loading log file #{path}" file.streams.each do |s| if s.metadata["rock_stream_type"] == "annotations" @annotations << Annotations.new(path,s) next end load_task_from_stream(s,path) end end
# File lib/orocos/log/replay.rb, line 904 def load_task_from_stream(stream,path) #get the name of the task which was logged into the stream task_name = if stream.metadata.has_key? "rock_task_name" begin namespace, _ = Namespace.split_name(stream.metadata["rock_task_name"]) Namespace.validate_namespace_name(namespace) rescue ArgumentError => e Orocos.warn "invalid metadata rock_task_name:'#{stream.metadata["rock_task_name"]}' for stream #{stream.name}: #{e}" stream.metadata.delete("rock_task_name") return load_task_from_stream(stream,path) end stream.metadata["rock_task_name"] else result = stream.name.to_s.match(/^(.*)\./) result[1] if result end if task_name == nil task_name = "unknown" Log.warn "Stream name (#{stream.name}) does not follow the convention TASKNAME.PORTNAME and has no metadata, assuming as TASKNAME \"#{task_name}\"" end #check if there is a namespace task_name = if task_name == basename(task_name) map_to_namespace(task_name) else task_name end task = @tasks[task_name] if !task task = @tasks[task_name]= TaskContext.new(self,task_name, path,@log_config_file) end begin task.add_stream(path,stream) Log.info " loading stream #{stream.name} (#{stream.type_name})" rescue InitializePortError => error Log.warn " loading stream #{stream.name} (#{stream.type_name}) failed. Call the port for an error message." end task end
returns an array of #log_markers
# File lib/orocos/log/replay.rb, line 832 def log_markers @markers ||= Array.new return @markers if !@markers.empty? annotations.each do |annotation| #check if this is the right type if annotation.stream.type_name == "/logger/Annotations" @markers.concat LogMarker::parse(annotation.samples) end end @markers.sort! do |a,b| a.time <=> b.time end @markers end
Tries to find a OutputPort for a specefic data type. For port_name Regexp is allowed. If precise is set to true an error will be raised if more than one port is matching type_name and port_name.
# File lib/orocos/log/replay.rb, line 311 def port_for(type_name, port_name, precise=true) Log.warn "#port_for is deprecated. Use either #find_all_ports or #find_port" if precise find_port(type_name, port_name) else find_all_ports(type_name, port_name) end end
Returns an array of all simulated ports
# File lib/orocos/log/replay.rb, line 823 def ports result = Array.new each_port do |port| result << port end result end
pretty print for Replay
# File lib/orocos/log/replay.rb, line 238 def pretty_print(pp) pp.text "Orocos::Log::Replay" pp.nest(2) do pp.breakable pp.text "replay speed = #{@speed}" pp.breakable pp.text "Markers = #{@markers}" pp.breakable @tasks.each_value do |task| pp.breakable task.pretty_print(pp) end pp.breakable pp.text "Stream Annotations:" @annotations.each do |a| pp.breakable a.pretty_print(pp) end end end
# File lib/orocos/log/replay.rb, line 652 def push_sample(stream_idx, sample_info) #write sample to simulated ports or properties log_output = @replayed_objects[stream_idx] log_output.update(sample_info) if block_given? data = sample_info[0].read_one_raw_data_sample(sample_info[1]) yield(log_output,Typelib.to_ruby(data)) end return *@current_sample[0, 2] end
replays the last sample to the log port
# File lib/orocos/log/replay.rb, line 899 def refresh index, _, sample_info = @current_sample @replayed_objects[index].update(sample_info) end
registers all replayed log tasks on the local name server
# File lib/orocos/log/replay.rb, line 474 def register_tasks @name_service ||= Local::NameService.new @name_service_async ||= Orocos::Async::Local::NameService.new :tasks => @tasks.values if defined?(Orocos::Async) @tasks.each_pair do |name,task| @name_service.register task @name_service_async.register task if @name_service_async end Orocos::name_service.add @name_service Orocos::Async.name_service.add @name_service_async if @name_service_async end
returns false if no ports are or will be replayed
# File lib/orocos/log/replay.rb, line 225 def replay? #check if stream was initialized if @stream return true else each_task do |task| return true if task.used? end end return false end
Resets the simulated time. This should be called after the replay was paused.
# File lib/orocos/log/replay.rb, line 532 def reset_time_sync @start_time = nil @base_time = nil @actual_speed = 0 @out_of_sync_delta = 0 end
Rewinds all streams and replays the first sample.
# File lib/orocos/log/replay.rb, line 678 def rewind() @stream.rewind step end
Runs through the log files until the end is reached.
# File lib/orocos/log/replay.rb, line 808 def run(time_sync = false,speed=1,&block) reset_time_sync @speed = speed while step(time_sync,&block) do end end #Iterates through all simulated ports. def each_port(&block) @tasks.each_value do |task| task.each_port(&block) end end #Returns an array of all simulated ports def ports result = Array.new each_port do |port| result << port end result end #returns an array of log_markers def log_markers @markers ||= Array.new return @markers if !@markers.empty? annotations.each do |annotation| #check if this is the right type if annotation.stream.type_name == "/logger/Annotations" @markers.concat LogMarker::parse(annotation.samples) end end @markers.sort! do |a,b| a.time <=> b.time end @markers end def has_task?(name) name = map_to_namespace name @tasks.has_key?(name.to_s) end #Iterates through all simulated tasks def each_task (&block) @tasks.each_value do |task| yield(task) if block_given? end end #Returns the current position of the replayed sample. def sample_index @stream.sample_index end #Returns the number of samples. def size return @stream.size end #Returns the time of the current sample. def time return @stream.time end #Returns true if the end of file is reached. def eof? return @stream.eof? end #Seeks to the given position def seek(pos) #check if stream was generated otherwise call align align if @stream == nil @current_sample = @stream.seek(pos) if !@current_sample return end @current_sample[2] = @stream.sample_info(@current_sample[0]) #write all data to the ports @stream.streams.length.times do |stream_idx| if info = @stream.sample_info(stream_idx) @replayed_objects[stream_idx].update(info) end end end #replays the last sample to the log port def refresh index, _, sample_info = @current_sample @replayed_objects[index].update(sample_info) end def load_task_from_stream(stream,path) #get the name of the task which was logged into the stream task_name = if stream.metadata.has_key? "rock_task_name" begin namespace, _ = Namespace.split_name(stream.metadata["rock_task_name"]) Namespace.validate_namespace_name(namespace) rescue ArgumentError => e Orocos.warn "invalid metadata rock_task_name:'#{stream.metadata["rock_task_name"]}' for stream #{stream.name}: #{e}" stream.metadata.delete("rock_task_name") return load_task_from_stream(stream,path) end stream.metadata["rock_task_name"] else result = stream.name.to_s.match(/^(.*)\./) result[1] if result end if task_name == nil task_name = "unknown" Log.warn "Stream name (#{stream.name}) does not follow the convention TASKNAME.PORTNAME and has no metadata, assuming as TASKNAME \"#{task_name}\"" end #check if there is a namespace task_name = if task_name == basename(task_name) map_to_namespace(task_name) else task_name end task = @tasks[task_name] if !task task = @tasks[task_name]= TaskContext.new(self,task_name, path,@log_config_file) end begin task.add_stream(path,stream) Log.info " loading stream #{stream.name} (#{stream.type_name})" rescue InitializePortError => error Log.warn " loading stream #{stream.name} (#{stream.type_name}) failed. Call the port for an error message." end task end # Loads all the streams defined in the provided log file def load_log_file(file, path) Log.info " loading log file #{path}" file.streams.each do |s| if s.metadata["rock_stream_type"] == "annotations" @annotations << Annotations.new(path,s) next end load_task_from_stream(s,path) end end #Loads a log files and creates TaskContexts which simulates the recorded tasks. #You can either specify a single file or a hole directory. If you want to load #more than one directory or file simultaneously you can use an array. # #Logs that share the same basename, will be joined such that the streams within #the logs appear continous. # #a collection of files/directories can be given as arguments, followed by a #typelib registry and/or an options hash. The options can be given as: # # :registry - same as providing the registry directly # :multifile - set to :last if you don't want merging of logs with the same # basename # def load(*paths) paths.flatten! raise ArgumentError, "No log file was given" if paths.empty? logreg = nil if paths.last.kind_of?(Typelib::Registry) logreg = paths.pop end opts = {} if paths.last.kind_of?(Hash) opts = paths.pop logreg = opts[:registry] if opts[:registry] end paths.each do |path| #check if path is a directory path = File.expand_path(path) if File.directory?(path) all_files = Dir.enum_for(:glob, File.join(path, '*.*.log')) by_basename = all_files.inject(Hash.new) do |h, path| split = path.match(/^(.*)\.(\d+)\.log$/) if split basename, number = split[1], Integer(split[2]) h[basename] ||= Array.new h[basename][number] = path h else Orocos.warn "invalid log file name #{path}. Expecting: /^(.*)\.(\d+)\.log$/" h end end if by_basename.empty? Orocos.warn "empty directory: #{path}" next end by_basename.each_value do |files| if opts[:multifile] == :last files = files[-1,1] end args = files.compact.map do |path| File.open(path) end args << logreg logfile = Pocolog::Logfiles.new(*args.compact) load_log_file(logfile, files.first) end elsif File.file?(path) file = Pocolog::Logfiles.open(path, logreg) load_log_file(file, path) else raise ArgumentError, "Can not load log file: #{path} is neither a directory nor a file" end end raise ArgumentError, "Nothing was loaded from the following log files #{paths.join("; ")}" if @tasks.empty? #register task on the local name server register_tasks end # Clears all reader buffers. # This is usefull if you are changing the replay direction. def clear_reader_buffers @tasks.each_value do |task| task.clear_reader_buffers end end # exports all aligned stream to a new log file # if no start and end index is given all data are exported # otherwise the data are truncated according to the given global indexes # the block is called for each sample to update a progress bar def export_to_file(file,start_index=0,end_index=0,&block) @stream.export_to_file(file,start_index,end_index,&block) end #This is used to support the syntax. #log_replay.task def method_missing(m,*args,&block) #:nodoc: task_name = full_name(m) return @tasks[task_name] if task_name begin super(m.to_sym,*args,&block) rescue NoMethodError => e Log.error "#{m} is not a Log::Task of the current log file(s)" Log.error "The following tasks are availabe:" @tasks.each_value do |task| Log.error " #{task.name}" end raise e end end end
# File lib/orocos/log/replay.rb, line 267 def sample_index() return @stream.sample_index if @stream return nil end
# File lib/orocos/log/replay.rb, line 259 def sample_index_for_time(time) prev_pos = sample_index seek(time) target_sample_pos = sample_index seek(prev_pos) return target_sample_pos end
Seeks to the given position
# File lib/orocos/log/replay.rb, line 881 def seek(pos) #check if stream was generated otherwise call align align if @stream == nil @current_sample = @stream.seek(pos) if !@current_sample return end @current_sample[2] = @stream.sample_info(@current_sample[0]) #write all data to the ports @stream.streams.length.times do |stream_idx| if info = @stream.sample_info(stream_idx) @replayed_objects[stream_idx].update(info) end end end
# File lib/orocos/log/replay.rb, line 272 def single_data(id) if @stream return @stream.single_data(id) end end
Returns the number of samples.
# File lib/orocos/log/replay.rb, line 866 def size return @stream.size end
Gets the next sample, writes it to the ports which are connected to the OutputPort and updates all its readers.
If a block is given it is called this the name of the replayed port.
@param [Boolean] #time_sync if true, the method will sleep as much
time as required to match the time delta in the file
@yield [reader,sample] @yieldparam reader the data reader of the port from which the
sample has been read
@yieldparam sample the data sample
# File lib/orocos/log/replay.rb, line 613 def step(time_sync=false,&block) #check if stream was generated otherwise call align if @stream == nil return align end stream_idx, time = @stream.advance if !stream_idx @current_sample = nil return end sample_info = @stream.sample_info(stream_idx) @current_sample = [stream_idx, time, sample_info] calc_statistics(time) #wait if replay is faster than the desired speed and time_sync is set to true if time_sync && @out_of_sync_delta > 0.001 if @process_qt_events == true start_wait = Time.now while true if $qApp $qApp.processEvents() end break if !@start_time #break if start_time was reseted throuh processEvents wait = @out_of_sync_delta -(Time.now - start_wait) if wait > 0.001 sleep [0.01,wait].min else break end calc_statistics(time) end else sleep(@out_of_sync_delta) end end push_sample(stream_idx, sample_info) end
Gets the previous sample and writes it to the ports which are connected to the OutputPort and updated its readers (see step).
# File lib/orocos/log/replay.rb, line 665 def step_back(time_sync=false,&block) #check if stream was generated otherwise call start if !@stream align return end @current_sample = @stream.step_back return if !@current_sample @current_sample[2] = @stream.sample_info(@current_sample[0]) push_sample(@current_sample[0], @current_sample[2]) end
# File lib/orocos/log/replay.rb, line 499 def stream_index_for_name(name) if @stream return @stream.stream_index_for_name(name) end throw "Stream is not initialized yet" end
# File lib/orocos/log/replay.rb, line 506 def stream_index_for_type(name) if @stream return @stream.stream_index_for_type(name) end throw "Stream is not initialized yet" end
returns ture if the next sample must be replayed to meet synchronous replay
# File lib/orocos/log/replay.rb, line 567 def sync_step? calc_statistics if @out_of_sync_delta > 0.001 false else true end end
Returns the simulated task with the given namen.
# File lib/orocos/log/replay.rb, line 383 def task(name) name2 = full_name(name) raise "cannot find TaskContext called #{name}" if !name2 return @tasks[name2] end
returns true if a task with the given name exists
# File lib/orocos/log/replay.rb, line 378 def task?(name) !!full_name(name) end
returns an array of all simulated tasks
# File lib/orocos/log/replay.rb, line 215 def tasks @tasks.values end
returns the time of the current sample replayed
# File lib/orocos/log/replay.rb, line 220 def time @base_time end
Returns where from the time used for alignment should be taken. It can be one of
- false
-
use the time at which the logger received the data (“logical time”)
- :use_sample_time
-
for streams whose data contains a field called “time” of type base/Time (from Rock's base/types package), use the time contained in that field. Otherwise, use the logical time.
See use_sample_time, use_sample_time=
# File lib/orocos/log/replay.rb, line 160 def time_source if use_sample_time return :use_sample_time else return false end end
this can be used to set a different time sync logic the code block has three parameters time = current sample time actual_delta = time between start of replay and now required_delta = time which should have elapsed between start and now to replay at the desired speed the code block must return the number of seconds which the replay shall wait before the sample is repalyed
Do not block the program otherwise qt events are no longer processed!!!
Example #time_sync do |time,actual_delta,required_delta|
my_object.busy? ? 1 : 0
end
# File lib/orocos/log/replay.rb, line 555 def time_sync(&block) if block_given? @time_sync_proc = block else @time_sync_proc = Proc.new do |time,actual_delta,required_delta| required_delta - actual_delta end end end
Declares how the timestamp can be extracted out of values of a given type
@example use the 'time' field in /base/samples/RigidBodyState as timestamp
replay.timestamp '/base/samples/RigidBodyState' do |rbs| rbs.time end
# File lib/orocos/log/replay.rb, line 292 def timestamp(type_name, &block) timestamps[type_name] = block end
If set to true all ports are replayed and are not filtered out by the filter otherwise only ports are replayed which have a reader or a connection to an other port
# File lib/orocos/log/replay.rb, line 300 def track(value,filter=Hash.new) options, filter = Kernel::filter_options(filter,[:tasks]) @tasks.each_value do |task| task.track(value,filter) if !options.has_key?(:tasks) || task.name =~ options[:tasks] end end