Repository navigation
Expand file tree
/
Copy pathcloudcomputing.exs
More file actions
143 lines (117 loc) · 4.85 KB
/
Copy pathcloudcomputing.exs
File metadata and controls
143 lines (117 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# SensorSimulator
#
# This Elixir module simulates sensor data to demonstrate the use of Erlang Term Storage (ETS).
# It continuously generates temperature readings, stores them in ETS, and analyzes the temperature trends.
# Great for learning ETS in Elixir and understanding data handling in concurrent environments.
#
# Usage: Run the module to start the sensor simulation and temperature trend analysis.
defmodule SensorSimulator do
use GenServer
@max_entries 500000 # Approximate to manage memory usage
@cleanup_interval 1000 # Milliseconds
# Starts the GenServer
def start_link(_) do
_pid = GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
# Initializes the ETS table and starts the sensor simulation
def init(:ok) do
# Use `:named_table` to allow access from other processes
:ets.new(:sensor_data, [:set, :public, :named_table])
_ref = schedule_sensor_read()
_ref = schedule_cleanup()
{:ok, %{}}
end
# Schedules sensor readings
defp schedule_sensor_read do
_ref = Process.send_after(self(), :read_sensor, 1)
end
# Schedules ETS cleanup
defp schedule_cleanup do
_ref = Process.send_after(self(), :cleanup, @cleanup_interval)
end
# Handles sensor readings
def handle_info(:read_sensor, state) do
current_time = System.os_time(:millisecond)
true_temp = calculate_true_temp(current_time)
simulated_temp = simulate_sensor_reading(true_temp)
:ets.insert(:sensor_data, {current_time, simulated_temp})
_ref = schedule_sensor_read()
{:noreply, state}
end
# Handles ETS cleanup
def handle_info(:cleanup, state) do
_result = cleanup_ets()
_ref = schedule_cleanup()
{:noreply, state}
end
# Cleans up old entries in ETS to manage memory
defp cleanup_ets do
current_size = :ets.info(:sensor_data, :size)
if current_size > @max_entries do
oldest_allowed_time = System.os_time(:millisecond) - @max_entries
:ets.select_delete(:sensor_data, [{{:"$1", :"$2"}, [{:<, :"$1", oldest_allowed_time}], [true]}])
end
end
# Calculates the true temperature
defp calculate_true_temp(time) do
# 5 minutes sine wave for a day
amplitude = 10.0
day_period = 5 * 60 * 1000
base_temp = 20.0
base_temp + amplitude * :math.sin(2 * :math.pi * time / day_period)
end
# Simulates a sensor reading with ±15% variance
defp simulate_sensor_reading(true_temp) do
variance = :rand.uniform() * 0.15 * true_temp
if :rand.uniform() > 0.5, do: true_temp + variance, else: true_temp - variance
end
# Public function to start the analysis process
def start_analysis do
_status = Task.start(fn -> analyze_temperature() end)
end
# Periodically analyze temperature data
defp analyze_temperature do
analyze_and_report()
:timer.sleep(10_000) # Use :timer.sleep for more reliable behavior
analyze_temperature()
end
# Analyzes and reports the temperature trend
defp analyze_and_report do
current_time = System.os_time(:millisecond)
past_5_min_readings = readings_in_last(5 * 60 * 1000, current_time)
current_avg = average_temperature(past_5_min_readings)
past_15_sec_readings = readings_in_last(15_000, current_time)
current_15_sec_avg = average_temperature(past_15_sec_readings)
one_min_ago_readings = readings_in_last(15_000, current_time - 60_000)
one_min_ago_avg = average_temperature(one_min_ago_readings)
trend = if current_15_sec_avg > one_min_ago_avg, do: "rising", else: "falling"
if Enum.count(past_5_min_readings) > 0, do: IO.puts("Past 5 min readings count: #{Enum.count(past_5_min_readings)}. Average temp (last 5 min): #{current_avg}, Trend: #{trend}")
end
# Fetches readings from the last specified milliseconds
defp readings_in_last(milliseconds, current_time) do
match_spec = [{{:"$1", :"$2"}, [{:>=, :"$1", current_time - milliseconds}], [{{:"$1", :"$2"}}]}]
readings = :ets.select(:sensor_data, match_spec)
readings
end
# Calculates the average temperature
defp average_temperature(readings) do
count = Enum.count(readings)
if count > 0 do
Enum.sum(Enum.map(readings, fn {_time, temp} -> temp end)) / count
else
nil # Or some default value if no readings are available
end
end
end
# Starting the server and the analysis process
IO.puts (" _( '`. _\\x/__")
IO.puts (" .=(`( . ) /X\\ CLOUD COMPUTING")
IO.puts ("---- Welcome to Cloud Computing: Elixir Edition. ---- ")
IO.puts ("This program simulates a day's temperature fluctuations every 5 minutes.")
IO.puts ("- Temperatures are collected continuously from simulated sensors")
IO.puts ("- sensors deviate +/- 15% from the true temperature.")
IO.puts ("- Every 10 seconds, an analysis process analyzes the temperatures.")
{:ok, _pid} = SensorSimulator.start_link([])
SensorSimulator.start_analysis()
# Prevent the script from exiting immediately
Process.sleep(:infinity)