Browse Source

fix 优化fusion链接异常,实现自动重连

RobinTan1024 4 years ago
parent
commit
7b41dfc6c8

+ 44 - 26
app/src/main/java/com/doverfuelingsolutions/issp/fusion/FusionManager.kt

@@ -10,11 +10,11 @@ import com.doverfuelingsolutions.issp.api.entity.PumpInfo
 import com.doverfuelingsolutions.issp.data.GlobalData
 import com.doverfuelingsolutions.issp.fusion.callback.OnFusionStatus
 import com.doverfuelingsolutions.issp.utils.StringUtil
-import com.doverfuelingsolutions.issp.utils.thread.ThreadUtil
 import com.doverfuelingsolutions.issp.utils.ValidateUtil
 import com.doverfuelingsolutions.issp.utils.log.DFSLog
 import com.doverfuelingsolutions.issp.utils.sp.SPKeys
 import com.doverfuelingsolutions.issp.utils.sp.SPUtil
+import com.doverfuelingsolutions.issp.utils.thread.ThreadUtil
 import com.wayne.www.waynelib.fdc.FdcClient
 import com.wayne.www.waynelib.fdc.OnFdcClientStateChangedListener
 import com.wayne.www.waynelib.fdc.OnFdcMessageReceivedListener
@@ -24,6 +24,7 @@ import kotlinx.coroutines.*
 import java.util.*
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
+import kotlin.math.min
 
 object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
     OnFdcServiceResponseReceivedListener,
@@ -32,7 +33,8 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
     private var isInitialized = false
     var stateFusion: FdcClient.FdcClientState = FdcClient.FdcClientState.Stopped
     private const val timeoutMax = 4000
-    private var reconnectTime = System.currentTimeMillis()
+    private var reconnectTime = 0
+    private var isKeepDisconnected = false
 
     var isLogin = false
         private set
@@ -41,14 +43,22 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
     val pumpList = arrayListOf<PumpInfo>()
 
     private val coroutineIO = CoroutineScope(Dispatchers.IO)
