eclipse-agail / agile-core

Source code repository for agile core components
Eclipse Public License 2.0
7 stars 16 forks source link

Error when trying to connect to BLE device #81

Closed mohamed-elsabagh closed 6 years ago

mohamed-elsabagh commented 6 years ago

Hello, @cskiraly we are facing problem in using latest version of agile-stack v0.4.0 We are trying to register our BLE device GATT server, so we added it to the device factory, after we scan from the UI we can see it in the list of the devices, but when we try to connect we get the following errors

agile-devicefactory      | 09:17:03.302 [Thread-4] DEBUG o.e.a.devicefactory.DeviceFactoryImp - The Constructor was loaded
agile-devicefactory      | 09:17:03.303 [Thread-4] ERROR o.e.a.devicefactory.DeviceFactoryImp - Instantiation Exception occured
agile-devicefactory      | java.lang.InstantiationException: null
agile-devicefactory      |  at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
agile-devicefactory      |  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
agile-devicefactory      |  at org.eclipse.agail.devicefactory.DeviceFactoryImp.getDevice(DeviceFactoryImp.java:338)
agile-devicefactory      |  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
agile-devicefactory      |  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
agile-devicefactory      |  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
agile-devicefactory      |  at java.lang.reflect.Method.invoke(Method.java:498)
agile-devicefactory      |  at org.freedesktop.dbus.AbstractConnection$1.run(Unknown Source)
agile-devicefactory      |  at org.freedesktop.dbus.AbstractConnection$_workerthread.run(Unknown Source)
agile-devicemanager      | org.freedesktop.dbus.exceptions.DBusExecutionException: Error Executing Method org.eclipse.agail.DeviceFactory.getDevice: null
agile-devicemanager      |  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
agile-devicemanager      | 09:17:03.310 [Thread-4] ERROR o.e.a.devicemanager.DeviceManagerImp - Can not register device: Error Executing Method org.eclipse.agail.DeviceFactory.getDevice: null

Also we noticed in the UI list we can't see more than 3 devices in the list, if the nearby devices are more than 3 (we need to do more investigation on this point to make sure)

dpap commented 6 years ago

Hi Mohamed. Have you implemented a new driver for your device? Have you removed the "abstract" keyword from the definition of your class? That message comes up when DeviceFactory can't find an appropriate class for BLE device.

cskiraly commented 6 years ago

@dpap, good catch. The problem seems to be the abstract keyword, and I overlooked it in the private mail I've received. One more reason to use GitHib issues, as we've highlighted ;-)

mohamed-elsabagh commented 6 years ago

Hello @dpap what I did is I ported the HexiwearDevice.java class into a custom class of our own and here is the final file

/*******************************************************************************
 * Copyright (C) 2017 Create-Net / FBK.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Create-Net / FBK - initial API and implementation
 ******************************************************************************/
package org.eclipse.agail.device.instance;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.freedesktop.dbus.DBusSigHandler;
import org.freedesktop.dbus.exceptions.DBusException;
import org.eclipse.agail.Device;
import org.eclipse.agail.Protocol;
import org.eclipse.agail.Protocol.NewRecordSignal;
import org.eclipse.agail.device.base.AgileBLEDevice;
import org.eclipse.agail.device.base.SensorUuid;
import org.eclipse.agail.object.DeviceDefinition;
import org.eclipse.agail.object.DeviceOverview;
import org.eclipse.agail.object.RecordObject;
import org.eclipse.agail.object.DeviceComponent;
import org.eclipse.agail.exception.AgileNoResultException;

public abstract class ModosmartDevice extends AgileBLEDevice implements Device {
  protected Logger logger = LoggerFactory.getLogger(ModosmartDevice.class);
  protected static final Map<String, SensorUuid> sensors = new HashMap<String, SensorUuid>();
  private static final String Temperature = "Temperature";

    {
        subscribedComponents.put(Temperature, 0);
    }

    {
        profile.add(new DeviceComponent(Temperature, ""));
    }

    static {
        sensors.put(Temperature, new SensorUuid("000000ff-0000-1000-8000-00805f9b34fb", "0000ff01-0000-1000-8000-00805f9b34fb", "", ""));
    }

