/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.souliss.internal.network.udp; import java.net.DatagramPacket; import java.util.ArrayList; import java.util.Iterator; import java.util.Map.Entry; import org.openhab.binding.souliss.internal.network.typicals.Constants; import org.openhab.binding.souliss.internal.network.typicals.SoulissGenericTypical; import org.openhab.binding.souliss.internal.network.typicals.SoulissNetworkParameter; import org.openhab.binding.souliss.internal.network.typicals.SoulissT16; import org.openhab.binding.souliss.internal.network.typicals.SoulissT1A; import org.openhab.binding.souliss.internal.network.typicals.SoulissT31; import org.openhab.binding.souliss.internal.network.typicals.SoulissTServiceUpdater; import org.openhab.binding.souliss.internal.network.typicals.SoulissTypicals; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class decodes incoming Souliss packets, starting from decodevNet * * @author Alessandro Del Pex * @author Tonino Fazio * @since 1.7.0 */ public class UDPSoulissDecoder { private SoulissTypicals soulissTypicalsRecipients; private static Logger logger = LoggerFactory.getLogger(UDPSoulissDecoder.class); public UDPSoulissDecoder(SoulissTypicals typicals) { soulissTypicalsRecipients = typicals; } /** * Get packet from VNET Frame * * @param packet * incoming datagram */ public void decodeVNetDatagram(DatagramPacket packet) { int checklen = packet.getLength(); ArrayList<Short> mac = new ArrayList<Short>(); for (int ig = 7; ig < checklen; ig++) { mac.add((short) (packet.getData()[ig] & 0xFF)); } decodeMacaco(mac); } /** * Decodes lower level MaCaCo packet * * @param macacoPck */ private void decodeMacaco(ArrayList<Short> macacoPck) { int functionalCode = macacoPck.get(0); logger.debug("decodeMacaco: Received functional code: 0x"+ Integer.toHexString(functionalCode)); switch (functionalCode) { case (byte) ConstantsUDP.Souliss_UDP_function_ping_resp: logger.debug("function_ping_resp"); decodePing(macacoPck); break; case (byte) ConstantsUDP.Souliss_UDP_function_subscribe_resp: case (byte) ConstantsUDP.Souliss_UDP_function_poll_resp: logger.debug("Souliss_UDP_function_subscribe_resp / Souliss_UDP_function_poll_resp"); decodeStateRequest(macacoPck); break; case ConstantsUDP.Souliss_UDP_function_typreq_resp:// Answer for // assigned // typical logic logger.debug("** TypReq answer"); decodeTypRequest(macacoPck); break; case (byte) ConstantsUDP.Souliss_UDP_function_health_resp:// Answer // nodes // healty logger.debug("function_health_resp"); decodeHealthRequest(macacoPck); break; case (byte) ConstantsUDP.Souliss_UDP_function_db_struct_resp:// Answer // nodes logger.debug("function_db_struct_resp"); decodeDBStructRequest(macacoPck); break; case 0x83: logger.debug("Functional code not supported"); break; case 0x84: logger.debug("Data out of range"); break; case 0x85: logger.debug("Subscription refused"); break; default: logger.debug("Unknown functional code"); break; } } // /** // * @param mac // */ private void decodePing(ArrayList<Short> mac) { int putIn_1 = mac.get(1); int putIn_2 = mac.get(2); logger.debug("decodePing: putIn code: {}, {}", putIn_1, putIn_2); } /** * Sovrascrive la struttura I nodi e la struttura dei tipici e richiama * UDPHelper.typicalRequest(opzioni, nodes, 0); * * @param mac */ private void decodeDBStructRequest(ArrayList<Short> mac) { try { int nodes = mac.get(5); int maxnodes = mac.get(6); int maxTypicalXnode = mac.get(7); int maxrequests = mac.get(8); int MaCaco_IN_S = mac.get(9); int MaCaco_TYP_S = mac.get(10); int MaCaco_OUT_S = mac.get(11); SoulissNetworkParameter.nodes = nodes; SoulissNetworkParameter.maxnodes = maxnodes; SoulissNetworkParameter.maxTypicalXnode = maxTypicalXnode; SoulissNetworkParameter.maxrequests = maxrequests; SoulissNetworkParameter.MaCacoIN_s = MaCaco_IN_S; SoulissNetworkParameter.MaCacoTYP_s = MaCaco_TYP_S; SoulissNetworkParameter.MaCacoOUT_s = MaCaco_OUT_S; logger.debug("decodeDBStructRequest"); logger.debug("Nodes: " + nodes); logger.debug("maxnodes: " + maxnodes); logger.debug("maxTypicalXnode: " + maxTypicalXnode); logger.debug("maxrequests: " + maxrequests); logger.debug("MaCaco_IN_S: " + MaCaco_IN_S); logger.debug("MaCaco_TYP_S: " + MaCaco_TYP_S); logger.debug("MaCaco_OUT_S: " + MaCaco_OUT_S); } catch (Exception e) { logger.error("decodeDBStructRequest: SoulissNetworkParameter update ERROR"); } } private void decodeTypRequest(ArrayList<Short> mac) { try { short tgtnode = mac.get(3); int numberOf = mac.get(4); int typXnodo = SoulissNetworkParameter.maxnodes; logger.debug( "--DECODE MACACO OFFSET: {} NUMOF: {} TYPICALSXNODE: {}", tgtnode, numberOf, typXnodo); // creates Souliss nodes for (int j = 0; j < numberOf; j++) { if (mac.get(5 + j) != 0) {// create only not-empty typicals if (!(mac.get(5 + j) == Constants.Souliss_T_related)) { String hTyp = Integer.toHexString(mac.get(5 + j)); short slot = (short) (j % typXnodo); short node = (short) (j / typXnodo + tgtnode); } } } } catch (Exception uy) { logger.error("decodeTypRequest ERROR"); } } private void decodeStateRequest(ArrayList<Short> mac) { boolean bDecoded_forLOG = false; int tgtnode = mac.get(3); // QUI. AGGIORNAMENTO DEL TIMESTAMP PER OGNI NODO. DA FARE USANDO NODI // FITTIZI SoulissTServiceUpdater.updateTIMESTAMP(soulissTypicalsRecipients, tgtnode); // sfoglio hashtable e scelgo tipici del nodo indicato nel frame // leggo valore tipico in base allo slot synchronized (this) { Iterator<Entry<String, SoulissGenericTypical>> iteratorTypicals = soulissTypicalsRecipients .getIterator(); while (iteratorTypicals.hasNext()) { SoulissGenericTypical typ = iteratorTypicals.next().getValue(); // se il tipico estratto appartiene al nodo che il frame deve // aggiornare... bDecoded_forLOG = false; if (typ.getSoulissNodeID() == tgtnode) { // ...allora controllo lo slot int slot = typ.getSlot(); // ...ed aggiorno lo stato in base al tipo int iNumBytes = 0; try { String sHex = Integer.toHexString(typ.getType()); String sRes = SoulissNetworkParameter .getPropTypicalBytes(sHex.toUpperCase()); if (sRes != null) iNumBytes = Integer.parseInt(sRes); } catch (NumberFormatException e) { e.printStackTrace(); iNumBytes = 0; } float val = 0; // ***** T1A ***** if (typ.getType() == 0x1A) { short sVal = getByteAtSlot(mac, slot); ((SoulissT1A) typ).setState(sVal); bDecoded_forLOG = true; // ***** T19 ***** } else if (typ.getType() == 0x19) { // set value of T19 at number of second slot short sVal = getByteAtSlot(mac, slot + 1); typ.setState(sVal); bDecoded_forLOG = true; } else if (iNumBytes == 1) { // caso valori digitali val = getByteAtSlot(mac, slot); typ.setState(val); bDecoded_forLOG = true; } else if (iNumBytes == 2) { // caso valori float val = getFloatAtSlot(mac, slot); typ.setState(val); bDecoded_forLOG = true; } else if (iNumBytes == 4) { // ***** T16 RGB ***** val = getByteAtSlot(mac, slot); typ.setState(val); ((SoulissT16) typ).setStateRED(getByteAtSlot(mac, slot + 1)); ((SoulissT16) typ).setStateGREEN(getByteAtSlot(mac, slot + 2)); ((SoulissT16) typ).setStateBLU(getByteAtSlot(mac, slot + 3)); bDecoded_forLOG = true; } else if (iNumBytes == 5) { // ***** T31 ***** // ******************* // SLOT 0: Control State short sVal = getByteAtSlot(mac, slot); ((SoulissT31) typ).setRawCommandState(sVal); /* * The control state bit meaning follow as: * BIT 0 Not used * BIT 1 (0 Heating OFF , 1 Heating ON) * BIT 2 (0 Cooling OFF , 1 Cooling ON) * BIT 3 (0 Fan 1 OFF , 1 Fan 1 ON) * BIT 4 (0 Fan 2 OFF , 1 Fan 2 ON) * BIT 5 (0 Fan 3 OFF , 1 Fan 3 ON) * BIT 6 (0 Manual Mode , 1 Automatic Mode for Fan) * BIT 7 (0 Heating Mode, 1 Cooling Mode) */ ((SoulissT31) typ).power.setState(getBitState(sVal, 0)); ((SoulissT31) typ).heating.setState(getBitState(sVal, 1)); ((SoulissT31) typ).cooling.setState(getBitState(sVal, 2)); ((SoulissT31) typ).fanLow.setState(getBitState(sVal, 3)); ((SoulissT31) typ).fanMed.setState(getBitState(sVal, 4)); ((SoulissT31) typ).fanHigh.setState(getBitState(sVal, 5)); ((SoulissT31) typ).fanAutoMode.setState(getBitState(sVal, 6)); ((SoulissT31) typ).heatingCoolingModeValue.setState(getBitState(sVal, 7)); // SLOT 1-2: Temperature Measured Value val = getFloatAtSlot(mac, slot + 1); ((SoulissT31) typ).setMeasuredValue(val); // SLOT 3-4: Temperature Setpoint Value val = getFloatAtSlot(mac, slot + 3); ((SoulissT31) typ).setSetpointValue(val); bDecoded_forLOG = true; } // non esegue per healt e timestamp, perchè il LOG viene // inserito in un altro punto del codice if (typ.getType() != 152 && typ.getType() != 153) if (iNumBytes == 4) // RGB Log logger.debug( "decodeStateRequest: {} ({}) = {}. RGB= {}, {}, {}", typ.getName(), Short.valueOf(typ.getType()), ((SoulissT16) typ).getState(), ((SoulissT16) typ).getStateRED(), ((SoulissT16) typ).getStateGREEN(), ((SoulissT16) typ).getStateBLU()); else if (iNumBytes == 5) { // T31 Thermostat logger.debug( "decodeStateRequest: {} ({}). Thermostat= {}, Temp.Measured= {}, Temp.SetPoint= {}", typ.getName(), Short.valueOf(typ.getType()), ((SoulissT31) typ).getRawCommandState(), ((SoulissT31) typ) .getTemperatureMeasuredValue(), ((SoulissT31) typ).getSetpointValue()); } else if (bDecoded_forLOG) { if (typ.getType() == 0x1A) { logger.debug( "decodeStateRequest: {} (0x{}) = {}", typ.getName(), Integer.toHexString(typ.getType()), Integer.toBinaryString(((SoulissT1A) typ) .getRawState())); } else logger.debug( "decodeStateRequest: {} (0x{}) = {}", typ.getName(), Integer.toHexString(typ.getType()), Float.valueOf(val)); } } } } } private Short getByteAtSlot(ArrayList<Short> mac, int slot) { return mac.get(5 + slot); } private float getFloatAtSlot(ArrayList<Short> mac, int slot) { int iOutput = mac.get(5 + slot); int iOutput2 = mac.get(5 + slot + 1); // ora ho i due bytes, li converto int shifted = iOutput2 << 8; float ret = HalfFloatUtils.toFloat(shifted + iOutput); return ret; } /** * Decodes a souliss nodes health request * * @param macaco * packet */ private void decodeHealthRequest(ArrayList<Short> mac) { int numberOf = mac.get(4); // build an array containing healths for (int i = 5; i < 5 + numberOf; i++) { // healths.add(Short.valueOf(mac.get(i))); SoulissTServiceUpdater.updateHEALTY(soulissTypicalsRecipients, i - 5, Short.valueOf(mac.get(i))); } } public short getBitState(short vRaw, int iBit) { final int MASK_BIT_1 = 0x1; if (((vRaw >>> iBit) & MASK_BIT_1) == 0) { return 0; } else { return 1; } } }