/**
*
* Copyright (c) 2009-2016 Freedomotic team http://freedomotic.com
*
* This file is part of Freedomotic
*
* This Program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2, or (at your option) any later version.
*
* This Program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* Freedomotic; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
package com.freedomotic.plugins.devices.lt111;
import com.freedomotic.api.EventTemplate;
import com.freedomotic.api.Protocol;
import com.freedomotic.app.Freedomotic;
import com.freedomotic.exceptions.UnableToExecuteException;
import com.freedomotic.reactions.Command;
import java.io.IOException;
import java.util.logging.Logger;
import com.freedomotic.events.ProtocolRead;
import com.freedomotic.things.EnvObjectLogic;
import com.freedomotic.things.EnvObjectPersistence;
import java.io.IOException;
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Lt111
extends Protocol {
List<String> address_list = new ArrayList<String>();
ServerSocket serverSocket;
Socket connectionSocket;
Client client;
int port;
private static final Logger LOG = Logger.getLogger(Lt111.class.getName());
final int POLLING_WAIT;
public Lt111() {
//every plugin needs a name and a manifest XML file
super("Lt111", "/lt111/lt111-manifest.xml");
//read a property from the manifest file below which is in
//FREEDOMOTIC_FOLDER/plugins/devices/com.freedomotic.hello/hello-world.xml
POLLING_WAIT = configuration.getIntProperty("polling_rate", 2000);
//POLLING_WAIT is the value of the property "time-between-reads" or 2000 millisecs,
//default value if the property does not exist in the manifest
setPollingWait(POLLING_WAIT); //millisecs interval between hardware device status reads
}
@Override
protected void onShowGui() {
/**
* uncomment the line below to add a GUI to this plugin the GUI can be
* started with a right-click on plugin list on the desktop frontend
* (com.freedomotic.jfrontend plugin)
*/
//bindGuiToPlugin(new HelloWorldGui(this));
}
@Override
protected void onHideGui() {
//implement here what to do when the this plugin GUI is closed
//for example you can change the plugin description
setDescription("My GUI is now hidden");
}
@Override
protected void onRun() {
LOG.info("Lt111 onRun() logs this message every " + "POLLINGWAIT=" + POLLING_WAIT
+ "milliseconds");
for (short i = 0; i < address_list.size(); i++) {
try{
System.out.println("Wilson debug: Start class with address "+address_list.get(i));
client = new Client(address_list.get(i),POLLING_WAIT);
client.start();
}catch(Exception e){
System.out.println("LT-111 OnStart Error: " + e);
}
}
//at the end of this method the system waits POLLINGTIME
//before calling it again. The result is this log message is printed
//every 2 seconds (2000 millisecs)
}
@Override
protected void onStart() {
LOG.info("Lt111 plugin is started");
Socket clientSocket = null;
address_list.clear();
for (EnvObjectLogic object : EnvObjectPersistence.getObjectByProtocol("LT111")){
String address = object.getPojo().getPhisicalAddress();
String name = object.getPojo().getName();
address_list.add(address);
System.out.println("Wilson debug: Modbus Address"+address);
}
}
@Override
protected void onStop() {
LOG.info("Lt111 plugin is stopped ");
}
@Override
protected void onCommand(Command c)
throws IOException, UnableToExecuteException {
LOG.info("Lt111 plugin receives a command called " + c.getName() + " with parameters "
+ c.getProperties().toString());
}
@Override
protected boolean canExecute(Command c) {
//don't mind this method for now
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected void onEvent(EventTemplate event) {
//don't mind this method for now
throw new UnsupportedOperationException("Not supported yet.");
}
class Client extends Thread
{
private DataOutputStream outToClient;
private byte[] read_bytes;
int logging_interval;
int polling;
String address;
DataOutputStream outToServer;
DataInputStream inFromServer;
public Client(String ip_address,int POLLING_WAIT)
{
address=ip_address;
polling=POLLING_WAIT;
}
public void run()
{
try{
String[] ip_address = address.split(":");
System.out.println("Wilson debug: IP:"+ip_address[0]+", "+ip_address[1]);
Socket clientSocket = new Socket(ip_address[0], Integer.valueOf(ip_address[1]));
clientSocket.setSoTimeout(2000); // 2 seconds timeout
System.out.println("Wilson debug: Initialze this address:"+address);
outToServer = new DataOutputStream(clientSocket.getOutputStream());
inFromServer = new DataInputStream(clientSocket.getInputStream());
/*
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Runnable periodicTask = new Runnable() {
public void run() {
// Invoke method(s) to do the work
read_realpower();
}
};
executor.scheduleAtFixedRate(periodicTask, 0, polling, TimeUnit.MILLISECONDS);
*/
read_realpower();
outToServer.close();
inFromServer.close();
clientSocket.close();
}catch(Exception e){
System.out.println("LT-111 Client Error: " + e);
}
//System.out.println("Runnning");
}
public void read_realpower()
{
byte[] read_command = new byte[] {(byte)0x01, (byte)0x03, (byte)0x00, (byte)0x48, (byte)0x00, (byte)0x06, (byte)0x45, (byte)0xDE};
try{
//int length = inFromServer.readInt();
short CRC;
byte[] message = new byte[17];
//String modifiedSentence = inFromServer.readLine();
// read length of incoming message
CRC=crc16(read_command);
read_command[6]=(byte)CRC;
read_command[7]=(byte)(CRC>>>8);
//System.out.println("Send bytes: "+getHex(read_command));
outToServer.write(read_command);
inFromServer.readFully(message, 0, message.length); // read the message
CRC=crc16(message);
//System.out.println("Receive bytes: "+getHex(message));
//System.out.println("CRC="+Integer.toHexString(CRC&0xFF));
if(((message[15]&0xFF)==(CRC&0xFF))&&(message[16]&0xFF)==((CRC>>>8)&0xFF)){
//System.out.println("Correct");
float voltage=(float) (((message[4]&0xFF)+((message[3]&0xFF)*256))/100);
short current=(short) (((message[6]&0xFF)+((message[5]&0xFF)*256))/1000);
short power=(short) ((message[8]&0xFF)+((message[7]&0xFF)*256));
int energy=(((message[9]&0xFF)*256*256*256)+((message[10]&0xFF)*256*256)+((message[11]&0xFF)*256)+(message[12]&0xFF))/3200;
short pf= (short) (((message[14]&0xFF)+(message[13]&0xFF)*256)/10);
Date date = new Date();
SimpleDateFormat ft = new SimpleDateFormat ("yyyy.MM.dd 'at' HH:mm:ss");
//System.out.println(ft.format(date)+" "+address+" read: Voltage:"+voltage+"V, Current:"+current+"mA, Power:"+power+"W, Energy:"+energy+"kwh, Power Factor:"+pf);
// Send data read event
ProtocolRead event = new ProtocolRead(this, "LT111",address );
event.addProperty("lt111.current", Short.toString(current));
event.addProperty("lt111.power", Short.toString(power));
event.addProperty("lt111.voltage", String.format("%.6g%n",voltage));
event.addProperty("lt111.energy", Integer.toString(energy));
event.addProperty("lt111.power_factor", Short.toString(pf));
Freedomotic.sendEvent(event);
}
//System.out.println(getHex(message));
}catch(Exception e){
System.out.println("LT-111 send Error: " + e);
}
}
public String getHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b&0xff)+",");
}
return result.toString();
}
public short crc16(byte[] data)
{
short reg_crc=(short)0xFFFF;
for (int i=0; i<(data.length-2); i++) {
reg_crc ^= (data[i]&0xFF);
for (int j=0; j<8; j++) {
if((reg_crc & 0x0001)==0x01){ /* LSB(b0)=1 */
reg_crc=(short)((reg_crc&0xFFFF)>>>1);
reg_crc ^= 0xA001;
}else{
reg_crc=(short)((reg_crc&0xFFFF) >>> 1);
}
}
}
return reg_crc;
}
// ignores the higher 16 bits
public float toFloat( int hbits )
{
int mant = hbits & 0x03ff; // 10 bits mantissa
int exp = hbits & 0x7c00; // 5 bits exponent
if( exp == 0x7c00 ) // NaN/Inf
exp = 0x3fc00; // -> NaN/Inf
else if( exp != 0 ) // normalized value
{
exp += 0x1c000; // exp - 15 + 127
if( mant == 0 && exp > 0x1c400 ) // smooth transition
return Float.intBitsToFloat( ( hbits & 0x8000 ) << 16
| exp << 13 | 0x3ff );
}
else if( mant != 0 ) // && exp==0 -> subnormal
{
exp = 0x1c400; // make it normal
do {
mant <<= 1; // mantissa * 2
exp -= 0x400; // decrease exp by 1
} while( ( mant & 0x400 ) == 0 ); // while not normal
mant &= 0x3ff; // discard subnormal bit
} // else +/-0 -> +/-0
return Float.intBitsToFloat( // combine all parts
( hbits & 0x8000 ) << 16 // sign << ( 31 - 15 )
| ( exp | mant ) << 13 ); // value << ( 23 - 10 )
}
}
}