OSDN Git Service

Kodak PIXPRO WPZ2 の接続シーケンスを強化。
[gokigen/A01d.git] / app / src / main / java / net / osdn / gokigen / a01d / camera / kodak / wrapper / command / KodakCommandCommunicator.java
1 package net.osdn.gokigen.a01d.camera.kodak.wrapper.command;
2
3 import android.util.Log;
4
5 import androidx.annotation.NonNull;
6 import androidx.annotation.Nullable;
7
8 import net.osdn.gokigen.a01d.camera.kodak.wrapper.command.messages.KodakCommandReceiveOnly;
9
10 import java.io.BufferedReader;
11 import java.io.ByteArrayOutputStream;
12 import java.io.DataOutputStream;
13 import java.io.InputStream;
14 import java.net.InetSocketAddress;
15 import java.net.Socket;
16 import java.util.ArrayDeque;
17 import java.util.Queue;
18
19 import static net.osdn.gokigen.a01d.camera.kodak.wrapper.command.messages.IKodakMessages.SEQ_RECEIVE_ONLY;
20 import static net.osdn.gokigen.a01d.camera.utils.SimpleLogDumper.dump_bytes;
21
22 public class KodakCommandCommunicator implements IKodakCommandPublisher, IKodakCommunication
23 {
24     private static final String TAG = KodakCommandCommunicator.class.getSimpleName();
25
26     private static final int BUFFER_SIZE = 1024 * 1024 + 16;  // 受信バッファは 1MB
27     private static final int COMMAND_SEND_RECEIVE_DURATION_MS = 5;
28     private static final int COMMAND_SEND_RECEIVE_DURATION_MAX = 3000;
29     private static final int COMMAND_POLL_QUEUE_MS = 5;
30
31     private final String ipAddress;
32     private final int portNumber;
33
34     private boolean isStart = false;
35     private boolean tcpNoDelay;
36     private boolean waitForever;
37     private Socket socket = null;
38     private DataOutputStream dos = null;
39     private BufferedReader bufferedReader = null;
40     private Queue<IKodakCommand> commandQueue;
41
42     public KodakCommandCommunicator(@NonNull String ip, int portNumber, boolean tcpNoDelay, boolean waitForever)
43     {
44         this.ipAddress = ip;
45         this.portNumber = portNumber;
46         this.tcpNoDelay = tcpNoDelay;
47         this.waitForever = waitForever;
48         this.commandQueue = new ArrayDeque<>();
49         commandQueue.clear();
50     }
51
52     @Override
53     public boolean isConnected()
54     {
55         return (socket != null);
56     }
57
58     @Override
59     public boolean connect()
60     {
61         try
62         {
63             Log.v(TAG, " connect()");
64             socket = new Socket();
65             socket.setTcpNoDelay(tcpNoDelay);
66             if (tcpNoDelay)
67             {
68                 socket.setKeepAlive(false);
69                 socket.setPerformancePreferences(0, 2, 0);
70                 socket.setOOBInline(true);
71                 socket.setReuseAddress(false);
72                 socket.setTrafficClass(0x80);
73             }
74             socket.connect(new InetSocketAddress(ipAddress, portNumber), 0);
75             return (true);
76         }
77         catch (Exception e)
78         {
79             e.printStackTrace();
80             socket = null;
81         }
82         return (false);
83     }
84
85     private void closeOutputStream()
86     {
87         try
88         {
89             if (dos != null)
90             {
91                 dos.close();
92             }
93         }
94         catch (Exception e)
95         {
96             e.printStackTrace();
97         }
98         dos = null;
99     }
100
101     private void closeBufferedReader()
102     {
103         try
104         {
105             if (bufferedReader != null)
106             {
107                 bufferedReader.close();
108             }
109         }
110         catch (Exception e)
111         {
112             e.printStackTrace();
113         }
114         bufferedReader = null;
115     }
116
117     private void closeSocket()
118     {
119         try
120         {
121             if (socket != null)
122             {
123                 socket.close();
124             }
125         }
126         catch (Exception e)
127         {
128             e.printStackTrace();
129         }
130         socket = null;
131     }
132
133     @Override
134     public void disconnect()
135     {
136         // 通信関連のクローズ
137         closeOutputStream();
138         closeBufferedReader();
139         closeSocket();
140
141         isStart = false;
142         commandQueue.clear();
143         System.gc();
144     }
145
146     @Override
147     public void start()
148     {
149         if (isStart)
150         {
151             // すでにコマンドのスレッド動作中なので抜ける
152             return;
153         }
154         isStart = true;
155         Log.v(TAG, " start()");
156
157         Thread thread = new Thread(new Runnable()
158         {
159             @Override
160             public void run()
161             {
162                 try
163                 {
164                     InputStream is = socket.getInputStream();
165                     dos = new DataOutputStream(socket.getOutputStream());
166                     while (isStart)
167                     {
168                         try
169                         {
170                             IKodakCommand command = commandQueue.poll();
171                             if (command != null)
172                             {
173                                 issueCommand(command);
174                             }
175                             Thread.sleep(COMMAND_POLL_QUEUE_MS);
176                             if ((is != null)&&(is.available() > 0))
177                             {
178                                 Log.v(TAG, " --- RECV MSG --- ");
179                                 receive_from_camera(new KodakCommandReceiveOnly(SEQ_RECEIVE_ONLY, null));
180                             }
181                         }
182                         catch (Exception e)
183                         {
184                             e.printStackTrace();
185                         }
186                     }
187                 }
188                 catch (Exception e)
189                 {
190                     Log.v(TAG, "<<<<< IP : " + ipAddress + " port : " + portNumber + " >>>>>");
191                     e.printStackTrace();
192                 }
193             }
194         });
195         try
196         {
197             thread.start();
198         }
199         catch (Exception e)
200         {
201             e.printStackTrace();
202         }
203     }
204
205     @Override
206     public void stop()
207     {
208         isStart = false;
209         commandQueue.clear();
210     }
211
212     @Override
213     public boolean enqueueCommand(@NonNull IKodakCommand command)
214     {
215         try
216         {
217             //Log.v(TAG, "Enqueue : "  + command.getId());
218             return (commandQueue.offer(command));
219         }
220         catch (Exception e)
221         {
222             e.printStackTrace();
223         }
224         return (false);
225     }
226
227     private void issueCommand(@NonNull IKodakCommand command)
228     {
229         try
230         {
231             boolean retry_over = true;
232             while (retry_over)
233             {
234                 //Log.v(TAG, "issueCommand : " + command.getId());
235                 byte[] commandBody = command.commandBody();
236                 if (commandBody != null)
237                 {
238                     // コマンドボディが入っていた場合には、コマンド送信(入っていない場合は受信待ち)
239                     send_to_camera(command.dumpLog(), commandBody);
240                     byte[] commandBody2 = command.commandBody2();
241                     if (commandBody2 != null)
242                     {
243                         // コマンドボディの2つめが入っていた場合には、コマンドを連続送信する
244                         send_to_camera(command.dumpLog(), commandBody2);
245                     }
246                 }
247                 retry_over = receive_from_camera(command);
248                 if (retry_over)
249                 {
250                     retry_over = command.sendRetry();
251                 }
252             }
253         }
254         catch (Exception e)
255         {
256             e.printStackTrace();
257         }
258     }
259
260     /**
261      *    カメラにコマンドを送信する(メイン部分)
262      *
263      */
264     private void send_to_camera(boolean isDumpReceiveLog, @NonNull byte[] byte_array)
265     {
266         try
267         {
268             if (dos == null)
269             {
270                 Log.v(TAG, " DataOutputStream is null.");
271                 return;
272             }
273
274             if (byte_array.length <= 0)
275             {
276                 // メッセージボディがない。終了する
277                 Log.v(TAG, " SEND BODY IS NOTHING.");
278                 return;
279             }
280
281             if (isDumpReceiveLog)
282             {
283                 // ログに送信メッセージを出力する
284                 dump_bytes("SEND[" + byte_array.length + "] ", byte_array);
285             }
286
287             // (データを)送信
288             dos.write(byte_array);
289             dos.flush();
290         }
291         catch (Exception e)
292         {
293             e.printStackTrace();
294         }
295     }
296
297     private void sleep(int delayMs)
298     {
299         try
300         {
301             Thread.sleep(delayMs);
302         }
303         catch (Exception e)
304         {
305             e.printStackTrace();
306         }
307     }
308
309     /**
310      *    カメラからにコマンドの結果を受信する(メイン部分)
311      *
312      */
313     private boolean receive_from_camera(@NonNull IKodakCommand command)
314     {
315         int delayMs = command.receiveDelayMs();
316         if ((delayMs < 0)||(delayMs > COMMAND_SEND_RECEIVE_DURATION_MAX))
317         {
318             delayMs = COMMAND_SEND_RECEIVE_DURATION_MS;
319         }
320
321         //  受信した後、すべてをまとめて「受信したよ」と応答するパターン
322         return (receive_single(command, delayMs));
323     }
324
325     private boolean receive_single(@NonNull IKodakCommand command, int delayMs)
326     {
327         boolean isDumpReceiveLog = command.dumpLog();
328         int id = command.getId();
329         IKodakCommandCallback callback = command.responseCallback();
330         try
331         {
332             int receive_message_buffer_size = BUFFER_SIZE;
333             byte[] byte_array = new byte[receive_message_buffer_size];
334             InputStream is = socket.getInputStream();
335             if (is == null)
336             {
337                 Log.v(TAG, " InputStream is NULL... RECEIVE ABORTED.");
338                 receivedAllMessage(isDumpReceiveLog, id, null, callback);
339                 return (false);
340             }
341
342             // 初回データが受信バッファにデータが溜まるまで待つ...
343             int read_bytes = waitForReceive(is, delayMs, command.maxRetryCount());
344             if (read_bytes < 0)
345             {
346                 // リトライオーバー検出
347                 Log.v(TAG, " DETECT RETRY OVER...");
348                 return (true);
349             }
350
351             // 受信したデータをバッファに突っ込む
352             ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
353             while (read_bytes > 0)
354             {
355                 read_bytes = is.read(byte_array, 0, receive_message_buffer_size);
356                 if (read_bytes <= 0)
357                 {
358                     Log.v(TAG, " RECEIVED MESSAGE FINISHED (" + read_bytes + ")");
359                     break;
360                 }
361                 byteStream.write(byte_array, 0, read_bytes);
362                 sleep(delayMs);
363                 read_bytes = is.available();
364             }
365             receivedAllMessage(isDumpReceiveLog, id, byteStream.toByteArray(), callback);
366             System.gc();
367         }
368         catch (Throwable e)
369         {
370             e.printStackTrace();
371             System.gc();
372         }
373         return (false);
374     }
375
376     private void receivedAllMessage(boolean isDumpReceiveLog, int id, byte[] body, IKodakCommandCallback callback)
377     {
378         Log.v(TAG, "receivedAllMessage() : " + ((body == null) ? 0 : body.length) + " bytes.");
379         if ((isDumpReceiveLog)&&(body != null))
380         {
381             // ログに受信メッセージを出力する
382             dump_bytes("RECV[" + body.length + "] ", body);
383         }
384         if (checkReceiveStatusMessage(body))
385         {
386             send_secondary_message(isDumpReceiveLog, body);
387         }
388
389         if (callback != null)
390         {
391             callback.receivedMessage(id, body);
392         }
393     }
394
395     private void send_secondary_message(boolean isDumpReceiveLog, @Nullable byte[] received_body)
396     {
397         if (received_body == null)
398         {
399             Log.v(TAG, "send_secondary_message : NULL ");
400             return;
401         }
402         Log.v(TAG, "send_secondary_message : [" + received_body[8] + "] [" + received_body[9] + "] ");
403         try {
404             byte[] message_to_send = null;
405             if ((received_body[8] == (byte) 0xd2) && (received_body[9] == (byte) 0xd2)) {
406                 message_to_send = new byte[]{
407                         (byte) 0x2e, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
408                         (byte) 0xd2, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x10, (byte) 0x00, (byte) 0x80,
409                         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
410                         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
411                         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
412                         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
413                         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
414                         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
415                         (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
416                 };
417             }
418
419             if ((received_body[8] == (byte) 0xba) && (received_body[9] == (byte) 0x0b)) {
420                 message_to_send = new byte[]
421                         {
422                                 (byte) 0x2e, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
423                                 (byte) 0xba, (byte) 0x0b, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x10, (byte) 0x00, (byte) 0x80,
424                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
425                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
426                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
427                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
428                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
429                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
430                                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
431                         };
432             }
433             if ((received_body[8] == (byte) 0xbb) && (received_body[9] == (byte) 0x0b)) {
434                 message_to_send = new byte[]
435                         {
436                                 (byte) 0x2e, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
437                                 (byte) 0xbb, (byte) 0x0b, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x10, (byte) 0x00, (byte) 0x80,
438                                 (byte) 0x1f, (byte) 0x00, (byte) 0x00, (byte) 0x90, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
439                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
440                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
441                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
442                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
443                                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
444                                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
445                         };
446             }
447             if ((isDumpReceiveLog)&&(message_to_send != null))
448             {
449                 // ログに受信メッセージを出力する
450                 dump_bytes("SND2[" + message_to_send.length + "] ", message_to_send);
451             }
452
453             if ((dos != null)&&(message_to_send != null))
454             {
455                 // (データを)送信
456                 dos.write(message_to_send);
457                 dos.flush();
458             }
459         }
460         catch (Exception e)
461         {
462             e.printStackTrace();
463         }
464     }
465
466     private boolean checkReceiveStatusMessage(@Nullable byte[] receive_body)
467     {
468         boolean isReceivedStatusMessage = false;
469         try
470         {
471             if (receive_body == null)
472             {
473                 return (false);
474             }
475             if (receive_body.length < 16)
476             {
477                 Log.v(TAG, " BODY SIZE IS SMALL. : " + receive_body.length);
478                 return (false);
479             }
480             if (((receive_body[8] == (byte) 0xd2)&&(receive_body[9] == (byte) 0x07))||
481                     ((receive_body[8] == (byte) 0xba)&&(receive_body[9] == (byte) 0x0b))||
482                     ((receive_body[8] == (byte) 0xbb)&&(receive_body[9] == (byte) 0x0b)))
483
484             {
485                 isReceivedStatusMessage = true;
486                 Log.v(TAG, "  >>> RECEIVED HOST PRIMARY MESSAGE. <<<");
487             }
488         }
489         catch (Exception e)
490         {
491             e.printStackTrace();
492         }
493         return (isReceivedStatusMessage);
494     }
495
496     private int waitForReceive(InputStream is, int delayMs, int retry_count)
497     {
498         boolean isLogOutput = true;
499         int read_bytes = 0;
500         try
501         {
502             while (read_bytes <= 0)
503             {
504                 Log.v(TAG, "  --- waitForReceive : " + retry_count + " delay : " + delayMs + "ms");
505                 sleep(delayMs);
506                 read_bytes = is.available();
507                 if (read_bytes <= 0)
508                 {
509                     if (isLogOutput)
510                     {
511                         Log.v(TAG, "  waitForReceive:: is.available() WAIT... : " + delayMs + "ms");
512                         isLogOutput = false;
513                     }
514                     retry_count--;
515                     if ((!waitForever)&&(retry_count < 0))
516                     {
517                         return (-1);
518                     }
519                 }
520             }
521         }
522         catch (Exception e)
523         {
524             e.printStackTrace();
525         }
526         return (read_bytes);
527     }
528 }