|
@@ -3,9 +3,10 @@ package inn.ocsf.bee.freigeld.core.demo
|
|
|
import com.oblac.nomen.Nomen
|
|
|
import inn.ocsf.bee.freigeld.core.calc.CoinStrategy
|
|
|
import inn.ocsf.bee.freigeld.core.calc.CoinUtils
|
|
|
+import inn.ocsf.bee.freigeld.core.data.CentralBankAccountLevel
|
|
|
import inn.ocsf.bee.freigeld.core.model.*
|
|
|
-import kotlinx.coroutines.CompletableDeferred
|
|
|
-import kotlinx.coroutines.runBlocking
|
|
|
+import inn.ocsf.bee.freigeld.core.model.data.PersonData
|
|
|
+import kotlinx.coroutines.*
|
|
|
import org.slf4j.LoggerFactory
|
|
|
import org.springframework.beans.factory.annotation.Autowired
|
|
|
import org.springframework.scheduling.annotation.Scheduled
|
|
@@ -19,19 +20,20 @@ import kotlin.math.roundToLong
|
|
|
@Service
|
|
|
class DemoInMem {
|
|
|
|
|
|
- val personMap = mutableMapOf<UUID, DemoHuman>()
|
|
|
+ @Autowired
|
|
|
+ private lateinit var world: GlobalWorld
|
|
|
|
|
|
@Autowired
|
|
|
private lateinit var emitter: GlobalEmitter
|
|
|
|
|
|
@Autowired
|
|
|
- private lateinit var bank: GlobalBank
|
|
|
+ private lateinit var bank: CentralBank
|
|
|
|
|
|
private val log = LoggerFactory.getLogger(javaClass)
|
|
|
|
|
|
@PostConstruct
|
|
|
fun init() {
|
|
|
- emitter.emit(10000, CoinValue.one)
|
|
|
+ /*emitter.emit(10000, CoinValue.one)
|
|
|
emitter.emit(5000, CoinValue.three)
|
|
|
emitter.emit(2000, CoinValue.five)
|
|
|
emitter.emit(1000, CoinValue.ten)
|
|
@@ -40,10 +42,21 @@ class DemoInMem {
|
|
|
emitter.emit(50, CoinValue.full)
|
|
|
emitter.emit(25, CoinValue.bi)
|
|
|
emitter.emit(10, CoinValue.mega)
|
|
|
-
|
|
|
+*/
|
|
|
bank.exchange(bank.getSelfAccount(), emitter, 1_300_000)
|
|
|
- (0 until 100).forEach { personMap.computeIfAbsent(UUID.randomUUID()) { id -> DemoHuman(id, Nomen.randomName()) } }
|
|
|
- personMap.keys.map { bank.addAccount(it) }.forEach {
|
|
|
+ (0 until 100).map {
|
|
|
+ val newHuman: NaturalPerson = PersonData.NaturalPersonImpl(Nomen.randomName())
|
|
|
+ val oldHuman = world.getPersonByIdentity(PersonIdentityFullName(newHuman.fullName))
|
|
|
+ if (oldHuman != null) {
|
|
|
+ oldHuman as NaturalPerson
|
|
|
+ } else {
|
|
|
+ world.addPerson(newHuman)
|
|
|
+ world.setPersonIdentity(newHuman, PersonIdentityFullName(newHuman.fullName))
|
|
|
+ newHuman
|
|
|
+ }
|
|
|
+ }.forEach { log.info("human ${it.fullName} created") }
|
|
|
+
|
|
|
+ world.getPersonIds().map { bank.addAccount(it) }.forEach {
|
|
|
bank.exchange(it, bank.getSelfAccount(), 1_200 + (500 * Math.random()).roundToLong())
|
|
|
.thenRun({ log.info("exchange ok") })
|
|
|
.exceptionally({ log.error("exchange error"); null })
|
|
@@ -51,8 +64,9 @@ class DemoInMem {
|
|
|
}
|
|
|
|
|
|
private fun doRand0() {
|
|
|
- val fromPerson = personMap.keys.random()
|
|
|
- val toPerson = personMap.keys.subtract(setOf(fromPerson)).random()
|
|
|
+ val personIds = world.getPersonIds()
|
|
|
+ val fromPerson = personIds.random()
|
|
|
+ val toPerson = personIds.subtract(setOf(fromPerson)).random()
|
|
|
val fromAccount = bank.getAccounts(fromPerson).first()
|
|
|
val toAccout = bank.getAccounts(toPerson).first()
|
|
|
val dir = if (fromAccount.overall >= toAccout.overall) fromAccount to toAccout else toAccout to fromAccount
|
|
@@ -73,60 +87,24 @@ class DemoInMem {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fun GlobalBank.getSelfAccount(): BankAccount {
|
|
|
+fun CentralBank.getSelfAccount(): BankAccount {
|
|
|
return this.getAccounts(this.id).first()
|
|
|
}
|
|
|
|
|
|
@Service
|
|
|
-class GlobalBank : Bank {
|
|
|
+class GlobalBankDemo : CentralBankAccountLevel() {
|
|
|
|
|
|
@Autowired
|
|
|
private lateinit var emitter: GlobalEmitter
|
|
|
|
|
|
- val accountMap = mutableMapOf<UUID, BankAccount>()
|
|
|
- val personToAccountTable = mutableSetOf<Pair<UUID, UUID>>()
|
|
|
+ //val accountMap = mutableMapOf<UUID, BankAccount>()
|
|
|
+ //val personToAccountTable = mutableSetOf<Pair<UUID, UUID>>()
|
|
|
val globalQueue: Deque<BankEvent> = ArrayDeque()
|
|
|
val globalFutureMap = mutableMapOf<UUID, CompletableDeferred<Any>>()
|
|
|
val coinToAccountMap = mutableMapOf<UUID, UUID>()
|
|
|
|
|
|
- private lateinit var selfId: UUID
|
|
|
- private lateinit var selfAccount: BankAccount
|
|
|
-
|
|
|
private val log = LoggerFactory.getLogger(javaClass)
|
|
|
|
|
|
- init {
|
|
|
- selfId = UUID.randomUUID()
|
|
|
- selfAccount = addAccount(selfId)
|
|
|
- }
|
|
|
-
|
|
|
- override fun getId(): UUID {
|
|
|
- return selfId
|
|
|
- }
|
|
|
-
|
|
|
- override fun hasPerson(person: UUID): Boolean {
|
|
|
- return personToAccountTable.any { it.first == person }
|
|
|
- }
|
|
|
-
|
|
|
- override fun hasAccount(account: UUID): Boolean {
|
|
|
- return accountMap.containsKey(account)
|
|
|
- }
|
|
|
-
|
|
|
- final override fun addAccount(person: UUID): BankAccount {
|
|
|
- val account = if (person != selfId) {
|
|
|
- val accountId = if (!accountMap.containsKey(person)) person else UUID.randomUUID() //first account assign to personId
|
|
|
- DemoOwnerAccout(DemoAccount(accountId, person), this)
|
|
|
- } else {
|
|
|
- DemoOwnerAccout(DemoSelfAccount(selfId), this)
|
|
|
- }
|
|
|
- accountMap[account.id] = account
|
|
|
- personToAccountTable.add(person to account.id)
|
|
|
- return account
|
|
|
- }
|
|
|
-
|
|
|
- override fun getAccounts(person: UUID): Collection<BankAccount> {
|
|
|
- return personToAccountTable.filter { it.first == person }.map { accountMap[it.second] }.filterNotNull()
|
|
|
- }
|
|
|
-
|
|
|
override fun exchange(to: BankAccount, from: BankAccount, amount: Long): CompletableFuture<Any> {
|
|
|
val e = ExchangeStartEvent(UUID.randomUUID(), to, from, amount)
|
|
|
val ret = CompletableFuture<Any>()
|
|
@@ -142,7 +120,7 @@ class GlobalBank : Bank {
|
|
|
suspend fun pollLater(e: BankEvent): Boolean {
|
|
|
return when (e) {
|
|
|
is ExchangeStartEvent -> {
|
|
|
- CoinUtils.makeSomeStrategy(CoinStrategy(e.amount, if (e.from.id != emitter.id) e.from.coins else null, if (e.from.id != selfAccount.id) e.to.coins else null)).let { st ->
|
|
|
+ CoinUtils.makeSomeStrategy(CoinStrategy(e.amount, if (e.from.id != emitter.id) e.from.coins else null, if (e.from.id != selfId) e.to.coins else null)).let { st ->
|
|
|
if (st.res!!.isOk()) {
|
|
|
st
|
|
|
} else if (st.res!!.isErr()) {
|
|
@@ -152,7 +130,7 @@ class GlobalBank : Bank {
|
|
|
TODO()
|
|
|
}
|
|
|
}?.let { st ->
|
|
|
- log.info("${selfAccount} do ${st.res?.name} transaction: ${e.from} -> ${e.to} = ${e.amount}")
|
|
|
+ log.info("${selfId} do ${st.res?.name} transaction: ${e.from} -> ${e.to} = ${e.amount}")
|
|
|
var fromAmount = e.from.overall
|
|
|
var toAmount = e.to.overall
|
|
|
|
|
@@ -168,7 +146,7 @@ class GlobalBank : Bank {
|
|
|
val newIds = emitter.emit(st.leftEmit!!)
|
|
|
val newCoins = emitter.extract(newIds)
|
|
|
e.from.accept(newCoins)
|
|
|
- if (e.from.id != selfAccount.id) {
|
|
|
+ if (e.from.id != selfId) {
|
|
|
emitter.free(newCoins)
|
|
|
} else {
|
|
|
//do nothing
|
|
@@ -190,7 +168,7 @@ class GlobalBank : Bank {
|
|
|
}
|
|
|
val newCoins = emitter.extract(newIds)
|
|
|
e.to.accept(newCoins)
|
|
|
- if (e.to.id != selfAccount.id) {
|
|
|
+ if (e.to.id != selfId) {
|
|
|
emitter.free(newCoins)
|
|
|
} else {
|
|
|
//do nothing
|
|
@@ -205,7 +183,7 @@ class GlobalBank : Bank {
|
|
|
e.to.accept(someCoins)
|
|
|
someCoins.forEach { localCoins.remove(it.id) }
|
|
|
emitter.accept(localCoins.values)
|
|
|
- if (e.to.id != selfAccount.id) {
|
|
|
+ if (e.to.id != selfId) {
|
|
|
emitter.free(someCoins)
|
|
|
} else {
|
|
|
//do nothing
|
|
@@ -220,7 +198,7 @@ class GlobalBank : Bank {
|
|
|
e.from.accept(someCoins)
|
|
|
someCoins.forEach { localCoins.remove(it.id) }
|
|
|
emitter.accept(localCoins.values)
|
|
|
- if (e.from.id != selfAccount.id) {
|
|
|
+ if (e.from.id != selfId) {
|
|
|
emitter.free(someCoins)
|
|
|
} else {
|
|
|
//do nothing
|
|
@@ -289,14 +267,25 @@ class GlobalBank : Bank {
|
|
|
|
|
|
@PostConstruct
|
|
|
fun start() {
|
|
|
+ GlobalScope.launch {
|
|
|
+ do {
|
|
|
+ tick()
|
|
|
+ delay(200)
|
|
|
+ } while (true)
|
|
|
+ }
|
|
|
emitter.listen { e ->
|
|
|
log.info("emitter event ${e.javaClass.name}")
|
|
|
when (e) {
|
|
|
is EmitterStartRecalculationEvent -> {
|
|
|
- globalQueue.offerFirst(BankPauseOnRecalcEvent(UUID.randomUUID(), e))
|
|
|
+ globalQueue.offerLast(BankPauseOnRecalcEvent(UUID.randomUUID(), e))
|
|
|
+ runBlocking {
|
|
|
+ do {
|
|
|
+ delay(500)
|
|
|
+ } while (!(globalQueue.peekFirst() is BankPauseOnRecalcEvent))
|
|
|
+ }
|
|
|
}
|
|
|
is EmitterStopRecalculationEvent -> {
|
|
|
- accountMap.values.map { it to it.coins?.map { it.id }?.intersect(e.nullCoins) }.filter { it.second?.isNotEmpty() ?: false }.forEach { ap ->
|
|
|
+ accounts.map { it to it.coins?.map { it.id }?.intersect(e.nullCoins) }.filter { it.second?.isNotEmpty() ?: false }.forEach { ap ->
|
|
|
val nullCoins = ap.second?.map {
|
|
|
//(ap.first as DemoAccount).internalCoins.get(it)?.incEra() //TODO убрать!
|
|
|
ap.first.extractOne(it)
|
|
@@ -309,7 +298,6 @@ class GlobalBank : Bank {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @Scheduled(initialDelay = 1000L, fixedDelay = 100L)
|
|
|
fun tick() {
|
|
|
var pollCount = 0
|
|
|
while (globalQueue.isNotEmpty() && pollCount < maxPollCount) {
|
|
@@ -321,7 +309,7 @@ class GlobalBank : Bank {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- class DemoOwnerAccout(val inner: BankAccount, val owner: GlobalBank) : BankAccount {
|
|
|
+ class DemoOwnerAccout(val inner: BankAccount, val owner: GlobalBankDemo) : BankAccount {
|
|
|
|
|
|
override fun getOverall(): Long {
|
|
|
return inner.overall
|
|
@@ -331,6 +319,10 @@ class GlobalBank : Bank {
|
|
|
return inner.coins
|
|
|
}
|
|
|
|
|
|
+ override fun getOwnerId(): UUID {
|
|
|
+ TODO("Not yet implemented")
|
|
|
+ }
|
|
|
+
|
|
|
override fun acceptOne(coin: Coin) {
|
|
|
if (owner.coinToAccountMap.containsKey(coin.id)) {
|
|
|
throw RuntimeException("already owned")
|
|
@@ -393,15 +385,8 @@ class ExchangeStartEvent(eventId: UUID, val to: BankAccount, val from: BankAccou
|
|
|
|
|
|
class BankPauseOnRecalcEvent(eventId: UUID, val emitterEvent: EmitterStartRecalculationEvent, var paused: Boolean = true) : AbstractBankEvent(eventId)
|
|
|
|
|
|
-class DemoHuman(val selfId: UUID, val name: String) : NaturalPerson {
|
|
|
-
|
|
|
- override fun getId(): UUID = selfId
|
|
|
-
|
|
|
-}
|
|
|
|
|
|
-class DemoSelfAccount(val selfId: UUID) : DemoAccount(selfId, selfId)
|
|
|
-
|
|
|
-open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
+open class DemoAccount(val accountId: UUID, val personId: UUID) : BankAccount {
|
|
|
|
|
|
val internalCoins = mutableMapOf<UUID, Coin>()
|
|
|
var _overall: Long? = null
|
|
@@ -417,10 +402,10 @@ open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
val coin = internalCoins.remove(coinId)!!
|
|
|
if (coin.current == 0L) {
|
|
|
_overall = null
|
|
|
- log.info("${this} leave coin ${coin.current}")
|
|
|
+ log.debug("${this} leave coin ${coin.current}")
|
|
|
} else {
|
|
|
decOverall(coin.current)
|
|
|
- log.info("${this} credit coin ${coin.current}")
|
|
|
+ log.debug("${this} credit coin ${coin.current}")
|
|
|
}
|
|
|
coin
|
|
|
} else {
|
|
@@ -441,6 +426,10 @@ open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
return internalCoins.values
|
|
|
}
|
|
|
|
|
|
+ override fun getOwnerId(): UUID {
|
|
|
+ return personId
|
|
|
+ }
|
|
|
+
|
|
|
private fun decOverall(amount: Long) {
|
|
|
if (amount == 0L) throw IllegalArgumentException("should not be 0")
|
|
|
if (_overall != null) {
|
|
@@ -468,7 +457,7 @@ open class DemoAccount(val accountId: UUID, personId: UUID) : BankAccount {
|
|
|
} else if (internalCoins.containsKey(coin.id)) {
|
|
|
throw IllegalArgumentException("same coin is not acceptable")
|
|
|
}
|
|
|
- log.info("${this} debit coin ${CoinUtils.sum(listOf(coin))}")
|
|
|
+ log.debug("${this} debit coin ${CoinUtils.sum(listOf(coin))}")
|
|
|
}
|
|
|
|
|
|
|