package com.sonycsl.Kadecot.wamp.echonetlite;
import com.sonycsl.Kadecot.call.ErrorResponse;
import com.sonycsl.Kadecot.core.Dbg;
import com.sonycsl.Kadecot.device.AccessException;
import com.sonycsl.Kadecot.device.DeviceProperty;
import com.sonycsl.Kadecot.device.echo.MyController;
import com.sonycsl.Kadecot.device.echo.MyNodeProfile;
import com.sonycsl.echo.Echo;
import com.sonycsl.echo.EchoFrame;
import com.sonycsl.echo.EchoProperty;
import com.sonycsl.echo.EchoSocket;
import com.sonycsl.echo.EchoUtils;
import com.sonycsl.echo.eoj.EchoObject;
import com.sonycsl.echo.eoj.device.DeviceObject;
import com.sonycsl.echo.eoj.device.housingfacilities.PowerDistributionBoardMetering;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ECHONETLiteManager {
public static final String PROTOCOL_TYPE_ECHO = "echonetlite";
private static ECHONETLiteManager sInstance = null;
private final MyNodeProfile mNodeProfile;
private final MyController mController;
private final Map<String, Callback> mCallbacks;
static final long CALLBACK_TIME_OUT = 1000 * 10;
static final long ACCESS_INTERVAL_TIME = 0;
private final Map<String, ECHONETLiteDeviceGenerator> mGenerators;
private final ECHONETLiteDiscovery mEchoDiscovery;
private ECHONETLiteClient mClient;
private final Map<InetAddress, Long> mLastAccessTimes;
private ECHONETLiteWampDevicePropertyChangedListener mListener;
public interface ECHONETLiteWampDevicePropertyChangedListener {
public void OnPropertyChanged(ECHONETLiteDeviceData data, List<DeviceProperty> list);
}
private ECHONETLiteManager() {
mNodeProfile = new MyNodeProfile();
mController = new MyController();
mCallbacks = new ConcurrentHashMap<String, Callback>();
mGenerators = new ConcurrentHashMap<String, ECHONETLiteDeviceGenerator>();
mEchoDiscovery = new ECHONETLiteDiscovery();
mLastAccessTimes = new ConcurrentHashMap<InetAddress, Long>();
setup();
}
public static synchronized ECHONETLiteManager getInstance() {
if (sInstance == null) {
sInstance = new ECHONETLiteManager();
}
return sInstance;
}
public void setClient(ECHONETLiteClient client) {
mClient = client;
}
public void setListener(ECHONETLiteWampDevicePropertyChangedListener pListener,
ECHONETLiteDiscovery.OnEchoDeviceInfoListener dListener) {
mListener = pListener;
mEchoDiscovery.setListener(dListener);
}
private void setup() {
Echo.addEventListener(new Echo.EventListener() {
@Override
public void onNewDeviceObject(DeviceObject device) {
mEchoDiscovery.onDiscover(device);
}
@Override
public void receiveEvent(EchoFrame frame) {
// System.err.println(frame);
EchoObject eoj =
Echo.getNode(frame.getSrcEchoAddress()).getInstance(
frame.getSrcEchoClassCode(), frame.getSrcEchoInstanceCode());
EchoProperty[] properties = frame.getProperties();
short tid = frame.getTID();
final String callbackId = getCallbackId(tid, eoj);
switch (frame.getESV()) {
case EchoFrame.ESV_SET_RES:
case EchoFrame.ESV_SETI_SNA:
case EchoFrame.ESV_SETC_SNA:
case EchoFrame.ESV_GET_RES:
case EchoFrame.ESV_GET_SNA:
Dbg.print("receive:" + callbackId);
Callback callback = mCallbacks.get(callbackId);
// synchronized(EchoSocket.class) {
if (callback != null) {
Dbg.print("callback:" + callbackId);
mCallbacks.remove(callbackId);
callback.run(properties);
}
// }
onReceiveInformerFrame(eoj, properties);
break;
case EchoFrame.ESV_INF:
case EchoFrame.ESV_INF_SNA:
case EchoFrame.ESV_INFC:
onReceiveInformerFrame(eoj, properties);
break;
}
}
private ECHONETLiteDeviceData getDeviceData(EchoObject eoj) {
Map<Long, ECHONETLiteDeviceData> devices = mClient.getDeviceMap();
for (ECHONETLiteDeviceData data : devices.values()) {
if (data.getInstanceCode() == eoj.getInstanceCode()) {
return data;
}
}
return null;
}
public void onReceiveInformerFrame(EchoObject eoj, EchoProperty[] properties) {
ECHONETLiteDeviceData data = getDeviceData(eoj);
if (data == null) {
return;
}
List<DeviceProperty> list = new ArrayList<DeviceProperty>();
for (EchoProperty p : properties) {
boolean success = p.edt != null;
DeviceProperty prop =
new DeviceProperty(toPropertyName(p.epc),
success ? toPropertyValue(p.edt)
: null, success);
list.add(prop);
}
if (mListener != null) {
mListener.OnPropertyChanged(data, list);
}
}
@Override
public void onGetProperty(EchoObject eoj, short tid, byte esv, EchoProperty property,
boolean success) {
super.onGetProperty(eoj, tid, esv, property, success);
if (success && (property.epc == DeviceObject.EPC_GET_PROPERTY_MAP)) {
byte[] properties = EchoUtils.propertyMapToProperties(property.edt);
HashSet<DeviceProperty> watchingPropertySet = new HashSet<DeviceProperty>();
switch (eoj.getEchoClassCode()) {
case PowerDistributionBoardMetering.ECHO_CLASS_CODE:
watchingPropertySet
.add(new DeviceProperty(
toPropertyName(PowerDistributionBoardMetering.EPC_MEASURED_CUMULATIVE_AMOUNT_OF_ELECTRIC_ENERGY_NORMAL_DIRECTION)));
watchingPropertySet
.add(new DeviceProperty(
toPropertyName(PowerDistributionBoardMetering.EPC_MEASURED_CUMULATIVE_AMOUNT_OF_ELECTRIC_ENERGY_REVERSE_DIRECTION)));
watchingPropertySet
.add(new DeviceProperty(
toPropertyName(PowerDistributionBoardMetering.EPC_UNIT_FOR_CUMULATIVE_AMOUNTS_OF_ELECTRIC_ENERGY)));
for (byte p : properties) {
int i = p & 0xFF;
if (i >= (PowerDistributionBoardMetering.EPC_MEASUREMENT_CHANNEL1 & 0xFF)
&& i <= (PowerDistributionBoardMetering.EPC_MEASUREMENT_CHANNEL32 & 0xFF)) {
watchingPropertySet.add(new DeviceProperty(toPropertyName(p)));
}
}
ECHONETLiteDeviceData data = getDeviceData(eoj);
if (data != null) {
// long delay =
// (Logger.DEFAULT_INTERVAL_MILLS)
// - (System.currentTimeMillis() %
// (Logger.DEFAULT_INTERVAL_MILLS));
// TODO: logger
// Logger.getInstance(mContext).watch(data.nickname,
// watchingPropertySet,
// Logger.DEFAULT_INTERVAL_MILLS, delay);
}
break;
}
}
}
});
}
public synchronized void start() {
if (Echo.getSelfNode() != null) {
try {
Echo.restart();
} catch (IOException e) {
e.printStackTrace();
}
return;
} else {
ArrayList<DeviceObject> deviceList = new ArrayList<DeviceObject>();
deviceList.add(mController);
for (String protocolName : mGenerators.keySet()) {
ECHONETLiteDeviceGenerator gen = mGenerators.get(protocolName);
List<ECHONETLiteDeviceData> agentDataList = new ArrayList<ECHONETLiteDeviceData>(
mClient.getDeviceMap()
.values());
gen.onInitGenerator(agentDataList);
for (ECHONETLiteDeviceData data : agentDataList) {
ECHONETLiteDeviceAgent agent = new ECHONETLiteDeviceAgent(data, gen);
deviceList.add(agent);
}
}
try {
Echo.start(mNodeProfile, deviceList.toArray(new DeviceObject[] {}));
} catch (IOException e) {
e.printStackTrace();
}
}
mEchoDiscovery.startDiscovering();
}
public synchronized void stop() {
try {
Echo.stop();
} catch (IOException e) {
e.printStackTrace();
}
}
public synchronized void refreshDeviceList() {
mEchoDiscovery.clearActiveDevices();
mEchoDiscovery.startDiscovering();
}
public synchronized void deleteAllDeviceData() {
try {
Echo.stop();
Echo.clear();
} catch (IOException e) {
e.printStackTrace();
}
mEchoDiscovery.clearActiveDevices();
// getEchoDeviceDatabase().deleteAllDeviceData();
for (String protocolName : mGenerators.keySet()) {
mGenerators.get(protocolName).onDeleteAllEchoDevice();
}
setup();
}
// public String getCallbackId(short tid, final EchoObject eoj, final byte
// epc) {
// return tid+","+eoj.getNode().getAddress()+","+eoj.getEchoObjectCode();
// }
private String getCallbackId(short tid, final EchoObject eoj) {
return tid + "," + eoj.getNode().getAddress() + "," + eoj.getEchoObjectCode();
}
private class Callback {
public volatile EchoProperty[] properties = null;
public void run(EchoProperty[] properties) {
this.properties = properties;
}
}
public static String toPropertyName(byte epc) {
return "0x" + EchoUtils.toHexString(epc);
}
private static JSONArray toPropertyValue(byte[] edt) {
JSONArray edtAry = new JSONArray();
if (edt != null) {
for (int i = 0; i < edt.length; i++) {
edtAry.put(edt[i] & 0xff);
}
}
return edtAry;
}
// TODO:
public int getAllowedPermissionLevel() {
return 1;
}
public static JSONObject convertPropertyAsJSON(String nickname, String propertyName,
Object propertyData) {
JSONObject jsonObj = new JSONObject();
try {
jsonObj.put("nickname", nickname);
jsonObj.put("property", propertyName);
jsonObj.put("data", propertyData);
} catch (JSONException e) {
e.printStackTrace();
}
return jsonObj;
}
public List<DeviceProperty> set(EchoObject eoj, List<DeviceProperty> propertyList)
throws AccessException {
if (eoj == null) {
throw new AccessException(new ErrorResponse(ErrorResponse.INVALID_PARAMS_CODE,
"Not found echo object"));
}
ArrayList<EchoProperty> list = new ArrayList<EchoProperty>();
try {
for (DeviceProperty p : propertyList) {
byte epc = Integer.decode(p.name).byteValue();
if (p.value instanceof Integer) {
JSONArray ja = new JSONArray();
ja.put(((Integer) p.value).intValue());
p.value = ja;
}
if (!(p.value instanceof JSONArray)) {
throw new AccessException(new ErrorResponse(ErrorResponse.INVALID_PARAMS_CODE));
}
JSONArray value = (JSONArray) p.value;
byte[] edt = new byte[value.length()];
for (int i = 0; i < value.length(); i++) {
edt[i] = (byte) value.getInt(i);
}
list.add(new EchoProperty(epc, edt));
}
} catch (Exception e) {
throw new AccessException(new ErrorResponse(ErrorResponse.INVALID_PARAMS_CODE, e));
}
waitForAccess(eoj.getNode().getAddress());
return setProperty(eoj, list);
}
private List<DeviceProperty> setProperty(EchoObject eoj, List<EchoProperty> propertyList)
throws AccessException {
try {
final Thread current = Thread.currentThread();
Callback callback = new Callback() {
@Override
public void run(EchoProperty[] properties) {
super.run(properties);
current.interrupt();
}
};
HashMap<Byte, byte[]> map = new HashMap<Byte, byte[]>();
// send
// synchronized(EchoSocket.class) {
EchoObject.Setter setter = eoj.set();
for (EchoProperty p : propertyList) {
setter.reqSetProperty(p.epc, p.edt);
map.put((Byte) p.epc, p.edt);
}
String id = send(eoj, setter, callback);
/*
* short nextTid = EchoSocket.getNextTIDNoIncrement(); id =
* getCallbackId(nextTid, eoj); mCallbacks.put(id, callback); short
* tid = setter.send(); if (nextTid != tid) { mCallbacks.remove(id);
* Dbg.print("fault"); throw new AccessException(new ErrorResponse(
* ErrorResponse.INTERNAL_ERROR_CODE, "fault")); }
*/
// }
// sleep
try {
Dbg.print("send:" + id);
Thread.sleep(CALLBACK_TIME_OUT);
} catch (InterruptedException e) {
}
if (mCallbacks.containsKey(id)) {
mCallbacks.remove(id);
// timeout
Dbg.print("ECHONET Lite Timeout:" + id);
throw new AccessException(new ErrorResponse(ErrorResponse.INTERNAL_ERROR_CODE,
"ECHONET Lite Timeout"));
} else {
if (callback.properties != null) {
List<DeviceProperty> list = new ArrayList<DeviceProperty>();
for (EchoProperty p : callback.properties) {
DeviceProperty prop =
new DeviceProperty(toPropertyName(p.epc), toPropertyValue(map
.get(p.epc)), p.edt == null);
list.add(prop);
}
return list;
} else {
throw new AccessException(new ErrorResponse(ErrorResponse.INTERNAL_ERROR_CODE));
}
}
} catch (IOException e) {
e.printStackTrace();
throw new AccessException(new ErrorResponse(ErrorResponse.INTERNAL_ERROR_CODE, e));
}
}
public List<DeviceProperty> get(EchoObject eoj, List<DeviceProperty> propertyList)
throws AccessException {
if (eoj == null) {
throw new AccessException(new ErrorResponse(ErrorResponse.INVALID_PARAMS_CODE,
"Not found echo object"));
}
ArrayList<Byte> list = new ArrayList<Byte>();
try {
for (DeviceProperty dp : propertyList) {
byte epc = Integer.decode(dp.name).byteValue();
list.add(epc);
}
} catch (Exception e) {
throw new AccessException(new ErrorResponse(ErrorResponse.INVALID_PARAMS_CODE, e));
}
waitForAccess(eoj.getNode().getAddress());
return getProperty(eoj, list);
}
private void waitForAccess(InetAddress address) {
long currentTime = System.currentTimeMillis();
Long time = mLastAccessTimes.get(address);
long lastAccessTime;
if (time == null) {
lastAccessTime = 0;
} else {
lastAccessTime = time;
}
long interval = currentTime - lastAccessTime;
if (interval < ACCESS_INTERVAL_TIME) {
Dbg.print("waitForAccess:" + (ACCESS_INTERVAL_TIME - interval));
try {
Thread.sleep(ACCESS_INTERVAL_TIME - interval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mLastAccessTimes.put(address, System.currentTimeMillis());
}
private List<DeviceProperty> getProperty(EchoObject eoj, List<Byte> epcList)
throws AccessException {
try {
final Thread current = Thread.currentThread();
Callback callback = new Callback() {
@Override
public void run(EchoProperty[] properties) {
super.run(properties);
current.interrupt();
}
};
// send
// synchronized(EchoSocket.class) {
EchoObject.Getter getter = eoj.get();
for (Byte b : epcList) {
getter.reqGetProperty(b);
}
String id = send(eoj, getter, callback);
/*
* short nextTid = EchoSocket.getNextTIDNoIncrement(); id =
* getCallbackId(nextTid, eoj); mCallbacks.put(id, callback); short
* tid = getter.send(); if (nextTid != tid) { mCallbacks.remove(id);
* Dbg.print("fault"); throw new AccessException(new ErrorResponse(
* ErrorResponse.INTERNAL_ERROR_CODE, "fault")); }
*/
// }
// sleep
try {
Dbg.print("send:" + id + "," + eoj.getClass().getSimpleName());
Thread.sleep(CALLBACK_TIME_OUT);
} catch (InterruptedException e) {
}
if (mCallbacks.containsKey(id)) {
mCallbacks.remove(id);
// timeout
Dbg.print("ECHONET Lite Timeout:" + id);
throw new AccessException(new ErrorResponse(ErrorResponse.INTERNAL_ERROR_CODE,
"ECHONET Lite Timeout"));
} else {
if (callback.properties != null) {
List<DeviceProperty> list = new ArrayList<DeviceProperty>();
for (EchoProperty p : callback.properties) {
String epc = toPropertyName(p.epc);
ECHONETLitePropertyName name = ECHONETLitePropertyName
.getPropertyNameFromEpc(epc);
DeviceProperty prop = new DeviceProperty(name.toString());
if (p.edt != null) {
prop.value = toPropertyValue(p.edt);
} else {
prop.value = null;
}
prop.success = (p.edt != null);
list.add(prop);
}
return list;
} else {
throw new AccessException(new ErrorResponse(ErrorResponse.INTERNAL_ERROR_CODE));
}
}
} catch (IOException e) {
e.printStackTrace();
throw new AccessException(new ErrorResponse(ErrorResponse.INTERNAL_ERROR_CODE, e));
}
}
private synchronized String send(EchoObject eoj, Object sender, Callback callback)
throws AccessException, IOException {
short nextTid = EchoSocket.getNextTIDNoIncrement();
String id = getCallbackId(nextTid, eoj);
mCallbacks.put(id, callback);
short tid = (short) (nextTid - 1);
if (sender instanceof EchoObject.Setter) {
tid = ((EchoObject.Setter) sender).send().getTID();
} else if (sender instanceof EchoObject.Getter) {
tid = ((EchoObject.Getter) sender).send().getTID();
}
if (nextTid != tid) {
mCallbacks.remove(id);
Dbg.print("fault");
throw new AccessException(new ErrorResponse(ErrorResponse.INTERNAL_ERROR_CODE, "fault"));
}
return id;
}
public void addEchoDeviceGenerator(ECHONETLiteDeviceGenerator generator) {
mGenerators.put(generator.getProtocolName(), generator);
}
}