<commit_before>package org.multibit.hd.hardware.core;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.wallet.KeyChain;
import org.multibit.hd.hardware.core.concurrent.SafeExecutors;
import org.multibit.hd.hardware.core.events.HardwareWalletEvents;
import org.multibit.hd.hardware.core.events.MessageEvents;
import org.multibit.hd.hardware.core.fsm.CreateWalletSpecification;
import org.multibit.hd.hardware.core.fsm.HardwareWalletContext;
import org.multibit.hd.hardware.core.fsm.LoadWalletSpecification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>Service to provide the following to application:</p>
 * <ul>
 * <li>Main entry point for downstream API consumers</li>
 * <li>Handles high level hardware wallet use cases (e.g. "create new wallet" etc)</li>
 * </ul>
 *
 * <p>Refer to the examples for how to correctly configure the service for use in downstream
 * consumer applications.</p>
 *
 * @since 0.0.1
 *  
 */
public class HardwareWalletService {

  private static final Logger log = LoggerFactory.getLogger(HardwareWalletService.class);

  /**
   * Monitors the hardware client to manage state transitions in response to incoming messages
   */
  private final ListeningScheduledExecutorService clientMonitorService = SafeExecutors.newSingleThreadScheduledExecutor("monitor-hw-client");

  /**
   * The current hardware wallet context
   */
  private final HardwareWalletContext context;

  /**
   * True if the service has stopped
   */
  private boolean stopped = false;

  /**
   * @param client The hardware wallet client providing the low level messages
   */
  public HardwareWalletService(HardwareWalletClient client) {

    Preconditions.checkNotNull(client, "'client' must be present");

    context = new HardwareWalletContext(client);
  }

  /**
   * <p>Start the service and await the connection of a hardware wallet</p>
   */
  public void start() {

    if (stopped) {
      throw new IllegalStateException("Once stopped the service must be started with a fresh instance");
    }

    // Start the hardware wallet state machine
    clientMonitorService.scheduleAtFixedRate(
      new Runnable() {
        @Override
        public void run() {

          // Note: If an error occurs all future requests are suppressed

          // It if we are in the await state then we use a client
          // call (e.g. initialise()) to poke the device to elicit
          // a low level message response
          context.getState().await(context);

        }
      },
      0, // Immediate start
      // Polling time in order to progress the state
      // Devices will respond with some kind of event within 500ms for states that are
      // awaiting progression (e.g. Connected -> Initialised)
      // Setting this lower has no effect on speed of operations and may introduce
      // instability with overlapping calls due to "impatience"
      1, TimeUnit.SECONDS
    );
  }

  /**
   * <p>Stop the service</p>
   */
  public void stopAndWait() {

    log.debug("Service {} stopping...", this.getClass().getSimpleName());

    context.resetToStopped();

    // Ensure downstream subscribers are purged
    HardwareWalletEvents.unsubscribeAll();
    MessageEvents.unsubscribeAll();

    try {<commit_msg>Switch to schedule with fixed delay to avoid overlapping requests<commit_after>package org.multibit.hd.hardware.core;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.wallet.KeyChain;
import org.multibit.hd.hardware.core.concurrent.SafeExecutors;
import org.multibit.hd.hardware.core.events.HardwareWalletEvents;
import org.multibit.hd.hardware.core.events.MessageEvents;
import org.multibit.hd.hardware.core.fsm.CreateWalletSpecification;
import org.multibit.hd.hardware.core.fsm.HardwareWalletContext;
import org.multibit.hd.hardware.core.fsm.LoadWalletSpecification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>Service to provide the following to application:</p>
 * <ul>
 * <li>Main entry point for downstream API consumers</li>
 * <li>Handles high level hardware wallet use cases (e.g. "create new wallet" etc)</li>
 * </ul>
 *
 * <p>Refer to the examples for how to correctly configure the service for use in downstream
 * consumer applications.</p>
 *
 * @since 0.0.1
 *  
 */
public class HardwareWalletService {

  private static final Logger log = LoggerFactory.getLogger(HardwareWalletService.class);

  /**
   * Monitors the hardware client to manage state transitions in response to incoming messages
   */
  private final ListeningScheduledExecutorService clientMonitorService = SafeExecutors.newSingleThreadScheduledExecutor("monitor-hw-client");

  /**
   * The current hardware wallet context
   */
  private final HardwareWalletContext context;

  /**
   * True if the service has stopped
   */
  private boolean stopped = false;

  /**
   * @param client The hardware wallet client providing the low level messages
   */
  public HardwareWalletService(HardwareWalletClient client) {

    Preconditions.checkNotNull(client, "'client' must be present");

    context = new HardwareWalletContext(client);
  }

  /**
   * <p>Start the service and await the connection of a hardware wallet</p>
   */
  public void start() {

    if (stopped) {
      throw new IllegalStateException("Once stopped the service must be started with a fresh instance");
    }

    // Start the hardware wallet state machine
    clientMonitorService.scheduleWithFixedDelay(
      new Runnable() {
        @Override
        public void run() {

          // Note: If an error occurs all future requests are suppressed

          // It if we are in the await state then we use a client
          // call (e.g. initialise()) to poke the device to elicit
          // a low level message response
          context.getState().await(context);

        }
      },
      0, // Immediate start
      // Delay time in order to progress the state
      // Devices will respond with some kind of event within 1 second for states that are
      // awaiting progression (e.g. Connected -> Initialised)
      // Setting this lower has no effect on speed of operations and may introduce
      // instability with overlapping calls due to "impatience"
      1, TimeUnit.SECONDS
    );
  }

  /**
   * <p>Stop the service</p>
   */
  public void stopAndWait() {

    log.debug("Service {} stopping...", this.getClass().getSimpleName());

    context.resetToStopped();

    // Ensure downstream subscribers are purged
    HardwareWalletEvents.unsubscribeAll();
    MessageEvents.unsubscribeAll();

    try {
{'fraction_non_alphanumeric': 0.09442640692640693, 'fraction_numerical': 0.002435064935064935, 'mean_word_length': 3.5328019619865114, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4942432', 'n_tokens_mistral': 2083, 'n_tokens_neox': 2041, 'n_words': 624}