# typehorloge
=
{
jour
:
int;mois
:
int;heure
:
int;minute
:
int;seconde
:
int}
;;
type horloge =
{ jour: int;
mois: int;
heure: int;
minute: int;
seconde: int }
# letencode
date
=
let
str
=
String.create
5
in
str
.[
0
]
<-
char_of_int
date
.
jour;
str
.[
1
]
<-
char_of_int
date
.
mois;
str
.[
2
]
<-
char_of_int
date
.
heure;
str
.[
3
]
<-
char_of_int
date
.
minute;
str
.[
4
]
<-
char_of_int
date
.
seconde;
str
;;
val encode : horloge -> string = <fun>
# letdecode
str
=
{
jour
=
int_of_char
str
.[
0
]
;
mois
=
int_of_char
str
.[
1
]
;
heure
=
int_of_char
str
.[
2
]
;
minute
=
int_of_char
str
.[
3
]
;
seconde
=
int_of_char
str
.[
4
]
}
;;
val decode : string -> horloge = <fun>
# main_serveur;;
- : (in_channel -> out_channel -> 'a) -> unit = <fun>
# lethorloge_service
ic
oc
=
try
let
date
=
Unix.localtime
(Unix.time
())
in
let
date_horloge
=
{
jour
=
date
.
Unix.tm_mday;
mois
=
date
.
Unix.tm_mon
+
1
;
heure
=
date
.
Unix.tm_hour;
minute
=
date
.
Unix.tm_min;
seconde
=
date
.
Unix.tm_sec
}
in
output_string
oc
(encode
date_horloge)
;
flush
oc
with
exn
->
print_endline
"Fin du traitement"
;flush
stdout
let
main_horloge
()
=
main_serveur
horloge_service
;;
val horloge_service : 'a -> out_channel -> unit = <fun>
val main_horloge : unit -> unit = <fun>
# main_client;;
- : (in_channel -> out_channel -> 'a) -> unit = <fun>
# letclient_horloge
ic
oc
=
let
date
=
ref
{
jour
=
0
;mois
=
0
;heure
=
0
;minute
=
0
;seconde
=
0
}
in
try
while
true
do
let
buffer
=
"xxxxx"
in
ignore
(input
ic
buffer
0
5
);
date
:=
decode
buffer
;
print_endline
"BIP"
;
flush
stdout
;
Unix.sleep
3
6
0
0
done
with
exn
->
shutdown_connection
ic
;
raise
exn
;;
val client_horloge : in_channel -> 'a -> unit = <fun>
# letmain_horloge
()
=
main_client
client_horloge
;;
val main_horloge : unit -> unit = <fun>
:$\n
.
#val hostaddr : string -> Unix.inet_addr = <fun>
val my_inet_addr : unit -> Unix.inet_addr = <fun>
let
establish_server
f
saddr
=
let
sock
=
ThreadUnix.socket
Unix
.
PF_INETUnix
.
SOCK_STREAM
0
in
Unix.bind
sock
saddr;
Unix.listen
sock
5
;
while
true
do
let
(s
,_
)
=
ThreadUnix.accept
sock
in
let
ic
=
Unix.in_channel_of_descr
s
and
oc
=
Unix.out_channel_of_descr
s
in
ignore
(Thread.create
(f
ic)
oc)
done;;
val establish_server :
(in_channel -> out_channel -> 'a) -> Unix.sockaddr -> unit = <fun>
# letread
fd
=
let
buf
=
String.create
1
0
2
4
in
let
n
=
ThreadUnix.read
fd
buf
0
1
0
2
4
in
let
s
=
String.sub
buf
0
n
in
s
;;
val read : Unix.file_descr -> string = <fun>
# letget_request
fd
=
let
s
=
read
fd
in
match
Str.split
(Str.regexp
"[:]"
)(String.sub
s
0
(String.index
s
'$'
))with
[
s1]
->
s1
|
_
->
failwith
"BadRequestFormat"
;;
val get_request : Unix.file_descr -> string = <fun>
# letwrite
fd
s
=
let
leng
=
(String.length
s)
in
let
n
=
ThreadUnix.write
fd
s
0
leng
in
if
n
<
lengthen
failwith
"I/O error"
;;
val write : Unix.file_descr -> string -> unit = <fun>
# letsend_answer
fd
ss
=
let
rec
mk_answer
=
function
[]
->
":$\n"
|
[
s]
->
s
^
":$\n"
|
s::ss
->
s
^
":"
^
(mk_answer
ss)
in
write
fd
(mk_answer
ss)
;;
val send_answer : Unix.file_descr -> string list -> unit = <fun>
# letsend_cancel
=
let
s
=
"cancel:$\n"
in
function
fd
->
write
fd
s
;;
val send_cancel : Unix.file_descr -> unit = <fun>
# classcmd_fifo
=
object(self)
val
n
=
new
num_cmd_gen
val
f
=
(Queue.create
()
:
(int
*
int*
int)Queue.t)
val
m
=
Mutex.create
()
val
c
=
Condition.create
()
method
add
num_drink
paid
=
let
num_cmd
=
n#get()
in
Mutex.lock
m
;
Queue.add
(num_cmd
,
num_drink
,
paid)
f
;
Mutex.unlock
m
;
Condition.signal
c
;
num_cmd
method
wait
()
=
Mutex.lock
m
;
Condition.wait
c
m
;
let
cmd
=
Queue.take
f
in
Mutex.unlock
m
;
cmd
end
;;
class cmd_fifo :
object
val c : Condition.t
val f : (int * int * int) Queue.t
val m : Mutex.t
val n : num_cmd_gen
method add : int -> int -> int
method wait : unit -> int * int * int
end
# classnum_cmd_gen
=
object
val
mutable
x
=
0
val
m
=
Mutex.create
()
method
get()
=
Mutex.lock
m
;
x
<-
x
+
1
;
let
r
=
x
in
Mutex.unlock
m
;
r
end
;;
class num_cmd_gen :
object val m : Mutex.t val mutable x : int method get : unit -> int end
# classready_table
size
=
object
val
t
=
(Hashtbl.create
size
:
(int
,
(string
*
int))
Hashtbl.t)
val
m
=
Mutex.create
()
val
c
=
Condition.create
()
method
add
num_cmd
num_drink
change
=
Mutex.lock
m
;
Hashtbl.add
t
num_cmd
(num_drink
,
change)
;
Mutex.unlock
m
;
Condition.broadcast
c
method
wait
num_cmd
=
Mutex.lock
m;
while
not(Hashtbl.mem
t
num_cmd)
do
Condition.wait
c
m
done
;
let
cmd
=
Hashtbl.find
t
num_cmd
in
Hashtbl.remove
t
num_cmd
;
Mutex.unlock
m
;
cmd
end
;;
class ready_table :
int ->
object
val c : Condition.t
val m : Mutex.t
val t : (int, string * int) Hashtbl.t
method add : int -> string -> int -> unit
method wait : int -> string * int
end
# classmachine
(f_cmd0
:
cmd_fifo)(t_ready0
:
ready_table)
=
object(self)
val
f_cmd
=
f_cmd0
val
t_ready
=
t_ready0
val
mutable
nb_available_drinks
=
0
val
drinks_table
=
[|
{
name
=
"cafe"
;real_stock
=
1
0
;virtual_stock
=
1
0
;price
=
3
0
0
};
{
name
=
"the"
;real_stock
=
5
;virtual_stock
=
5
;price
=
2
5
0
};
{
name
=
"chocolat"
;real_stock
=
1
0
;virtual_stock
=
1
0
;price
=
2
5
0
}
|]
val
mutable
cash
=
0
val
m
=
Mutex.create()
initializer
nb_available_drinks
<-
Array.length
drinks_table
method
get_drink_price
i
=
drinks_table
.
(i).
price
method
get_drink_index
s
=
array_index
drinks_table
(fun
d
->
d
.
name=
s)
method
get_menu
()
=
let
f
d
ns
=
if
d
.
real_stock
>
0
then
d
.
name::nselse
ns
in
Array.fold_right
f
drinks_table
[]
method
cancel_cmd
num_drink
=
let
drink
=
drinks_table
.
(num_drink)in
drink
.
virtual_stock
<-
drink
.
virtual_stock+
1
method
set_cmd
num_drink
paid
=
f_cmd#add
num_drink
paid
method
wait_cmd
num_cmd
=
t_ready#wait
num_cmd
method
deliver_drink
num_drink
=
let
drink
=
drinks_table
.
(num_drink)in
drink
.
real_stock
<-
drink
.
real_stock-
1
;
if
drink
.
real_stock
=
0
then
nb_available_drinks
<-
nb_available_drinks
-
1
method
run()
=
while
nb_available_drinks
>
0
do
let
(num_cmd
,
num_drink
,
amount)
=
f_cmd#wait
()
in
let
drink
=
drinks_table
.
(num_drink)in
let
change
=
amount
-
drink
.
pricein
Mutex.lock
m
;
if
(drink
.
virtual_stock
>
0
)
&
(cash
>=
change)
then
begin
drink
.
virtual_stock
<-
drink
.
virtual_stock-
1
;
cash
<-
cash
+
drink
.
price;
t_ready#add
num_cmd
drink
.
namechange
end
else
t_ready#add
num_cmd
"cancel"
0
;
Mutex.unlock
m
done
end
;;
class machine :
cmd_fifo ->
ready_table ->
object
val mutable cash : int
val drinks_table : drink_descr array
val f_cmd : cmd_fifo
val m : Mutex.t
val mutable nb_available_drinks : int
val t_ready : ready_table
method cancel_cmd : int -> unit
method deliver_drink : int -> unit
method get_drink_index : string -> int
method get_drink_price : int -> int
method get_menu : unit -> string list
method run : unit -> unit
method set_cmd : int -> int -> int
method wait_cmd : int -> string * int
end
# typedrink_descr
=
{
name
:
string;
mutable
real_stock
:
int;
mutable
virtual_stock
:
int;
price
:
int
}
;;
# letarray_index
t
f
=
let
i
=
ref
0
in
let
n
=
Array.length
t
in
while
(
!
i
<
n)
&
(not
(f
t
.
(!
i)))do
incr
i
done
;
if
!
i=
nthen
raise
Not_found
else
!
i;;
val array_index : 'a array -> ('a -> bool) -> int = <fun>
# letwaiter
mach
ic
oc
=
let
f_in
=
Unix.descr_of_in_channel
ic
in
let
f_out
=
Unix.descr_of_out_channel
oc
in
(try
send_answer
f_out
(mach#get_menu())
;
let
drink_name
=
get_request
f_in
in
let
num_drink
=
mach#get_drink_index
drink_name
in
let
drink_price
=
mach#get_drink_price
num_drink
in
send_answer
f_out
[
string_of_intdrink_price
]
;
let
paid
=
int_of_string
(get_request
f_in)
in
if
paid
<
drink_price
then
failwith
"NotEnough"
;
let
num_cmd
=
mach#set_cmd
num_drink
paid
in
let
drink_name
,
change
=
mach#wait_cmd
num_cmd
in
mach#deliver_drink
num_drink;
send_answer
f_out
[
drink_name;(string_of_int
change)
]
with
Not_found
->
send_cancel
f_out
|
Failure(
"int_of_string"
)->
send_cancel
f_out
|
Failure(
"I/O error"
)->
send_cancel
f_out
|
Failure(
"NotEnough"
)->
send_cancel
f_out
|
Failure(
"BadRequestFormat"
)->
send_cancel
f_out
);
close_in
ic
;
flush
oc
;
close_out
oc
;
Thread.exit
()
;;
val waiter :
< deliver_drink : 'a -> 'b; get_drink_index : string -> 'a;
get_drink_price : 'a -> int; get_menu : unit -> string list;
set_cmd : 'a -> int -> 'c; wait_cmd : 'c -> string * int; .. > ->
in_channel -> out_channel -> unit = <fun>
# letmain
()
=
if
Array.length
Sys.argv
<
2
then
begin
Printf.eprintf
"usage : %s port\n"
Sys.argv
.
(0
);
exit
1
end
else
begin
let
port
=
int_of_string
Sys.argv
.
(1
)in
let
f_cmd
=
new
cmd_fifo
in
let
t_ready
=
new
ready_table
in
let
mach
=
new
machine
f_cmd
(t_ready
1
3
)in
ignore
(Thread.create
mach#run
())
;
establish_server
(waiter
mach)
(Unix
.
ADDR_INET(my_inet_addr
()
,
port))
end
;;
val main : unit -> unit = <fun>