All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sputnikdev.bluetooth.manager.impl.CombinedCharacteristicGovernorImpl Maven / Gradle / Ivy

package org.sputnikdev.bluetooth.manager.impl;

/*-
 * #%L
 * org.sputnikdev:bluetooth-manager
 * %%
 * Copyright (C) 2017 Sputnik Dev
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sputnikdev.bluetooth.URL;
import org.sputnikdev.bluetooth.manager.BluetoothGovernor;
import org.sputnikdev.bluetooth.manager.BluetoothObjectType;
import org.sputnikdev.bluetooth.manager.BluetoothObjectVisitor;
import org.sputnikdev.bluetooth.manager.CharacteristicGovernor;
import org.sputnikdev.bluetooth.manager.CombinedGovernor;
import org.sputnikdev.bluetooth.manager.DeviceGovernor;
import org.sputnikdev.bluetooth.manager.GovernorListener;
import org.sputnikdev.bluetooth.manager.ManagerListener;
import org.sputnikdev.bluetooth.manager.NotReadyException;
import org.sputnikdev.bluetooth.manager.ValueListener;
import org.sputnikdev.bluetooth.manager.transport.CharacteristicAccessType;

import java.time.Instant;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;

/**
 *
 * @author Vlad Kolotov
 */
class CombinedCharacteristicGovernorImpl
        implements CharacteristicGovernor, BluetoothObjectGovernor, CombinedGovernor {

    private Logger logger = LoggerFactory.getLogger(CombinedCharacteristicGovernorImpl.class);

    private BluetoothManagerImpl bluetoothManager;
    private final URL url;
    private CharacteristicGovernor delegate;
    private final List valueListeners = new CopyOnWriteArrayList<>();
    private final List governorListeners = new CopyOnWriteArrayList<>();
    private final CompletableFutureService readyService =
            new CompletableFutureService<>(this, BluetoothGovernor::isReady);
    private Instant lastInteracted;
    private Instant lastNotified;
    private final ManagerListener delegateListener = new DelegatesListener();

    CombinedCharacteristicGovernorImpl(BluetoothManagerImpl bluetoothManager, URL url) {
        this.bluetoothManager = bluetoothManager;
        this.url = url;
    }

    @Override
    public Set getFlags() throws NotReadyException {
        return getDelegate().getFlags();
    }

    @Override
    public boolean isReadable() throws NotReadyException {
        return getDelegate().isReadable();
    }

    @Override
    public boolean isWritable() throws NotReadyException {
        return getDelegate().isWritable();
    }

    @Override
    public boolean isNotifiable() throws NotReadyException {
        return getDelegate().isNotifiable();
    }

    @Override
    public boolean isNotifying() throws NotReadyException {
        return getDelegate().isNotifying();
    }

    @Override
    public byte[] read() throws NotReadyException {
        return getDelegate().read();
    }

    @Override
    public boolean write(byte[] data) throws NotReadyException {
        return getDelegate().write(data);
    }

    @Override
    public void addValueListener(ValueListener valueListener) {
        synchronized (delegateListener) {
            valueListeners.add(valueListener);
            if (delegate != null) {
                delegate.addValueListener(valueListener);
            }
        }
    }

    @Override
    public void removeValueListener(ValueListener valueListener) {
        synchronized (delegateListener) {
            valueListeners.remove(valueListener);
            if (delegate != null) {
                delegate.removeValueListener(valueListener);
            }
        }
    }

    @Override
    public URL getURL() {
        return url;
    }

    @Override
    public boolean isReady() {
        return delegate != null && delegate.isReady();
    }

    @Override
    public BluetoothObjectType getType() {
        return BluetoothObjectType.CHARACTERISTIC;
    }

    @Override
    public Instant getLastInteracted() {
        return delegate != null ? delegate.getLastInteracted() : lastInteracted;
    }

    @Override
    public Instant getLastNotified() {
        return delegate != null ? delegate.getLastNotified() : lastNotified;
    }

    @Override
    public void accept(BluetoothObjectVisitor visitor) throws Exception {
        if (delegate != null) {
            visitor.visit(delegate);
        }
    }

    @Override
    public void addGovernorListener(GovernorListener listener) {
        synchronized (delegateListener) {
            governorListeners.add(listener);
            if (delegate != null) {
                delegate.addGovernorListener(listener);
            }
        }
    }

    @Override
    public void removeGovernorListener(GovernorListener listener) {
        synchronized (delegateListener) {
            governorListeners.remove(listener);
            if (delegate != null) {
                delegate.removeGovernorListener(listener);
            }
        }
    }

    @Override
    public void init() {
        bluetoothManager.addManagerListener(delegateListener);

        bluetoothManager.getRegisteredGovernors().stream()
                .filter(registeredURL -> !COMBINED_ADDRESS.equals(registeredURL.getAdapterAddress())
                        && registeredURL.copyWithProtocol(null).copyWithAdapter(COMBINED_ADDRESS).equals(url))
                .map(registeredURL -> bluetoothManager.getGovernor(registeredURL))
                .filter(BluetoothGovernor::isReady)
                .reduce((a, b) -> { throw new IllegalStateException("multiple 'ready' characteristics found"); })
                .ifPresent(governor -> installDelegate((CharacteristicGovernor) governor));
    }

    @Override
    public void update() {
        if (delegate == null) {
            // try to find and register delegate
            bluetoothManager.getRegisteredGovernors().stream()
                    .filter(this::targetDevice)
                    .forEach(this::registerTargetCharacteristic);
            // do nothing else, bluetooth manager takes care of installing the delegate through the ManagerListener
        }
    }

    @Override
    public void reset() {
        uninstallDelegate();
    }

    @Override
    public void dispose() {
        bluetoothManager.removeManagerListener(delegateListener);
        reset();
        governorListeners.clear();
        valueListeners.clear();
        readyService.clear();
    }

    @Override
    @SuppressWarnings({"unchecked", "rawtypes"})
    public  CompletableFuture whenReady(Function function) {
        return readyService.submit((Function) function);
    }

    private void installDelegate(CharacteristicGovernor delegate) {
        synchronized (delegateListener) {
            this.delegate = delegate;
            governorListeners.forEach(delegate::addGovernorListener);
            valueListeners.forEach(delegate::addValueListener);
            lastInteracted = delegate.getLastInteracted();
            lastNotified = delegate.getLastNotified();
        }
        bluetoothManager.notify(() -> {
            if (delegate.isReady()) {
                BluetoothManagerUtils.forEachSilently(governorListeners, GovernorListener::ready, true, logger,
                        "Execution error of a governor listener: ready");
                readyService.completeSilently();
            }
            BluetoothManagerUtils.forEachSilently(governorListeners, GovernorListener::lastUpdatedChanged,
                    lastInteracted, logger,"Execution error of a governor listener: lastUpdatedChanged");
        });
    }

    private void uninstallDelegate() {
        CharacteristicGovernor delegate = this.delegate;
        if (delegate != null) {
            synchronized (delegateListener) {
                governorListeners.forEach(delegate::removeGovernorListener);
                valueListeners.forEach(delegate::removeValueListener);
                lastInteracted = delegate.getLastInteracted();
                lastNotified = delegate.getLastNotified();
            }
        }
        this.delegate = null;
    }

    private CharacteristicGovernor getDelegate() {
        CharacteristicGovernor delegate = this.delegate;
        if (delegate != null) {
            return delegate;
        }
        throw new NotReadyException("Combined characteristic governor is not ready yet");
    }

    private void registerTargetCharacteristic(URL deviceURL) {
        bluetoothManager.getCharacteristicGovernor(url.copyWithAdapter(deviceURL.getAdapterAddress()));
    }

    private boolean targetDevice(URL registeredURL) {
        return registeredURL.isDevice()
                && !COMBINED_ADDRESS.equalsIgnoreCase(registeredURL.getAdapterAddress())
                && registeredURL.getDeviceAddress().equalsIgnoreCase(url.getDeviceAddress())
                && ((DeviceGovernor) bluetoothManager.getGovernor(registeredURL)).isServicesResolved();
    }

    private class DelegatesListener implements ManagerListener {
        @Override
        public void ready(BluetoothGovernor governor, boolean isReady) {
            if (governor instanceof CharacteristicGovernorImpl
                    && governor.getURL().copyWithProtocol(null).copyWithAdapter(COMBINED_ADDRESS).equals(url)) {
                if (isReady) {
                    installDelegate((CharacteristicGovernor) governor);
                } else {
                    uninstallDelegate();
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy