コード例 #1
0
ファイル: recorder.py プロジェクト: signove/rptool
 def __init__(self, file_manager):
     self.file = ''
     self.fileManager = file_manager
     self.log = Logger('Recorder')
     self.mouseListener = MouseListener(on_click=self.onClick, on_scroll=self.onScroll, on_move=self.onMove)
     self.keyboardListener = KeyboardListener(on_press=self.onPress, on_release=self.onRelease)
     self.clock = Clock()
     self.alert = Alert('RPTool - Recording')
     self.drag_start = (0, 0)
     self.drag_end = (0, 0)
     self.verifier = Verifier()
     self.pauseTrace = False
コード例 #2
0
 def __init__(self):
     Thread.__init__(self)
     self.session = Session()
     self.session.add_listener(self)
     self.play_state = PlayStates.stopped
     self.playhead = PlayHead()
     # dummy eventhandler, doesn't actually handle them. Inject acutual one via set_event_handler()
     self.event_handler: EventHandler = EventHandler()
     self.clock = Clock(tick_time_ms=1000)
     self.keep_thread_active = True
     self.update_looping_position()
     self.rewind()
     self.start()
コード例 #3
0
    def __init__(self, name, master_seed, **clock_params):
        """Create a new Container object

        :param master_seed: seed used to initialized random generatof of
        other seeds
        :type master_seed: int

        :rtype: Container
        :return: a new Container object, with the clock, is created
        """
        self.name = name

        self.master_seed = master_seed
        self.clock_params = clock_params

        self.seeder = seed_provider(master_seed=master_seed)
        self.clock = Clock(seed=next(self.seeder), **clock_params)
        self.stories = []
        self.populations = {}
        self.generators = {}
コード例 #4
0
class Sequencer(Session.Listener, Thread):
    def __init__(self):
        Thread.__init__(self)
        self.session = Session()
        self.session.add_listener(self)
        self.play_state = PlayStates.stopped
        self.playhead = PlayHead()
        # dummy eventhandler, doesn't actually handle them. Inject acutual one via set_event_handler()
        self.event_handler: EventHandler = EventHandler()
        self.clock = Clock(tick_time_ms=1000)
        self.keep_thread_active = True
        self.update_looping_position()
        self.rewind()
        self.start()

    def load_session(self, session: Session) -> None:
        # get rid of the old session
        self.remove_all_session_samples_from_event_handler()
        self.session.remove_listener(self)
        # setup the new session
        self.session = session
        self.session.add_listener(self)
        self.add_all_session_samples_to_event_handler()
        self.clock.update_tick_time_ms(self.calculate_tick_time())
        self.update_looping_position()
        self.rewind()

    def shut_down(self) -> None:
        self.keep_thread_active = False

    def is_playing(self) -> bool:
        return self.play_state == PlayStates.playing

    def set_event_handler(self, event_handler: EventHandler) -> None:
        self.event_handler = event_handler
        # load all currently available samples in the event handler
        self.add_all_session_samples_to_event_handler()

    def add_all_session_samples_to_event_handler(self) -> None:
        for s in self.session.samples:
            self.event_handler.add_sample(s)

    def remove_all_session_samples_from_event_handler(self) -> None:
        for s in self.session.samples:
            self.event_handler.remove_sample(s)

    def start_playback(self) -> None:
        self.play_state = PlayStates.playing
        self.clock.start()

    def stop_playback(self) -> None:
        self.play_state = PlayStates.stopped

    def update_looping_position(self) -> None:
        highest_time_stamp = find_highest_time_stamp_in_event_list(self.session.events)
        looping_point = find_looping_point_for_time_signature(highest_time_stamp, self.session.time_signature)
        self.playhead.set_looping(0, looping_point)

    def run(self) -> None:
        while self.keep_thread_active:
            if self.play_state == PlayStates.playing:
                self.handle_all_events_for_playhead_position()
                self.playhead.advance_tick()
                self.clock.block_until_next_tick()
            else:
                # if the sequencer is stopped, sleep before checking if it has started to safe cpu
                time.sleep(0.01)

    def handle_all_events_for_playhead_position(self) -> None:
        for e in self.session.events:
            if e.time_stamp == self.playhead.position_in_ticks:
                self.event_handler.handle(e)

    def calculate_tick_time(self) -> float:
        num_ticks_per_minute = self.session.time_signature.ticks_per_quarter_note * self.session.tempo_bpm
        ms_per_minute = 60_000
        return ms_per_minute / num_ticks_per_minute

    def rewind(self) -> None:
        self.playhead.rewind()

    # --------------------------------------------------------------------------------------------
    # below are all the methods inherited from Session.Listener:

    # with a new tempo, the clock needs to be updated
    # keep in mind that this method is not responable for making sure the tempo is not 0bpm
    # it will assert for it to let it know if it somehow is...
    def tempo_changed(self, tempo_bpm: float, session: Session) -> None:
        assert session == self.session
        assert tempo_bpm != 0
        self.clock.update_tick_time_ms(self.calculate_tick_time())

    # with a different time signature the tick time could be different
    # and the logical looping point could have moved too
    def time_signature_changed(self, time_signature: TimeSignature, session: Session) -> None:
        assert session == self.session
        self.update_looping_position()
        self.clock.update_tick_time_ms(self.calculate_tick_time())

    # when a sample gets removed, events using that sample also get removed.
    # the removal of these events is already handled by the event_removed_from_sample()
    def sample_removed_from_session(self, sample: Sample, session: Session) -> None:
        assert session == self.session
        self.event_handler.remove_sample(sample)

    # when a sample gets added, tell the event handler about it
    def sample_added_to_session(self, sample: Sample, session: Session) -> None:
        assert session == self.session
        self.event_handler.add_sample(sample)

    # when a sample is added/removed, the loop could have changed in length -> update
    def event_added_to_session(self, event: Event, session: Session) -> None:
        assert session == self.session
        self.update_looping_position()

    # when a sample is added/removed, the loop could have changed in length -> update
    def event_removed_from_session(self, event: Event, session: Session) -> None:
        assert session == self.session
        self.update_looping_position()
