Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

зделяль #3

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions labs/lab6-example/.scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
binPack.parentConstructors = true
maxColumn = 120
includeCurlyBraceInSelectChains = false
align = most
version = "2.3.2"
trailingCommas = preserve
newlines.penalizeSingleSelectMultiArgList = false
newlines.alwaysBeforeMultilineDef = false
1 change: 1 addition & 0 deletions labs/lab6-example/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ lazy val akkaVersion = "2.6.3"
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor-typed" % akkaVersion,
"ch.qos.logback" % "logback-classic" % "1.2.3",
"com.github.pureconfig" %% "pureconfig" % "0.12.3",
"com.typesafe.akka" %% "akka-actor-testkit-typed" % akkaVersion % Test,
"org.scalatest" %% "scalatest" % "3.1.0" % Test
)
1 change: 1 addition & 0 deletions labs/lab6-example/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.0")
10 changes: 10 additions & 0 deletions labs/lab6-example/src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
number-of-chefs = 10
number-of-customers = 2
seed = 209384
chef-config = { type = "chef-config", cooking-time-boundaries = { type = "boundaries", min = 1, max = 2 }}
customer-config = { type = "customer-config",
order-decision-time-boundaries = { type = "boundaries", min = 1, max = 2 },
eating-time-boundaries = { type = "boundaries", min = 1, max = 2 },
order-size-boundaries: { type = "boundaries", min = 1, max = 2 },
khinkali-amount-boundaries = { type = "boundaries", min = 1, max = 2 }
}
53 changes: 41 additions & 12 deletions labs/lab6-example/src/main/scala/khinkali/Cafe.scala
Original file line number Diff line number Diff line change
@@ -1,24 +1,53 @@
package khinkali

import akka.actor.typed.Behavior
import java.time.LocalDateTime

import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.Behaviors
import pureconfig._
import pureconfig.generic.auto._

import scala.util.Random

