123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- package com.doverfuelingsolutions.issp.fusion
- import androidx.lifecycle.Lifecycle
- import androidx.lifecycle.LifecycleObserver
- import androidx.lifecycle.OnLifecycleEvent
- import com.doverfuelingsolutions.issp.R
- import com.doverfuelingsolutions.issp.api.FuelInfoApi
- import com.doverfuelingsolutions.issp.api.dto.DFSResult
- import com.doverfuelingsolutions.issp.api.entity.PumpInfo
- import com.doverfuelingsolutions.issp.data.GlobalData
- import com.doverfuelingsolutions.issp.fusion.callback.OnFusionEvent
- 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.wayne.www.waynelib.fdc.FdcClient
- import com.wayne.www.waynelib.fdc.OnFdcClientStateChangedListener
- import com.wayne.www.waynelib.fdc.OnFdcMessageReceivedListener
- import com.wayne.www.waynelib.fdc.OnFdcServiceResponseReceivedListener
- import com.wayne.www.waynelib.fdc.message.*
- import kotlinx.coroutines.*
- import kotlin.coroutines.resume
- import kotlin.coroutines.suspendCoroutine
- object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
- OnFdcServiceResponseReceivedListener,
- OnFdcMessageReceivedListener {
- var stateFusion: FdcClient.FdcClientState = FdcClient.FdcClientState.Stopped
- private const val timeoutMax = 4000
- var firstLink = false
- private set
- private var isLogin = false
- val pumpList = arrayListOf<PumpInfo>()
- private val coroutineIO = CoroutineScope(Dispatchers.IO)
- var onFusionEvent: OnFusionEvent? = null
- override fun onFdcClientStateChanged(sender: FdcClient?, state: FdcClient.FdcClientState?) {
- DFSLog.i("Fusion: state = ${state?.name}")
- if (sender == null || state == null) return
- stateFusion = state
- when (state) {
- FdcClient.FdcClientState.Connected -> {
- firstLink = true
- loginFetchInfo()
- }
- // 中途断网重连
- FdcClient.FdcClientState.Connecting -> onFusionEvent?.onFusionReconnect()
- FdcClient.FdcClientState.MyAddReConnect -> {
- if (!firstLink) {
- onFusionEvent?.onFusionInit(FusionError.WrongAddress, StringUtil.get(R.string.wrong_fusion_address))
- } else {
- onFusionEvent?.onFusionReconnect()
- }
- }
- else -> {}
- }
- }
- override fun onServiceResponseReceived(sender: FdcClient?, serviceResponse: ServiceResponse?) {
- if (sender == null || serviceResponse == null) return
- // DFSLog.v("onServiceResponseReceived")
- }
- override fun onFdcMessageReceived(sender: FdcClient?, fdcMessage: FdcMessage?) {
- if (sender == null || fdcMessage == null) return
- // DFSLog.v("onFdcMessageReceived")
- }
- @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
- fun initialize() {
- FdcClient.getDefault().addOnFdcClientStateChangedListeners(this)
- FdcClient.getDefault().addOnFdcServiceResponseReceivedListeners(this)
- FdcClient.getDefault().addOnFdcMessageReceivedListeners(this)
- FdcClient.getDefault().start(
- SPUtil.getString(SPKeys.MIDDLE_IP),
- SPUtil.getString(SPKeys.MIDDLE_PORT).toInt(),
- GlobalData.serialNumber.get(),
- SPUtil.getString(SPKeys.MIDDLE_WORKSTATION_ID).toInt(),
- null
- )
- coroutineIO.launch {
- delay(12000)
- if (stateFusion == FdcClient.FdcClientState.Stopped) {
- onFusionEvent?.onFusionInit(FusionError.Timeout, StringUtil.get(R.string.connect_timeout))
- }
- }
- }
- @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- fun close() {
- val manager = this
- coroutineIO.launch {
- logout()
- FdcClient.getDefault().removeOnFdcClientStateChangedListeners(manager)
- FdcClient.getDefault().removeOnFdcServiceResponseReceivedListeners(manager)
- FdcClient.getDefault().removeOnFdcMessageReceivedListeners(manager)
- FdcClient.getDefault().stop()
- firstLink = false
- }
- }
- fun restart() {
- coroutineIO.launch {
- close()
- delay(timeoutMax.toLong() + 1000L)
- initialize()
- }
- }
- /**
- * 初始化流程中的登录+获取油品信息
- */
- fun loginFetchInfo() {
- coroutineIO.launch {
- if (!isLogin) {
- val success = if (login()) true else {
- DFSLog.d("Fusion: retry login after logout")
- logout()
- delay(7000)
- login()
- }
- if (!success) {
- isLogin = false
- onFusionEvent?.onFusionInit(FusionError.Login, StringUtil.get(R.string.login_fail))
- return@launch
- } else {
- isLogin = true
- }
- }
- val resultPumpList = requestPumpInfo()
- if (resultPumpList.success && resultPumpList.data != null) {
- if (pumpList.isNotEmpty()) pumpList.clear()
- pumpList.addAll(resultPumpList.data)
- onFusionEvent?.onFusionInit(FusionError.Success)
- } else {
- onFusionEvent?.onFusionInit(FusionError.GetNozzleInfo, StringUtil.get(R.string.fail_get_nozzle))
- }
- }
- }
- /**
- * 获取所有未结算加油订单
- * @param pumpId -1表示全部FP,其他代表某个FP
- */
- suspend fun getAllUnsettledOrder(pumpId: Int = -1) = suspendCoroutine<DFSResult<List<DeviceClass>>> {
- ThreadUtil.io {
- FdcClient.getDefault().sendGetAvailableFuelSaleTrxs(pumpId, { _, response ->
- coroutineIO.launch {
- if (response == null || response !is ServiceResponseGetAvailableFuelSaleTrxs) {
- it.resume(DFSResult.fail(R.string.fail_get_order))
- } else if (response.singleFdcData == null || response.singleDeviceClass == null) {
- it.resume(DFSResult.success(emptyList()))
- } else {
- DFSLog.v("uncleared orders of pump $pumpId", response.singleFdcData.deviceClasses)
- val unsettledList = response.singleFdcData.deviceClasses.filter { !it.isClear }
- val resultDetailList = unsettledList
- .map { dc -> async { getOrderDetail(dc) } }
- .map { deferred -> deferred.await() }
- .filter { it.success }
- .mapNotNull {
- it.data?.productName =
- pumpList.find { pump -> pump.pumpId == it.data?.pumpNo }?.oilName
- ?: ""
- it.data
- }
- it.resume(DFSResult.success(resultDetailList))
- }
- }
- }, 4000)
- }
- }
- /**
- * 挂起函数:获取订单详情
- */
- private suspend fun getOrderDetail(deviceClass: DeviceClass) = suspendCoroutine<DFSResult<DeviceClass>> {
- getOrderDetailAsync(deviceClass) { asyncResult ->
- it.resume(asyncResult)
- }
- }
- /**
- * 获取订单详情
- */
- private fun getOrderDetailAsync(dcRaw: DeviceClass, callback: (result: DFSResult<DeviceClass>) -> Unit) {
- ThreadUtil.io {
- FdcClient.getDefault().sendGetFuelSalesTrxDetailsRequestAsync(dcRaw.pumpNo, dcRaw.transactionSeqNo, dcRaw.releaseTokenAttribute, { _, response ->
- if (response == null || response !is ServiceResponseGetFuelSalesTrxDetails || response.fdcData.isEmpty() || response.fdcData[0].deviceClasses.isEmpty()) {
- callback(DFSResult.fail(R.string.fail_get_order_detail))
- } else {
- val dc = response.fdcData[0].deviceClasses[0]
- // 迷惑的数据需要 hack 一下
- dc.releaseTokenAttribute = dcRaw.releaseTokenAttribute
- val correctNozzle = pumpList.find {
- it.pumpId == dc.pumpNo
- }?.nozzleList?.find {
- it.logicId == dc.nozzleNo
- }
- correctNozzle?.let {
- dc.nozzleNo = it.physicalId
- dc.logicNo = it.logicId
- }
- callback(DFSResult.success(dc))
- }
- }, 3000)
- }
- }
- /**
- * 消单
- * @desc 返回的 dc.isLock 不会发生改变 FIXME
- */
- suspend fun clearOrder(dc: DeviceClass): DFSResult<Boolean> {
- return if (dc.isClear) {
- DFSResult.success(true)
- } else if (dc.releaseTokenAttribute == null || !ValidateUtil.isPositiveInt(dc.releaseTokenAttribute)) {
- DFSResult.fail(R.string.wrong_order_info)
- } else {
- clearOrder(dc.pumpNo, dc.transactionSeqNo, dc.releaseTokenAttribute.toInt())
- }
- }
- suspend fun clearOrder(pumpId: Int, transactionNo: String, token: Int,) = suspendCoroutine<DFSResult<Boolean>> {
- ThreadUtil.io {
- FdcClient.getDefault().sendClearFuelSaleTrxRequestAsync(pumpId, transactionNo, token, { _, response ->
- if (response != null && response.singleDeviceClass != null && response.overallResult.equals("Success", true)) {
- DFSLog.i("clear order[$pumpId-$transactionNo-$token] succeed")
- it.resume(DFSResult.success(true))
- } else {
- DFSLog.e("FusionManager.clearOrder failed", response)
- it.resume(DFSResult.fail(R.string.fail_lock_order))
- }
- },4000)
- }
- }
- /**
- * 锁定/解锁订单
- */
- suspend fun lockOrder(dc: DeviceClass, lock: Boolean): DFSResult<Boolean> {
- return if (dc.isLock == lock) {
- DFSResult.success(true)
- } else if (dc.releaseTokenAttribute == null || !ValidateUtil.isPositiveInt(dc.releaseTokenAttribute)) {
- DFSResult.fail(R.string.wrong_order_info)
- } else {
- lockOrder(dc.pumpNo, dc.transactionSeqNo, dc.releaseTokenAttribute.toInt(), lock)
- }
- }
- suspend fun lockOrder(pumpId: Int, transactionNo: String, token: Int, lock: Boolean) = suspendCoroutine<DFSResult<Boolean>> {
- ThreadUtil.io {
- val callback = OnFdcServiceResponseReceivedListener { _, response ->
- if (response != null && response.singleDeviceClass != null && response.overallResult.equals("Success", true)) {
- it.resume(DFSResult.success(true))
- } else {
- it.resume(DFSResult.fail(response?.overallResult ?: StringUtil.get(R.string.fail_operate)))
- }
- }
- if (lock) {
- FdcClient.getDefault().sendLockFuelSaleTrxRequestAsync(pumpId, transactionNo, token, callback, 4000)
- } else {
- FdcClient.getDefault().sendUnLockFuelSaleTrxRequestAsync(pumpId, transactionNo, token, callback, 4000)
- }
- }
- }
- suspend fun requestPumpInfo() = FuelInfoApi.requestPumpInfo()
- private suspend fun login() = suspendCoroutine<Boolean> {
- val port = SPUtil.getString(SPKeys.MIDDLE_PORT).toInt()
- FdcClient.getDefault().sendLogonRequestAsync(port, port, "00.07", { _, response ->
- if (response != null && response is ServiceResponseLogOn) {
- val status = response.singleFdcData.fdcStatus
- DFSLog.v("Fusion: try login response = $status")
- if (status.equals(FusionConstants.CODE_SUCCESS, true)) {
- DFSLog.d("Fusion: login succeeded")
- it.resume(true)
- } else {
- DFSLog.d("Fusion: login failed")
- it.resume(false)
- }
- }
- }, timeoutMax)
- }
- private suspend fun logout() = suspendCoroutine<Boolean> {
- FdcClient.getDefault().sendLogOffRequestAsync({ _, response ->
- val status = response?.singleFdcData?.fdcStatus
- DFSLog.v("Fusion: try logout response = $status")
- if (status != null && status.equals(FusionConstants.CODE_SUCCESS, true)) {
- DFSLog.d("Fusion: logout succeeded")
- it.resume(true)
- } else {
- DFSLog.d("Fusion: logout failed")
- it.resume(false)
- }
- }, timeoutMax)
- }
- }
|