/*
* Copyright (C) 2013-2014 Sony Computer Science Laboratories, Inc. All Rights Reserved.
* Copyright (C) 2014 Sony Corporation. All Rights Reserved.
*/
package com.sonycsl.Kadecot.log;
import android.content.Context;
import com.sonycsl.Kadecot.device.DeviceData;
import com.sonycsl.Kadecot.device.DeviceDatabase;
import com.sonycsl.Kadecot.device.DeviceInfo;
import com.sonycsl.Kadecot.device.DeviceManager;
import com.sonycsl.Kadecot.device.DeviceProperty;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// フォーマットの仕様は LTSV
// 日ごとに1ファイル
// ファイル名 : {year}_{month}_{day}.log, everyday.log
// センサログ(get:successアリ)と機器操作ログ(set:successアリ)
//
// 使用電力計測・取得間隔 30分間隔以内
// データ蓄積期間(表示できることを前提とする) 1時間以内の単位 1ケ月以上
// 1日以内の単位 13ヶ月以上
//
// ● 蓄積データ:
// ・1時間単位の積算電力量、最大2年間
// 測定データ項目・測定間隔:
// ・電力(W)5秒間隔 積算電力量(Wh)5秒間隔
//
// 30分以内(1ヶ月過ぎた分は消去)と一日以内(無限?)のファイルは別にいる.
public class Logger {
protected final Context mContext;
private static Logger sInstance = null;
private static final String VERSION = "0.1";
private static final String LABEL_VERSION = "version";
private static final String LABEL_UNIXTIME = "unixtime";
private static final String LABEL_NICKNAME = "nickname";
private static final String LABEL_DEVICE_TYPE = "device_type";
private static final String LABEL_PROTOCOL = "protocol";
private static final String LABEL_ACCESS_TYPE = "access_type"; // set or
// get(&
// inform)
private static final String LABEL_PROPERTY_NAME = "property_name";
private static final String LABEL_PROPERTY_VALUE = "property_value";
private static final String LABEL_SUCCESS = "success";
private static final String LABEL_MESSAGE = "message";
public static final String ACCESS_TYPE_SET = "set";
public static final String ACCESS_TYPE_GET = "get";
public static final long DEFAULT_INTERVAL_MILLS = 60 * 1000 * 30;
// public static final String LABEL_USER = "user";
protected final HashMap<Long, Watching> mWatchedDevices;
private HashMap<String, LinkedHashMap<String, String>> mLatestValidAccessDataCache;
private Logger(Context context) {
mContext = context.getApplicationContext();
mWatchedDevices = new HashMap<Long, Watching>();
mLatestValidAccessDataCache = new HashMap<String, LinkedHashMap<String, String>>();
}
public static synchronized Logger getInstance(Context context) {
if (sInstance == null) {
sInstance = new Logger(context);
}
return sInstance;
}
public void watch(String nickname, HashSet<DeviceProperty> propertySet) {
watch(nickname, propertySet, DEFAULT_INTERVAL_MILLS);
}
public void watch(String nickname, HashSet<DeviceProperty> propertySet, long intervalMills) {
watch(nickname, propertySet, intervalMills, 0);
}
public void watch(String nickname, HashSet<DeviceProperty> propertySet, long intervalMills,
long delayMills) {
// 定期的にgetする
DeviceData data = DeviceDatabase.getInstance(mContext).getDeviceData(nickname);
if (data == null) {
return;
}
watch(data.deviceId, propertySet, intervalMills, delayMills);
}
public synchronized void watch(long deviceId, HashSet<DeviceProperty> propertySet,
long intervalMills, final long delayMills) {
Watching watching;
if (mWatchedDevices.containsKey(deviceId)) {
watching = mWatchedDevices.get(deviceId);
for (DeviceProperty p : propertySet) {
watching.propertySet.add(p);
}
} else {
watching = new Watching(deviceId, propertySet);
mWatchedDevices.put(deviceId, watching);
}
watching.intervalMills = intervalMills;
final Watching w = watching;
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(delayMills);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
w.start();
}
});
}
public void unwatch(String nickname, HashSet<String> propertyNameSet) {
DeviceData data = DeviceDatabase.getInstance(mContext).getDeviceData(nickname);
if (data == null) {
return;
}
unwatch(data.deviceId, propertyNameSet);
}
public void unwatch(long deviceId, HashSet<String> propertyNameSet) {
if (mWatchedDevices.containsKey(deviceId)) {
Watching watching = mWatchedDevices.get(deviceId);
for (String p : propertyNameSet) {
watching.propertySet.remove(p);
}
}
}
public void unwatchAll() {
Object[] keys = mWatchedDevices.keySet().toArray();
for (Object k : keys) {
Watching w = mWatchedDevices.get(k);
mWatchedDevices.remove(k);
w.stop();
}
mWatchedDevices.clear();
}
public void unwatch(String nickname) {
DeviceData data = DeviceDatabase.getInstance(mContext).getDeviceData(nickname);
if (data == null) {
return;
}
unwatch(data.deviceId);
}
public void unwatch(long deviceId) {
if (mWatchedDevices.containsKey(deviceId)) {
Watching watching = mWatchedDevices.get(deviceId);
mWatchedDevices.remove(deviceId);
watching.stop();
}
}
class Watching implements Runnable {
final long deviceId;
final HashSet<DeviceProperty> propertySet;
long intervalMills = DEFAULT_INTERVAL_MILLS;
ExecutorService mExecutor = null;
public Watching(long deviceId, HashSet<DeviceProperty> propertyNameSet) {
this.deviceId = deviceId;
this.propertySet = propertyNameSet;
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
ArrayList<DeviceProperty> list = new ArrayList<DeviceProperty>();
for (DeviceProperty p : propertySet) {
list.add(p);
}
if (list.isEmpty()) {
mWatchedDevices.remove(deviceId);
return;
}
DeviceData data = DeviceDatabase.getInstance(mContext).getDeviceData(deviceId);
if (data == null) {
mWatchedDevices.remove(deviceId);
return;
}
DeviceManager.getInstance(mContext).get(data.nickname, list, 0);
try {
printDebugLog(data.nickname + "," + intervalMills);
Thread.sleep(intervalMills);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
mWatchedDevices.remove(deviceId);
return;
}
}
}
public void start() {
stop();
mExecutor = Executors.newSingleThreadExecutor();
mExecutor.execute(this);
}
public void stop() {
if (mExecutor != null) {
mExecutor.shutdown();
mExecutor.shutdownNow();
}
mExecutor = null;
}
}
public void watchEveryday(String nickname, String propertyName, long intervalMills,
long delayMills) {
// 定期的にgetする
}
public void thinOut() {
}
public synchronized void insertLog(DeviceData data, DeviceInfo info, String accessType,
DeviceProperty property) {
LinkedHashMap<String, String> record = new LinkedHashMap<String, String>();
Date date = new Date();
record.put(LABEL_VERSION, VERSION);
record.put(LABEL_UNIXTIME, Long.toString(date.getTime()));
record.put(LABEL_NICKNAME, data.nickname);
record.put(LABEL_DEVICE_TYPE, info.deviceType);
record.put(LABEL_PROTOCOL, data.protocolName);
record.put(LABEL_ACCESS_TYPE, accessType);
record.put(LABEL_PROPERTY_NAME, property.name);
record.put(LABEL_PROPERTY_VALUE, (property.value != null) ? property.value.toString()
: "null");
record.put(LABEL_SUCCESS, Boolean.toString(property.success));
record.put(LABEL_MESSAGE, property.message != null ? property.message.toString() : null);
if (property.success) {
String key = getAccessDataCacheKey(data.nickname, accessType, property.name);
mLatestValidAccessDataCache.put(key, record);
}
insertLog(date, record);
}
public synchronized void insertLog(Date date, LinkedHashMap<String, String> record) {
String fileName = getLogFileName(date);
printDebugLog("log file name:" + fileName);
File file = getLogFile(fileName);
try {
LTSVWriter w = new LTSVWriter(new FileOutputStream(file, true));
try {
w.write(record);
} catch (IOException e) {
e.printStackTrace();
w.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public JSONArray queryLog(long beginning, long end) {
return queryLog(beginning, end, null);
}
public JSONArray queryLog(long beginning, long end, LogFilter filter) {
JSONArray ret = new JSONArray();
List<File> fileList = getLogFileList(beginning, end);
if (beginning == -1) {
beginning = 0;
}
if (end == -1) {
end = System.currentTimeMillis();
}
for (File logFile : fileList) {
List<LinkedHashMap<String, String>> list = null;
LTSVReader reader = null;
try {
reader = new LTSVReader(new FileInputStream(logFile));
list = reader.read();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (reader != null) {
reader.close();
}
if (list != null) {
for (LinkedHashMap<String, String> data : list) {
long unixtime = getUnixTime(data);
if (unixtime >= beginning && unixtime <= end) {
if (filter == null) {
ret.put(convertLog(data));
} else if (filter.predicate(data)) {
ret.put(convertLog(data));
}
}
}
}
}
return ret;
}
public String logview(LinkedHashMap<String, String> data) {
return null;
}
public SimpleDateFormat getLogFileNameFormat() {
return new SimpleDateFormat("yyyy'_'MM'_'dd'.ltsv'", Locale.JAPAN);
}
public String getLogFileName(Date date) {
SimpleDateFormat sdf = getLogFileNameFormat();
String fileName = sdf.format(date);
return fileName;
}
public File getLogDir() {
File dir = new File(mContext.getFilesDir(), "log");
if (!dir.exists()) {
dir.mkdirs();
}
return dir;
}
public File getLogFile(String fileName) {
File dir = getLogDir();
if (!dir.exists()) {
dir.mkdirs();
}
File ret = new File(dir, fileName);
return ret;
}
public static long getUnixTime(LinkedHashMap<String, String> data) {
String version = data.get(LABEL_VERSION);
if (version.equals("0.1")) {
return Long.parseLong(data.get("unixtime"));
}
return -2;
}
public static String getNickname(LinkedHashMap<String, String> data) {
String version = data.get(LABEL_VERSION);
if (version.equals("0.1")) {
return data.get("nickname");
}
return null;
}
public JSONObject convertLog(LinkedHashMap<String, String> data) {
JSONObject ret = new JSONObject();
String version = data.get(LABEL_VERSION);
if (version.equals("0.1")) {
try {
ret.put(LABEL_UNIXTIME, data.get("unixtime"));
ret.put(LABEL_NICKNAME, data.get("nickname"));
ret.put(LABEL_DEVICE_TYPE, data.get("device_type"));
ret.put(LABEL_PROTOCOL, data.get("protocol"));
ret.put(LABEL_ACCESS_TYPE, data.get("access_type"));
ret.put(LABEL_PROPERTY_NAME, data.get("property_name"));
ret.put(LABEL_PROPERTY_VALUE, data.get("property_value"));
ret.put(LABEL_SUCCESS, data.get("success"));
ret.put(LABEL_MESSAGE, data.get("message"));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return ret;
}
public List<File> getLogFileList(long beginning, long end) {
File dir = getLogDir();
File[] logFiles = dir.listFiles();
Arrays.sort(logFiles);
ArrayList<File> ret = new ArrayList<File>();
if (beginning == -1) {
beginning = 0;
} else {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(beginning);
Calendar c1 = Calendar.getInstance();
c1.clear();
c1.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
beginning = c1.getTimeInMillis();
}
if (end == -1) {
end = System.currentTimeMillis();
}
SimpleDateFormat sdf = getLogFileNameFormat();
for (File f : logFiles) {
try {
long time = sdf.parse(f.getName()).getTime();
if (time >= beginning && time <= end) {
ret.add(f);
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return ret;
}
public void printDebugLog(Object s) {
// Log.v(TAG, "["+TAG+"]"+ (s != null?s.toString():"null"));
}
public interface LogFilter {
public boolean predicate(LinkedHashMap<String, String> data);
}
public String getAccessDataCacheKey(String nickname, String accessType, String propertyName) {
String key = nickname + "\n" + accessType + "\n" + propertyName;
return key;
}
}