public static void initialize(String appVersion, boolean debugLogging) { if (debugLogging) { minimumLogLevel = DEBUG; } Logger initLogger = getLogger(Logger.class); StringBuilder sb = new StringBuilder(); sb.append("System information:"); sb.append(Util.NEW_LINE_CHAR); sb.append(System.getProperty("os.version")); sb.append(", "); sb.append(android.os.Build.VERSION.SDK_INT); sb.append(", "); sb.append(android.os.Build.DEVICE); sb.append(", "); sb.append(android.os.Build.MODEL); sb.append(", "); sb.append(android.os.Build.PRODUCT); sb.append("; App version: "); sb.append(appVersion); initLogger.info(sb.toString()); }
/** Created by matthes on 03.11.15. */ public abstract class AsyncAdapter implements OBDAdapter { private static Logger LOGGER = Logger.getLogger(AsyncAdapter.class); private static final long DEFAULT_NO_DATA_TIMEOUT = 15000; // *10 for debug private final char endOfLineOutput; private final char endOfLineInput; private CommandExecutor commandExecutor; private Subscription dataObservable; private AtomicBoolean quirkDisabled = new AtomicBoolean(false); public AsyncAdapter(char endOfLineOutput, char endOfLineInput) { this.endOfLineOutput = endOfLineOutput; this.endOfLineInput = endOfLineInput; } /** * use this method to disable a CommandExecutor quirk. * * @see {@link #getQuirk()} */ public void disableQuirk() { if (!quirkDisabled.getAndSet(true) && commandExecutor != null) { commandExecutor.setQuirk(null); } } @Override public Observable<Boolean> initialize(InputStream is, OutputStream os) { this.commandExecutor = new CommandExecutor( is, os, Collections.emptySet(), this.endOfLineInput, this.endOfLineOutput); this.commandExecutor.setQuirk(getQuirk()); /** */ Observable<Boolean> observable = Observable.create( new Observable.OnSubscribe<Boolean>() { @Override public void call(final Subscriber<? super Boolean> subscriber) { while (!subscriber.isUnsubscribed()) { /** poll the next possible command */ BasicCommand cmd = pollNextCommand(); if (cmd != null) { try { commandExecutor.execute(cmd); } catch (IOException e) { subscriber.onError(e); subscriber.unsubscribe(); } } try { byte[] response = commandExecutor.retrieveLatestResponse(); processResponse(response); if (hasEstablishedConnection()) { subscriber.onNext(true); subscriber.onCompleted(); } } catch (IOException e) { subscriber.onError(e); subscriber.unsubscribe(); } catch (StreamFinishedException e) { subscriber.onError(e); subscriber.unsubscribe(); } catch (InvalidCommandResponseException e) { LOGGER.warn(e.getMessage(), e); } catch (NoDataReceivedException e) { LOGGER.warn(e.getMessage(), e); } catch (UnmatchedResponseException e) { LOGGER.warn(e.getMessage(), e); } catch (AdapterSearchingException e) { LOGGER.warn(e.getMessage(), e); } } } }); return observable; } protected abstract boolean hasEstablishedConnection(); /** * an implementation can provide a quirk for response parsing/filtering * * @return a quirk that filters a line of raw data */ protected abstract ResponseQuirkWorkaround getQuirk(); protected Observable<DataResponse> createDataObservable() { Observable<DataResponse> dataObservable = Observable.create( new Observable.OnSubscribe<DataResponse>() { @Override public void call(Subscriber<? super DataResponse> subscriber) { while (!subscriber.isUnsubscribed()) { /** poll the next possible command */ BasicCommand cmd = pollNextCommand(); if (cmd != null) { try { commandExecutor.execute(cmd); } catch (IOException e) { subscriber.onError(e); subscriber.unsubscribe(); } } /** read the inputstream byte by byte */ try { byte[] bytes = commandExecutor.retrieveLatestResponse(); try { DataResponse result = processResponse(bytes); /** call our subscriber! */ if (result != null) { subscriber.onNext(result); if (LOGGER.isEnabled(Logger.DEBUG)) { if (result instanceof LambdaProbeVoltageResponse) { LOGGER.debug("Received lambda voltage: " + result); } } } } catch (AdapterSearchingException e) { LOGGER.warn("Adapter still searching: " + e.getMessage()); } catch (NoDataReceivedException e) { LOGGER.warn("No data received: " + e.getMessage()); } catch (InvalidCommandResponseException e) { LOGGER.warn("InvalidCommandResponseException: " + e.getMessage()); } catch (UnmatchedResponseException e) { LOGGER.warn("Unmatched response: " + e.getMessage()); } } catch (IOException e) { /** IOException signals broken connection, notify subscriber accordingly */ subscriber.onError(e); return; } catch (StreamFinishedException e) { /** the stream has ended, notify the subscriber */ LOGGER.info("The stream was closed: " + e.getMessage()); subscriber.onCompleted(); return; } } subscriber.onCompleted(); } }) .timeout(DEFAULT_NO_DATA_TIMEOUT, TimeUnit.MILLISECONDS); return dataObservable; } @Override public Observable<DataResponse> observe() { return createDataObservable(); } /** * an async adapter might want to send commands out irregularly or on startup. An implementation * can provided a command that should be executed using this method. * * <p>The returned command gets executed after a valid line of response has been read. * * @return the command or null if there is no pending */ protected abstract BasicCommand pollNextCommand(); /** * Parse a line of response * * @param bytes the bytes * @return a command instace * @throws InvalidCommandResponseException * @throws NoDataReceivedException * @throws UnmatchedResponseException * @throws AdapterSearchingException */ protected abstract DataResponse processResponse(byte[] bytes) throws InvalidCommandResponseException, NoDataReceivedException, UnmatchedResponseException, AdapterSearchingException; }