    public static boolean Matches(DeviceOverview d) {
        return d.name.contains("MODOSMART");
    }

    public static String deviceTypeName = "MODOSMART";

    public ModosmartDevice(DeviceOverview deviceOverview) throws DBusException {
        super(deviceOverview);
    }

    public ModosmartDevice(DeviceDefinition devicedefinition) throws DBusException {
        super(devicedefinition);
    }

    @Override
    public void Connect() throws DBusException {
        super.Connect();
        for (String componentName : subscribedComponents.keySet()) {
            logger.info("Modosmart Connect: " + componentName);
            //DeviceRead(componentName);
            if (subscribedComponents.get(componentName) > 0) {
                logger.info("Resubscribing to {}", componentName);
                deviceProtocol.Subscribe(address, getReadValueProfile(componentName));
            }
        }
    }

  @Override
  protected String DeviceRead(String componentName) {
    logger.info("Modosmart DeviceRead: "+ componentName);
    if ((protocol.equals(BLUETOOTH_LOW_ENERGY)) && (deviceProtocol != null)) {
      if (isConnected()) {
        if (isSensorSupported(componentName.trim())) {
          try {
            byte[] result = deviceProtocol.Read(address, getReadValueProfile(componentName));
            return formatReading(componentName, result);
          } catch (DBusException e) {
            e.printStackTrace();
          }
        } else {
          throw new AgileNoResultException("Sensor not supported:" + componentName);
        }
      } else {
        throw new AgileNoResultException("BLE Device not connected: " + deviceName);
      }
    } else {
      throw new AgileNoResultException("Protocol not supported: " + protocol);
    }
    throw new AgileNoResultException("Unable to read "+componentName);
  }

 @Override
  public synchronized void Subscribe(String componentName) {
    if ((protocol.equals(BLUETOOTH_LOW_ENERGY)) && (deviceProtocol != null)) {
      if (isConnected()) {
        if (isSensorSupported(componentName.trim())) {
          try {
            if (!hasOtherActiveSubscription(componentName)) {
              deviceProtocol.Subscribe(address, getReadValueProfile(componentName));
              addNewRecordSignalHandler();
            }
        logger.info("Modosmart Subscribe");
            subscribedComponents.put(componentName, subscribedComponents.get(componentName) + 1);
          } catch (Exception e) {
            e.printStackTrace();
          }
        } else {
          throw new AgileNoResultException("Sensor not supported:" + componentName);
        }
      } else {
        throw new AgileNoResultException("BLE Device not connected: " + deviceName);
      }
    } else {
      throw new AgileNoResultException("Protocol not supported: " + protocol);
    }
  }

 @Override
  public synchronized void Unsubscribe(String componentName) throws DBusException {
    if ((protocol.equals(BLUETOOTH_LOW_ENERGY)) && (deviceProtocol != null)) {
      if (isConnected()) {
        if (isSensorSupported(componentName.trim())) {
          try {
            subscribedComponents.put(componentName, subscribedComponents.get(componentName) - 1);
            if (!hasOtherActiveSubscription(componentName)) {
              deviceProtocol.Unsubscribe(address, getReadValueProfile(componentName));
              removeNewRecordSignalHandler();
            }
          } catch (Exception e) {
            e.printStackTrace();
          }
        } else {
          throw new AgileNoResultException("Sensor not supported:" + componentName);
        }
      } else {
        throw new AgileNoResultException("BLE Device not connected: " + deviceName);
      }
    } else {
      throw new AgileNoResultException("Protocol not supported: " + protocol);
    }
  }

@Override
  public void Write(String componentName, String payload) {
            logger.debug("Device. Write not implemented");
    }

@Override
  public void Execute(String command) {
            logger.debug("Device. Execute not implemented");
    }

  @Override
  public List<String> Commands(){
            logger.debug("Device. Commands not implemented");
            return null;
      }

    // =======================Utility methods===========================