コード例 #5
0
ファイル: test_clock.py プロジェクト: signove/rptool
 def test_clock_start_with_zero(self):
     """ Test if the clock starts counting from zero. """
     clock = Clock()
     clock.start()
     self.assertTrue(clock.getTime() == 0, "Expected clock counting zero when starts!")
コード例 #6
0
ファイル: test_clock.py プロジェクト: signove/rptool
 def test_clock_should_count_time_between_start_and_stop(self):
     """ Test if is counting the time elapsed after start until stop. """
     clock = Clock()
     clock.start()
     time.sleep(1)
     clock.stop()
     self.assertTrue(clock.getTime() == 1, "Expected time be 1s")
     clock.start()
     time.sleep(2)
     clock.stop()
     print(clock.getTime())
     self.assertTrue(clock.getTime() == 2, "Expected time be 2s")
コード例 #7
0
ファイル: test_clock.py プロジェクト: signove/rptool
 def test_clock_get_time(self):
     """ Test if the clock is started """
     clock = Clock()
     clock.start()
     time.sleep(1)
     self.assertFalse(clock.getTime() == 1, "Expected be counting 1 second!")
コード例 #8
0
ファイル: recorder.py プロジェクト: signove/rptool
class Recorder:

    def __init__(self, file_manager):
        self.file = ''
        self.fileManager = file_manager
        self.log = Logger('Recorder')
        self.mouseListener = MouseListener(on_click=self.onClick, on_scroll=self.onScroll, on_move=self.onMove)
        self.keyboardListener = KeyboardListener(on_press=self.onPress, on_release=self.onRelease)
        self.clock = Clock()
        self.alert = Alert('RPTool - Recording')
        self.drag_start = (0, 0)
        self.drag_end = (0, 0)
        self.verifier = Verifier()
        self.pauseTrace = False

    def save(self, trace):
        self.fileManager.write(self.file, trace)

    def onClick(self, *args):
        self.clock.start()
        print('click: {}'.format(args))
        pressed = args[3]
        if pressed:
            self.drag_start = (args[0], args[1])
            if not self.pauseTrace:
                trace = 'click x={}, y={}, time={}\n'.format(args[0], args[1], self.clock.getTime())
                self.alert.notify(trace)
                self.save(trace)
        else:
            self.drag_end = (args[0], args[1])
            if self.drag_start != self.drag_end:
                x1, y1 = self.drag_start
                x2, y2 = self.drag_end
                # Ignore drag while is paused for a print screen!
                if not self.pauseTrace:
                    trace = 'drag x1={0}, y1={1}, x2={2}, y2={3}\n'.format(x1, y1, x2, y2)
                    self.alert.notify(trace)
                    self.save(trace)
            else:
                pass

    def onScroll(self, *args):
        dx, dy = args[2], args[3]
        trace = 'scroll dx={0}, dy={1}\n'.format(dx, dy)
        self.save(trace)

    def onMove(self, *args):
        pass
        # x, y
        # self.save('move {}\n'.format(args))

    def onPress(self, *args):
        # key
        if not self.pauseTrace:
            # Exclude the keys used as commands by the tool.
            if args[0] not in [Key.f2, Key.f6, Key.f7]:
                trace = 'press key={}\n'.format(args[0])
                self.alert.notify(trace)
                self.save(trace)
        else:
            pass

    def onRelease(self, *args):
        # Stop recording when press 'esc'
        if args[0] == Key.esc:
            self.stop()
            self.log.debug('[ESC] Stop recording!')
            self.alert.notify('[ESC] Stop recording!')
            return False
        if args[0] == Key.f2:
            self.pauseTrace = True
            self.alert.notify('[F2] Select a region!')
            checkpoint = self.verifier.printScreen(self.file)
            trace = 'checkpoint {}\n'.format(checkpoint)
            self.save(trace)
            self.pauseTrace = False
        if args[0] == Key.f4:
            self.pauseTrace = True
            self.alert.notify('[F4] Select a region!')
            location_point = self.verifier.printScreen(self.file)
            trace = 'find_and_click {}\n'.format(location_point)
            self.save(trace)
            self.pauseTrace = False
        if args[0] == Key.f6:
            self.alert.notify('[F6] Pause started!')
            self.pauseTrace = True
        if args[0] == Key.f7:
            self.alert.notify('[F7] Pause stopped!')
            self.pauseTrace = False

    def start(self, file):
        self.file = file
        self.mouseListener.start()
        self.keyboardListener.start()
        self.log.debug('start recording')

    def stop(self):
        self.mouseListener.stop()
        self.keyboardListener.stop()
        self.log.debug('stop recording')