+    private var jobRestart: Job? = null
 
     var onFusionStatus: OnFusionStatus? = null
 
     override fun onFdcClientStateChanged(sender: FdcClient?, state: FdcClient.FdcClientState?) {
-        DFSLog.d("Fusion: state = ${state?.name?.toLowerCase(Locale.CHINESE)}")
+        DFSLog.d("Fusion state changed: ${state?.name?.toLowerCase(Locale.CHINESE)}")
         if (sender == null || state == null) return
 
         stateFusion = state
+        // Reset reconnectTime.
+        if (state != FdcClient.FdcClientState.Connecting && state != FdcClient.FdcClientState.MyAddReConnect) {
+            isKeepDisconnected = false
+            if (reconnectTime > 0) {
+                reconnectTime = 0
+            }
+        }
         when (state) {
             FdcClient.FdcClientState.Connected -> {
                 onFusionStatus?.onFusionStatus(FusionStatus.Connected)
@@ -69,6 +79,7 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
                     delay(2000)
                     if (stateFusion == FdcClient.FdcClientState.Connecting || stateFusion == FdcClient.FdcClientState.DisconnectedByHeartbeat) {
                         // Result of restart has to be MyAddReConnect or Connected so the custom Connecting status triggered above can't last forever.
+                        DFSLog.i("Try restart fusion client as current client remains Connecting or DisconnectedByHeartbeat after delay")
                         restart()
                     }
                 }
@@ -83,15 +94,22 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
             }
             FdcClient.FdcClientState.MyAddReConnect -> {
                 // Status MyAddReConnect means client socket has been closed.
-                // Restart might or might not work.
-                // Prevent infinite loop cause restart could also triggered MyAddReConnect.
-                val time = System.currentTimeMillis()
-                if (time - reconnectTime > 20_000) {
-                    onFusionStatus?.onFusionStatus(FusionStatus.Connecting)
-                    reconnectTime = time
-                    restart()
-                } else {
-                    onFusionStatus?.onFusionStatus(FusionStatus.Disconnected)
+                // Restart might or might not works.
+                // Prevent infinite loop cause restart could also trigger MyAddReConnect.
+                jobRestart = coroutineIO.launch {
+                    isKeepDisconnected = true
+
+                    val delayTime = reconnectTime * 10L
+                    reconnectTime = min(reconnectTime + 1, 6)
+                    if (delayTime > 0) {
+                        onFusionStatus?.onFusionStatus(FusionStatus.Disconnected)
+                        delay(delayTime * 1000)
+                    }
+                    if (isKeepDisconnected) {
+                        DFSLog.d("Try to restart 'Disconnected' fusion client.")
+                        onFusionStatus?.onFusionStatus(FusionStatus.Connecting)
+                        restart()
+                    }
                 }
             }
         }
@@ -103,6 +121,7 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
 
     @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
     fun initialize() {
+        DFSLog.i("FusionManager is initializing.")
         isInitialized = true
         FdcClient.getDefault().addOnFdcClientStateChangedListeners(this)
         FdcClient.getDefault().addOnFdcServiceResponseReceivedListeners(this)
@@ -118,6 +137,7 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
 
     @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
     fun close() {
+        DFSLog.i("FusionManager is closing.")
         isInitialized = false
         isLogin = false
         isFetchPumpInfo = false
@@ -132,7 +152,18 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
         }
     }
 
-    fun restart() {
+    /**
+     * Restart fusion client.
+     *
+     * @param immediate FusionManager has an internal restart loop when client remains disconnected. This restart loop will be broken as 'immediate' is true.
+     */
+    fun restart(immediate: Boolean = false) {
+        DFSLog.d("Fusion client is restarting.")
+        if (immediate) {
+            isKeepDisconnected = false
+            reconnectTime = 0
+            if (jobRestart?.isActive == true) jobRestart?.cancel()
+        }
         coroutineIO.launch {
             close()
             delay(timeoutMax.toLong() + 1000L)
@@ -151,19 +182,6 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
                 login()
             }
             it.resume(isLogin)
-
-            // FIXME pumpList is not set
-//            val resultPumpList = requestPumpInfo()
-//            if (resultPumpList.success && resultPumpList.data != null) {
-//                if (pumpList.isNotEmpty()) pumpList.clear()
-//                pumpList.addAll(resultPumpList.data)
-//                onFusionStatus?.onFusionInit(FusionStatus.Success)
-//            } else {
-//                onFusionStatus?.onFusionInit(
-//                    FusionStatus.GetNozzleInfo,
-//                    StringUtil.get(R.string.fail_get_nozzle)
-//                )
-//            }
         }
     }
 

+ 19 - 15
app/src/main/java/com/doverfuelingsolutions/issp/view/MainActivity.kt

@@ -95,8 +95,8 @@ class MainActivity : AppCompatActivity(),
                         if (!isFuelModified && !isMiddleModified) return@let
 
                         if (isMiddleModified || FusionManager.stateFusion != FdcClient.FdcClientState.Connected) {
-                            onFusionStatus(FusionStatus.Connecting)
-                            FusionManager.restart()
+                            setFusionLinkingLoading()
+                            FusionManager.restart(true)
                         } else if (isFuelModified && FusionManager.stateFusion == FdcClient.FdcClientState.Connected) {
                             val dialog = LoadingDialogBuilder(this@MainActivity)
                                 .setLoadingText(R.string.in_get_fuel)
@@ -144,22 +144,12 @@ class MainActivity : AppCompatActivity(),
     }
 
     override fun onFusionStatus(status: FusionStatus) {
-        DFSLog.i("Fusion status: ${status.name.toLowerCase(Locale.CHINESE)}")
+        DFSLog.i("Fusion output status: ${status.name.toLowerCase(Locale.CHINESE)}")
         lifecycleScope.launch {
             dialogFusionLinking?.hide()
             when (status) {
                 FusionStatus.Connecting -> {
-                    // Don't dismiss dialog, just show & hide for reuse.
-                    if (dialogFusionLinking == null) {
-                        dialogFusionLinking = LoadingDialogBuilder(this@MainActivity)
-                            .setTitle(R.string.fusion_in_connect)
-                            .setLoadingText(R.string.might_take_minutes)
-                            .setCancelable(false)
-                            .show()
-                        dialogFusionLinking?.window?.let { WindowUtil.setFullscreen(it) }
-                    } else {
-                        dialogFusionLinking?.show()
-                    }
+                    setFusionLinkingLoading()
                 }
                 FusionStatus.Connected -> {
                     DFSToastUtil.success(R.string.connect_fusion_success)
@@ -171,7 +161,7 @@ class MainActivity : AppCompatActivity(),
                     // 2. Communication exception and failed retry.
                     DFSToastUtil.fail(R.string.disconnect_fusion)
                     setFragment(FragmentReconnect.build {
-                        onFusionStatus(FusionStatus.Connecting)
+                        setFusionLinkingLoading()
                     })
                 }
             }
@@ -203,6 +193,20 @@ class MainActivity : AppCompatActivity(),
         }
     }
 
+    private fun setFusionLinkingLoading() {
+        // Don't dismiss dialog, just show & hide for reuse.
+        if (dialogFusionLinking == null) {
+            dialogFusionLinking = LoadingDialogBuilder(this@MainActivity)
+                .setTitle(R.string.fusion_in_connect)
+                .setLoadingText(R.string.reconnect_fusion_tip)
+                .setCancelable(false)
+                .show()
+            dialogFusionLinking?.window?.let { WindowUtil.setFullscreen(it) }
+        } else {
+            dialogFusionLinking?.show()
+        }
+    }
+
     fun setFragment(fragment: Fragment, back: Boolean = false) {
         supportFragmentManager.beginTransaction().run {
             replace(R.id.fragmentBox, fragment)

+ 1 - 1
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentReconnect.kt

@@ -35,7 +35,7 @@ class FragmentReconnect private constructor(private val reconnectHandler: () ->
     override fun onClick(v: View?) {
         when (v) {
             binding.buttonReconnect -> {
-                FusionManager.restart()
+                FusionManager.restart(true)
                 reconnectHandler.invoke()
             }
             binding.buttonPreference -> {

+ 1 - 1
app/src/main/res/layout/layout_loading.xml

@@ -22,5 +22,5 @@
         android:id="@+id/loadingTip"
         style="@style/wrap"
         android:text="@string/in_loading"
-        android:textSize="18sp" />
+        android:textSize="24sp" />
 </androidx.appcompat.widget.LinearLayoutCompat>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -153,6 +153,7 @@
     <!-- Fusion -->
     <string name="fusion_in_connect">正在连接 Fusion…</string>
     <string name="reconnect">重新连接</string>
+    <string name="reconnect_fusion_tip">与 Fusion 的连接似乎不太稳定,正常尝试重连…</string>
     <string name="connect_fusion_success">连接 Fusion 成功</string>
     <string name="disconnect_fusion">无法连接到 Fusion</string>
     <string name="fusion_login_failed">登录到 Fusion 失败…</string>