    private Map<String, String> getReadValueProfile(String sensorName) {
        Map<String, String> profile = new HashMap<String, String>();
        SensorUuid s = sensors.get(sensorName);
        if (s != null) {
            profile.put(GATT_SERVICE, s.serviceUuid);
            profile.put(GATT_CHARACTERSTICS, s.charValueUuid);
            logger.info("Modosmart Gatt Service: "+s.serviceUuid);
            logger.info("Modosmart Gatt Characteristic: "+s.charValueUuid);
        }
        return profile;
    }

    @Override
    protected boolean isSensorSupported(String sensorName) {
        return sensors.containsKey(sensorName);
    }

    /**
     * Checks if there is another active subscription on the given component of
     * the device
     *
     * @param componentName
     * @return
     */
    @Override
    protected boolean hasOtherActiveSubscription(String componentName) {
        for (String component : subscribedComponents.keySet()) {
            if (subscribedComponents.get(component) > 0) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected String formatReading(String componentName, byte[] readData) {
            int resultX = 0;
            int resultY = 0;
            int resultZ = 0;
            String value = "";
        switch (componentName) {
           case Temperature:
        }
        return "0";
    }

    /**

     */
    @SuppressWarnings("unchecked")
    @Override
    protected void addNewRecordSignalHandler() {
        logger.info("Modosmart addNewRecordSignalHandler");
        try {
            if (newRecordSigHanlder == null && connection != null) {
                newRecordSigHanlder = new DBusSigHandler<Protocol.NewRecordSignal>() {
                    @Override
                    public void handle(NewRecordSignal sig) {
                        if (sig.address.equals(address)) {
                            for(String componentName : getComponentNames(sig.profile)){
                                String readVal = formatReading(componentName, sig.record);
                                if (Float.parseFloat(readVal) != 0.0) {
                                    RecordObject recObj = new RecordObject(deviceID, componentName,
                                            formatReading(componentName, sig.record), getMeasurementUnit(componentName), "",
                                            System.currentTimeMillis());
                                    data = recObj;
                                    logger.info("Device notification component {} value {}", componentName, recObj.value);
                                    lastReadStore.put(componentName, recObj);
                                    try {
                                        Device.NewSubscribeValueSignal newRecordSignal = new Device.NewSubscribeValueSignal(
                                                AGILE_NEW_RECORD_SUBSCRIBE_SIGNAL_PATH, recObj);
                                        connection.sendSignal(newRecordSignal);
                                    } catch (DBusException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                };
                connection.addSigHandler(Protocol.NewRecordSignal.class, newRecordSigHanlder);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Given the profile of the component returns the list of component names
     *
     * @param uuid
     * @return
     */
    protected List<String> getComponentNames(Map<String, String> profile) {
        List<String> ret = new ArrayList<String>();
        String serviceUUID = profile.get(GATT_SERVICE);
        String charValueUuid = profile.get(GATT_CHARACTERSTICS);
        for (Entry<String, SensorUuid> su : sensors.entrySet()) {
            if (su.getValue().serviceUuid.equals(serviceUUID) && su.getValue().charValueUuid.equals(charValueUuid)) {
                ret.add(su.getKey());
            }
        }
        return ret;
    }

    @Override
    protected String getMeasurementUnit(String sensor) {
        return "";
    }

  /**
   * Given the profile of the component returns the name of the sensor
   *
   * @param uuid
   * @return
   */
  @Override
  protected String getComponentName(Map<String, String> profile) {
    String serviceUUID = profile.get(GATT_SERVICE);
    String charValueUuid = profile.get(GATT_CHARACTERSTICS);
    for (Entry<String, SensorUuid> su : sensors.entrySet()) {
      if (su.getValue().serviceUuid.equals(serviceUUID) && su.getValue().charValueUuid.equals(charValueUuid)) {
        return su.getKey();
      }
    }
    return null;
  }
}

that's the only change I did, then run the docker-compose

Actually I found it's abstract class, thanks, let me test after removing this

cskiraly commented 6 years ago

"public abstract class ModosmartDevice extends AgileBLEDevice implements Device { ..."

The problems however stems from our templates, which we should correct. Sorry for that!

mohamed-elsabagh commented 6 years ago

@dpap Confirmed that the abstract class was the issue, thanks so much I owe you a cup of coffee