object Cafe {
sealed trait Command
case object Start extends Command
case object Start extends Command
case object CustomerLeft extends Command

def apply(config: Config): Behavior[Command] = {
val random = new Random(config.seed)
var numberOfCustomers = config.numberOfCustomers
var startTime = System.currentTimeMillis()

Behaviors.receive { (ctx, msg) =>
msg match {
case Start =>
val waiter = ctx.spawn(Waiter(), Constants.waiter)
val chefs = (1 to config.numberOfChefs)
.map(i => ctx.spawn(Chef(waiter, config.chefConfig, random.nextLong()), s"${Constants.chef}$i"))
val customers = (1 to config.numberOfCustomers).map { i =>
ctx.spawn(Customer(waiter, config.customerConfig, random.nextLong()), s"${Constants.customer}$i")
}

waiter ! Waiter.Start
waiter ! Waiter.SetChefs(chefs.toList)

def apply(): Behavior[Command] = Behaviors.receive { (ctx, msg) =>
msg match {
case Start =>
val waiter = ctx.spawn(Waiter(), "Waiter")
val customers = (1 to 10).map { i => ctx.spawn(Customer(waiter, CustomerOrder(List(Khinkali(Stuffing.Beef, 10)))), s"Customer$i")}
startTime = System.currentTimeMillis()

waiter ! ???
customers.foreach { c =>
ctx.watchWith(c, CustomerLeft)
c ! Customer.Start
}
Behaviors.same

customers.foreach { c =>
c ! Customer.Start
}
Behaviors.same
case CustomerLeft =>
numberOfCustomers -= 1
if (numberOfCustomers == 0) {
val endTime = System.currentTimeMillis()
ctx.system.log.info(s"All customers left, the overall processing time is ${endTime - startTime} ms")
}
Behaviors.same
}
}
}
}
43 changes: 41 additions & 2 deletions labs/lab6-example/src/main/scala/khinkali/Chef.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,51 @@
package khinkali

import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{ActorRef, Behavior}

import scala.concurrent.duration._
import scala.util.Random

object Chef {
sealed trait Command

case class TakeOrder(order: Order, replyTo: ActorRef[Result], customer: ActorRef[Customer.Eat.type]) extends Command
case class FinishOrder(orderId: Int, customer: ActorRef[Customer.Eat.type]) extends Command
case class FinishOrder(orderId: Int, customer: ActorRef[Customer.Eat.type]) extends Command

val random: Random = new Random()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Опять какой-то глобальный рандом, который шерится между всеми шефами. Можно хотя бы в Behaviors.setup его инициализирвоать


def apply(waiter: ActorRef[Waiter.Command], chefConfig: ChefConfig, seed: Long): Behavior[Command] = {
random.setSeed(seed)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В итоге у рандома будет сид последнего созданного шефа

waitForOrder(waiter, chefConfig)
}

def waitForOrder(waiter: ActorRef[Waiter.Command], chefConfig: ChefConfig): Behavior[Command] = Behaviors.receive {
(ctx, msg) =>
msg match {
case TakeOrder(order, replyTo, customer) =>
ctx.log.info(s"taking order ${order.orderId}")
replyTo ! Result.Ok
ctx.scheduleOnce(
order.preparationTime(random, chefConfig.cookingTimeBoundaries).second,
ctx.self,
FinishOrder(order.orderId, customer)
)
cook(waiter, chefConfig)
case _ => Behaviors.same
}
}

def apply(): Behavior[Command] = ???
def cook(waiter: ActorRef[Waiter.Command], chefConfig: ChefConfig): Behavior[Command] = Behaviors.receive {
(ctx, msg) =>
msg match {
case TakeOrder(_, replyTo, _) =>
replyTo ! Result.Busy
Behaviors.same
case FinishOrder(orderId, customer) =>
ctx.log.info(s"finished cooking $orderId")
waiter ! Waiter.ServeOrder(orderId, customer)
waitForOrder(waiter, chefConfig)
case _ => Behaviors.same
}
}
}
26 changes: 26 additions & 0 deletions labs/lab6-example/src/main/scala/khinkali/Config.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package khinkali

import scala.util.Random

case class Boundaries(min: Int, max: Int) {
def randomWithin(random: Random): Int = {
random.between(this.min, this.max)
}
}

case class Config(
numberOfCustomers: Int,
numberOfChefs: Int,
seed: Long,
chefConfig: ChefConfig,
customerConfig: CustomerConfig
)

case class CustomerConfig(
orderDecisionTimeBoundaries: Boundaries,
eatingTimeBoundaries: Boundaries,
orderSizeBoundaries: Boundaries,
khinkaliAmountBoundaries: Boundaries
)

case class ChefConfig(cookingTimeBoundaries: Boundaries)
8 changes: 8 additions & 0 deletions labs/lab6-example/src/main/scala/khinkali/Constants.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package khinkali

object Constants {
val chef = "Chef"
val customer = "Customer"
val waiter = "Waiter"
val cafe = "Cafe"
}
60 changes: 42 additions & 18 deletions labs/lab6-example/src/main/scala/khinkali/Customer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,69 @@ package khinkali

import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout

import scala.util.{Failure, Random, Success}
import scala.concurrent.duration._

object Customer {
sealed trait Command

case object Start extends Command
case object Start extends Command
case class LeaveOrder(order: CustomerOrder) extends Command
case object Eat extends Command
case object Leave extends Command
case object Eat extends Command
case object Leave extends Command

def apply(waiter: ActorRef[Waiter.Command], order: CustomerOrder): Behavior[Command] =
start(order, waiter)
implicit val timeout: Timeout = Timeout(1.second)

def start(order: CustomerOrder, waiter: ActorRef[Waiter.Command]): Behavior[Command] =
def apply(
waiter: ActorRef[Waiter.Command],
customerConfig: CustomerConfig,
seed: Long
): Behavior[Command] = {
val random: Random = new Random(seed)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот здесь лучше чем в шефах

start(customerConfig, waiter, random)
}

def start(
customerConfig: CustomerConfig,
waiter: ActorRef[Waiter.Command],
random: Random
): Behavior[Command] =
Behaviors.receive { (ctx, msg) =>
msg match {
case Start =>
ctx.scheduleOnce(1.second, ctx.self, LeaveOrder(order))
leaveOrder(waiter)
ctx.scheduleOnce(
customerConfig.orderDecisionTimeBoundaries.randomWithin(random).second,
ctx.self,
LeaveOrder(
CustomerOrder.generateOrder(
random,
customerConfig
)
)
)
leaveOrder(customerConfig, waiter, random)
case _ => Behaviors.same
}
}

def leaveOrder(waiter: ActorRef[Waiter.Command]): Behavior[Command] = Behaviors.receive { (ctx, msg) =>
msg match {
case LeaveOrder(order) =>
ctx.log.info(s"Leaving order $order")
waiter ! ???
waitForEat
case _ => Behaviors.same
def leaveOrder(customerConfig: CustomerConfig, waiter: ActorRef[Waiter.Command], random: Random): Behavior[Command] =
Behaviors.receive { (ctx, msg) =>
msg match {
case LeaveOrder(order) =>
ctx.log.info(s"Leaving order $order")
waiter ! Waiter.AcceptOrder(order, ctx.self)
waitForEat(customerConfig, random)
case _ => Behaviors.same
}
}
}

def waitForEat: Behavior[Command] = Behaviors.receive { (ctx, msg) =>
def waitForEat(customerConfig: CustomerConfig, random: Random): Behavior[Command] = Behaviors.receive { (ctx, msg) =>
msg match {
case Eat =>
ctx.log.info(s"Now eating")
ctx.scheduleOnce(1.second, ctx.self, Leave)
ctx.scheduleOnce(customerConfig.eatingTimeBoundaries.randomWithin(random).second, ctx.self, Leave)
waitToLeave
case _ => Behaviors.same
}
Expand Down
15 changes: 13 additions & 2 deletions labs/lab6-example/src/main/scala/khinkali/Main.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
package khinkali

import akka.actor.typed.ActorSystem
import pureconfig._
import pureconfig.generic.auto._

object Main extends App {
val system: ActorSystem[Cafe.Command] = ActorSystem(Cafe(), "Cafe")
system ! Cafe.Start

val config = ConfigSource.default
.load[Config]

config match {
case Left(value) =>
print(s"Error $value")
case Right(config) =>
val system: ActorSystem[Cafe.Command] = ActorSystem(Cafe(config), Constants.cafe)
system ! Cafe.Start
}
}
34 changes: 30 additions & 4 deletions labs/lab6-example/src/main/scala/khinkali/Model.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
package khinkali

import akka.actor.typed.ActorRef

import scala.util.Random

case class CustomerOrder(dishes: List[Khinkali]) {
def toOrder(id: Int): Order = Order(id, dishes)
}
case class Order(orderId: Int, dishes: List[Khinkali])

object CustomerOrder {
def generateOrder(random: Random, customerConfig: CustomerConfig): CustomerOrder = {
CustomerOrder(
(1 to customerConfig.orderSizeBoundaries.randomWithin(random))
.map[Khinkali](_ => Khinkali(random.nextInt(), customerConfig.khinkaliAmountBoundaries.randomWithin(random)))
.toList
)
}
}
case class Order(orderId: Int, dishes: List[Khinkali]) {
def preparationTime(random: Random, cookingTimeBoundaries: Boundaries): Int = {
this.dishes.map(_.amount * cookingTimeBoundaries.randomWithin(random)).sum
}
}

case class Khinkali(stuffing: Stuffing, amount: Int)

sealed trait Stuffing

object Stuffing {
case object Beef extends Stuffing
case object Mutton extends Stuffing
case object Beef extends Stuffing
case object Mutton extends Stuffing
case object CheeseAndMushrooms extends Stuffing

implicit def intToStuffing(x: Int): Stuffing = {
x % 3 match {
case 0 => Beef
case 1 => Mutton
case _ => CheeseAndMushrooms
}
}
}

sealed trait Result
object Result {
case object Ok extends Result
case object Ok extends Result
case object Busy extends Result
}
Loading