1818import java .net .InetAddress ;
1919import java .net .InetSocketAddress ;
2020import java .net .Socket ;
21- import java .util .Arrays ;
2221
2322import org .eclipse .jdt .annotation .NonNullByDefault ;
2423import org .eclipse .jdt .annotation .Nullable ;
2524import org .openhab .binding .danfossairunit .internal .protocol .Parameter ;
25+ import org .openhab .core .util .HexUtils ;
2626import org .slf4j .Logger ;
2727import org .slf4j .LoggerFactory ;
2828
@@ -39,47 +39,51 @@ public class DanfossAirUnitCommunicationController implements CommunicationContr
3939 private static final int TCP_PORT = 30046 ;
4040 private static final int READ_TIMEOUT_MILLISECONDS = 5_000 ;
4141 private static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 5_000 ;
42+ private static final int RESPONSE_LENGTH = 63 ;
43+ private static final byte [] EMPTY = new byte [0 ];
4244
4345 private final Logger logger = LoggerFactory .getLogger (DanfossAirUnitCommunicationController .class );
44- private final InetAddress inetAddr ;
46+ private final InetAddress hostAddress ;
4547 private final int connectTimeoutMilliseconds ;
4648
47- private boolean connected = false ;
4849 private @ Nullable Socket socket ;
4950 private @ Nullable OutputStream outputStream ;
5051 private @ Nullable InputStream inputStream ;
5152
52- public DanfossAirUnitCommunicationController (InetAddress inetAddr ) {
53- this (inetAddr , DEFAULT_CONNECT_TIMEOUT_MILLISECONDS );
53+ public DanfossAirUnitCommunicationController (InetAddress hostAddress ) {
54+ this (hostAddress , DEFAULT_CONNECT_TIMEOUT_MILLISECONDS );
5455 }
5556
56- public DanfossAirUnitCommunicationController (InetAddress inetAddr , int connectTimeoutMilliseconds ) {
57- this .inetAddr = inetAddr ;
57+ public DanfossAirUnitCommunicationController (InetAddress hostAddress , int connectTimeoutMilliseconds ) {
58+ this .hostAddress = hostAddress ;
5859 this .connectTimeoutMilliseconds = connectTimeoutMilliseconds ;
5960 }
6061
61- @ Override
62- public synchronized void connect () throws IOException {
63- if (connected ) {
62+ private synchronized void connect () throws IOException {
63+ Socket socket = this .socket ;
64+ if (socket != null && !socket .isClosed ()) {
65+ // Already connected
6466 return ;
6567 }
66- Socket socket = this .socket = new Socket ();
67- socket .connect (new InetSocketAddress (inetAddr , TCP_PORT ), connectTimeoutMilliseconds );
68+
69+ socket = new Socket ();
70+ socket .connect (new InetSocketAddress (hostAddress , TCP_PORT ), connectTimeoutMilliseconds );
6871 socket .setSoTimeout (READ_TIMEOUT_MILLISECONDS );
69- outputStream = socket .getOutputStream ();
70- inputStream = socket .getInputStream ();
71- connected = true ;
72+
73+ OutputStream outputStream = socket .getOutputStream ();
74+ InputStream inputStream = socket .getInputStream ();
75+
76+ this .socket = socket ;
77+ this .outputStream = outputStream ;
78+ this .inputStream = inputStream ;
7279 }
7380
7481 @ Override
75- public synchronized void disconnect () {
76- if (!connected ) {
77- return ;
78- }
82+ public synchronized void close () {
7983 try {
80- Socket localSocket = this .socket ;
81- if (localSocket != null ) {
82- localSocket .close ();
84+ Socket socket = this .socket ;
85+ if (socket != null && ! socket . isClosed () ) {
86+ socket .close ();
8387 }
8488 } catch (IOException ioe ) {
8589 logger .debug ("Connection to air unit could not be closed gracefully. {}" , ioe .getMessage ());
@@ -88,51 +92,78 @@ public synchronized void disconnect() {
8892 this .inputStream = null ;
8993 this .outputStream = null ;
9094 }
91- connected = false ;
9295 }
9396
9497 @ Override
9598 public byte [] sendRobustRequest (Parameter parameter ) throws IOException {
96- return sendRobustRequest (parameter , new byte [] {} );
99+ return sendRobustRequest (parameter , EMPTY );
97100 }
98101
99102 @ Override
100103 public synchronized byte [] sendRobustRequest (Parameter parameter , byte [] value ) throws IOException {
101- connect ();
102104 byte [] request = parameter .getRequest (value );
105+
103106 try {
104- return sendRequestInternal (request );
105- } catch (IOException ioe ) {
106- // retry once if there was connection problem
107- disconnect ();
108107 connect ();
109- return sendRequestInternal (request );
108+
109+ byte [] response = sendRequestInternal (request );
110+ if (logger .isTraceEnabled ()) {
111+ logger .trace ("{} response: {}" , parameter , HexUtils .bytesToHex (response ));
112+ }
113+
114+ return response ;
115+ } catch (IOException suppressedException ) {
116+ logger .debug ("{} request failed, retrying once: {}" , parameter , suppressedException .getMessage ());
117+
118+ close ();
119+
120+ try {
121+ connect ();
122+
123+ byte [] response = sendRequestInternal (request );
124+ if (logger .isTraceEnabled ()) {
125+ logger .trace ("{} response: {}" , parameter , HexUtils .bytesToHex (response ));
126+ }
127+
128+ return response ;
129+ } catch (IOException e ) {
130+ suppressedException .addSuppressed (e );
131+ throw suppressedException ;
132+ }
110133 }
111134 }
112135
113- private synchronized byte [] sendRequestInternal (byte [] request ) throws IOException {
114- OutputStream localOutputStream = this .outputStream ;
136+ private byte [] sendRequestInternal (byte [] request ) throws IOException {
137+ OutputStream outputStream = this .outputStream ;
138+ InputStream inputStream = this .inputStream ;
115139
116- if (localOutputStream == null ) {
117- throw new IOException (
118- String .format ("Output stream is null while sending request: %s" , Arrays .toString (request )));
119- }
120- localOutputStream .write (request );
121- localOutputStream .flush ();
122-
123- byte [] result = new byte [63 ];
124- InputStream localInputStream = this .inputStream ;
125- if (localInputStream == null ) {
126- throw new IOException (
127- String .format ("Input stream is null while sending request: %s" , Arrays .toString (request )));
140+ if (outputStream == null || inputStream == null ) {
141+ throw new IOException ("Input/output streams not initialized" );
128142 }
129143
130- int bytesRead = localInputStream .read (result , 0 , 63 );
131- if (bytesRead < 63 ) {
132- throw new IOException (String .format (
133- "Error reading from stream, read returned %d as number of bytes read into the buffer" , bytesRead ));
144+ outputStream .write (request );
145+ outputStream .flush ();
146+
147+ return readExact (inputStream , RESPONSE_LENGTH );
148+ }
149+
150+ private byte [] readExact (InputStream inputStream , int length ) throws IOException {
151+ byte [] response = new byte [length ];
152+ int offset = 0 ;
153+
154+ while (offset < length ) {
155+ int bytesRead = inputStream .read (response , offset , length - offset );
156+
157+ if (bytesRead == -1 ) {
158+ throw new IOException ("Stream closed while reading response" );
159+ }
160+ if (bytesRead == 0 ) {
161+ throw new IOException ("Read returned 0 bytes, possible stream stall" );
162+ }
163+
164+ offset += bytesRead ;
134165 }
135166
136- return result ;
167+ return response ;
137168 }
138169}
0 commit comments