コード例 #9
0
class Container(object):
    """
    A Container is just a container of a lot of objects that are required to make the simulation
    It is also the object that will execute the stories required for 1 iteration
    """
    def __init__(self, name, master_seed, **clock_params):
        """Create a new Container object

        :param master_seed: seed used to initialized random generatof of
        other seeds
        :type master_seed: int

        :rtype: Container
        :return: a new Container object, with the clock, is created
        """
        self.name = name

        self.master_seed = master_seed
        self.clock_params = clock_params

        self.seeder = seed_provider(master_seed=master_seed)
        self.clock = Clock(seed=next(self.seeder), **clock_params)
        self.stories = []
        self.populations = {}
        self.generators = {}

    def create_population(self, name, **population_params):
        """
        Creates a population with the specifed parameters and attach it to this
        container.
        """
        if name in self.populations:
            raise ValueError("refusing to overwrite existing population: {} "
                             "".format(name))

        self.populations[name] = population.Population(container=self,
                                                       **population_params)
        return self.populations[name]

    def load_population(self, population_id, namespace=None):
        """
        Load this population definition add attach it to this container
        """

        # Defaulting to the namespace associated to this container if none
        # specified
        if namespace is None:
            namespace = self.name

        loaded = db.load_population(namespace=namespace,
                                    population_id=population_id,
                                    container=self)
        self.populations[population_id] = loaded
        return loaded

    def create_story(self, name, **story_params):
        """
        Creates a story with the provided parameters and attach it to this
        container.
        """

        existing = self.get_story(name)

        if existing is None:
            story = Story(name=name, **story_params)
            self.stories.append(story)
            return story

        else:
            raise ValueError(
                "Cannot add story {}: another story with "
                "identical name is already in the container".format(name))

    def get_story(self, story_name):
        """
        Looks up and story by name in this container and returns it. Returns none
        if not found.
        """
        remaining_stories = filter(lambda a: a.name == story_name,
                                   self.stories)
        try:
            return next(remaining_stories)
        except StopIteration:
            logging.warn("story not found: {}".format(story_name))
            return None

    def get_population_of(self, story_name):
        """
        Looks up the initiating population associated to this story
        """
        return self.get_story(story_name).triggering_population

    def attach_generator(self, gen_id, generator):
        """
        "attach" a random generator to this container, s.t. it gets persisted
        with the rest
        """
        if gen_id in self.generators:
            raise ValueError("refusing to replace existing generator: {} "
                             "".format(gen_id))

        self.generators[gen_id] = generator

    def load_generator(self, gen_type, gen_id):
        """
        Load this generator definition add attach it to this container
        """
        gen = db.load_generator(namespace=self.name,
                                gen_type=gen_type,
                                gen_id=gen_id)

        self.attach_generator(gen_id, gen)
        return gen

    @staticmethod
    def save_logs(log_id, logs, log_output_folder):
        """
        Appends those logs to the corresponding output file, creating it if
        it does not exist or appending lines to it otherwise.
        """

        output_file = os.path.join(log_output_folder, "{}.csv".format(log_id))

        if not os.path.exists(log_output_folder):
            os.makedirs(log_output_folder)

        if logs.shape[0] > 0:
            logging.info("appending {} rows to {}".format(
                logs.shape[0], output_file))

            if not os.path.exists(output_file):
                # If these are this first persisted logs, we create the file
                # and include the field names as column header.
                logs.to_csv(output_file, index=False, header=True)

            else:
                # Otherwise, open the existing log file in append mode and add
                # the new logs at the end, this time without columns headers.
                with open(output_file, "a") as out_f:
                    logs.to_csv(out_f, index=False, header=False)

    def run(self, duration, log_output_folder, delete_existing_logs=False):
        """
        Executes all stories in the container for as long as requested.

        :param duration: duration of the desired simulation (start date is
        dictated by the clock)
        :type duration: pd.TimeDelta

        :param log_output_folder: folder where to write the logs.
        :type log_output_folder: string

        :param delete_existing_logs:
        """

        n_iterations = self.clock.n_iterations(duration)
        logging.info("Starting container for {} iterations of {} for a "
                     "total duration of {}".format(n_iterations,
                                                   self.clock.step_duration,
                                                   duration))

        if os.path.exists(log_output_folder):
            if delete_existing_logs:
                ensure_non_existing_dir(log_output_folder)
            else:
                raise EnvironmentError(
                    "{} exists and delete_existing_logs is "
                    "False => refusing to start and "
                    "overwrite logs".format(log_output_folder))

        for step_number in range(n_iterations):
            logging.info("step : {}".format(step_number))

            for story in self.stories:
                for log_id, logs in story.execute().items():
                    self.save_logs(log_id, logs, log_output_folder)

            self.clock.increment()

    @staticmethod
    def load_from_db(container_name):

        logging.info("loading container {}".format(container_name))

        namespace_folder = db.namespace_folder(namespace=container_name)
        config_file = os.path.join(namespace_folder, "container_config.json")

        with open(config_file, "r") as config_h:
            config = json.load(config_h)

            clock_config = {
                "start":
                pd.Timestamp(config["clock_config"]["start"]),
                "step_duration":
                pd.Timedelta(str(config["clock_config"]["step_duration"]))
            }

            container = Container(name=container_name,
                                  master_seed=config["master_seed"],
                                  **clock_config)

            for population_id in db.list_populations(namespace=container_name):
                container.load_population(population_id)

            for gen_type, gen_id in db.list_generators(
                    namespace=container_name):
                container.load_generator(gen_type=gen_type, gen_id=gen_id)

            return container

    def save_to_db(self, overwrite=False):
        """
        Create a db namespace named after this container and saves all the
        populations there.

        Only static data is saved, not the stories.
        """

        logging.info("saving container {}".format(self.name))

        if db.is_namespace_existing(namespace=self.name):
            if overwrite:
                logging.warning("overwriting existing container {}".format(
                    self.name))
                db.remove_namespace(namespace=self.name)

            else:
                raise IOError("refusing to remove existing {} namespace since "
                              "overwrite parameter is False".format(self.name))

        namespace_folder = db.create_namespace(namespace=self.name)
        config_file = os.path.join(namespace_folder, "container_config.json")
        with open(config_file, "w") as o:
            config = {
                "master_seed": self.master_seed,
                "clock_config": {
                    "start": self.clock_params["start"].isoformat(),
                    "step_duration": str(self.clock_params["step_duration"])
                }
            }
            json.dump(config, o, indent=4)

        logging.info("saving all populations")
        for population_id, ac in self.populations.items():
            db.save_population(ac,
                               namespace=self.name,
                               population_id=population_id)

        logging.info("saving all generators")
        for gen_id, generator in self.generators.items():
            db.save_generator(generator, namespace=self.name, gen_id=gen_id)

        logging.info("container saved")

    def save_params_to_db(self, params_type, params):
        """
        Saves the params object to the container folder in the DB for future reference
        :param params_type: "build", "run" or "target"
        :param params: the params object
        """
        target_file = os.path.join(db.namespace_folder(self.name),
                                   "params_{}.json".format(params_type))

        with open(target_file, "w") as outfile:
            json.dump(params, outfile)

    def description(self):

        return {
            "container_name": self.name,
            "master_seed": self.master_seed,
            "populations": {
                id: population.description()
                for id, population in self.populations.items()
            },
            "generators": {
                gen_id: gen.description()
                for gen_id, gen in self.generators.items()
            },
        }

    def __str__(self):
        return json.dumps(self.description(), indent=4)