-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 5ee8f92
Showing
8 changed files
with
1,625 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Auto detect text files and perform LF normalization | ||
* text=auto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.venv*/ | ||
venv*/ | ||
__pycache__/ | ||
flask_session/ | ||
terrains/ | ||
imgs/ | ||
test.py | ||
LoRaWAN-US915.pl |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
#!/usr/bin/perl -w | ||
# | ||
# Script to create png file from 2d terrain data | ||
# | ||
# copied from https://github.com/rainbow-src/sensors/tree/master/terrain%20generators | ||
|
||
use GD; | ||
use strict; | ||
|
||
my ($display_x, $display_y) = (800, 800); # 800x800 pixel display pane | ||
|
||
my ($terrain, $norm_x, $norm_y) = (0, 0, 0); | ||
|
||
my @sensors = (); | ||
my @gws = (); | ||
|
||
die "usage: $0 <terrain_file.txt> <output.png>\n" | ||
unless (@ARGV == 2); | ||
|
||
my $terrain_file = $ARGV[0]; | ||
my $output_file = $ARGV[1]; | ||
|
||
|
||
# COLLECT INFO FROM INPUT FILE | ||
|
||
open(FH, "<$terrain_file") or | ||
die "Error: could not open terrain file $terrain_file\n"; | ||
|
||
while(<FH>){ | ||
chomp; | ||
if (/^# stats: (.*)/){ | ||
my $stats_line = $1; | ||
if ($stats_line =~ /terrain=([0-9]+\.[0-9]+)m\^2/){ | ||
$terrain = $1; | ||
} | ||
$norm_x = sqrt($terrain); | ||
$norm_y = sqrt($terrain); | ||
} elsif (/^# node coords: (.*)/){ | ||
my $sensor_coord = $1; | ||
my @coords = split(/\] /, $sensor_coord); | ||
@sensors = map { /([0-9]+) \[([0-9]+\.[0-9]+) ([0-9]+\.[0-9]+)/; [$1, $2, $3]; } @coords; | ||
} elsif (/^# gateway coords: (.*)/){ | ||
my $sensor_coord = $1; | ||
my @coords = split(/\] /, $sensor_coord); | ||
@gws = map { /([A-Z]+) \[([0-9]+\.[0-9]+) ([0-9]+\.[0-9]+)/; [$1, $2, $3]; } @coords; | ||
} | ||
} | ||
close(FH); | ||
|
||
|
||
### GENERATE SVG IMAGE OF TERRAIN ### | ||
|
||
my $im = new GD::Image->new($display_x, $display_y); | ||
my $white = $im->colorAllocate(255,255,255); | ||
my $black = $im->colorAllocate(0,0,0); | ||
my $red = $im->colorAllocate(255,0,0); | ||
|
||
foreach my $po (@sensors){ | ||
my ($s, $x, $y) = @$po; | ||
($x, $y) = (int(($x * $display_x)/$norm_x), int(($y * $display_y)/$norm_y)); | ||
$im->rectangle($x-2, $y-2, $x+2, $y+2, $black); | ||
# $im->string(gdSmallFont,$x-2,$y-20,$s,$white); | ||
} | ||
#$im->filledRectangle(3-3, $display_y/2-3, 3+3, $display_y/2+3, $black); | ||
foreach my $po (@gws){ | ||
my ($s, $x, $y) = @$po; | ||
($x, $y) = (int(($x * $display_x)/$norm_x), int(($y * $display_y)/$norm_y)); | ||
$im->rectangle($x-5, $y-5, $x+5, $y+5, $red); | ||
$im->string(gdGiantFont,$x-2,$y-20,$s,$white); | ||
} | ||
|
||
|
||
open(FILEOUT, ">$output_file") or | ||
die "could not open file $output_file for writing!"; | ||
binmode FILEOUT; | ||
print FILEOUT $im->png; | ||
close FILEOUT; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
#!/usr/bin/perl -w | ||
# | ||
# Script to create a 2D terrain of nodes | ||
# | ||
# modified copy of https://github.com/rainbow-src/sensors/tree/master/terrain%20generators | ||
|
||
use strict; | ||
use Math::Random; | ||
|
||
(@ARGV==3) || die "usage: $0 <terrain_side_size_(m)> <num_of_nodes> <num_of_gateways>\ne.g. $0 100 500 10\n"; | ||
|
||
my $tx = $ARGV[0]; | ||
my $nodes = $ARGV[1]; | ||
my $gws = $ARGV[2]; | ||
|
||
($tx < 1) && die "grid side must be higher than 1 meters!\n"; | ||
($nodes < 1) && die "number of nodes must be higher than 1!\n"; | ||
|
||
my @sensors; | ||
my @gws; | ||
my %coords; | ||
|
||
for(my $i=1; $i<=$nodes; $i++){ | ||
my ($x, $y) = (int(rand($tx*10)), int(rand($tx*10))); | ||
($x, $y) = ($x/10, $y/10); | ||
while (exists $coords{$x}{$y}){ | ||
($x, $y) = (int(rand($tx*10)), int(rand($tx*10))); | ||
($x, $y) = ($x/10, $y/10); | ||
} | ||
$coords{$x}{$y} = 1; | ||
push(@sensors, [$x, $y]); | ||
} | ||
for(my $i=1; $i<=$gws; $i++){ | ||
my ($x, $y) = (int(rand($tx*10)), int(rand($tx*10))); | ||
($x, $y) = ($x/10, $y/10); | ||
while (exists $coords{$x}{$y}){ | ||
($x, $y) = (int(rand($tx*10)), int(rand($tx*10))); | ||
($x, $y) = ($x/10, $y/10); | ||
} | ||
$coords{$x}{$y} = 1; | ||
push(@gws, [$x, $y]); | ||
} | ||
|
||
|
||
printf "# terrain map [%i x %i]\n", $tx, $tx; | ||
print "# node coords:"; | ||
my $n = 1; | ||
foreach my $s (@sensors){ | ||
my ($x, $y) = @$s; | ||
printf " %s [%.1f %.1f]", $n, $x, $y; | ||
$n++; | ||
} | ||
print "\n"; | ||
print "# gateway coords:"; | ||
my $l = "A"; | ||
foreach my $g (@gws){ | ||
my ($x, $y) = @$g; | ||
printf " %s [%.1f %.1f]", $l, $x, $y; | ||
$l++; | ||
} | ||
print "\n"; | ||
|
||
print "# generated with: $0 ",join(" ",@ARGV),"\n"; | ||
printf "# stats: nodes=%i gateways=%i terrain=%.1fm^2 node_sz=%.2fm^2\n", scalar @sensors, scalar @gws, $tx*$tx, 0.1 * 0.1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import subprocess, random, datetime, os, re | ||
from flask import Flask, render_template, redirect, url_for, request, session | ||
from flask_bootstrap import Bootstrap5 | ||
from flask_session import Session | ||
from flask_wtf import FlaskForm, CSRFProtect | ||
from wtforms import IntegerField, SubmitField, SelectField, BooleanField | ||
from wtforms.validators import DataRequired, NumberRange | ||
|
||
app = Flask(__name__, static_folder=os.getcwd()) | ||
app.secret_key = 'tO$&!|0wkamvVia0?n$NqIRVWOG' | ||
bootstrap = Bootstrap5(app) | ||
csrf = CSRFProtect(app) | ||
app.config["SESSION_PERMANENT"] = False | ||
app.config["SESSION_TYPE"] = "filesystem" | ||
Session(app) | ||
|
||
def parse_res(str): | ||
data = str.decode('utf-8') | ||
|
||
result = {} | ||
|
||
pattern = re.compile(r'(.+?) = ([\d.]+(?: secs| J| times| bytes|))') | ||
matches = pattern.findall(data) | ||
for match in matches: | ||
key, value = match | ||
result[key.strip()] = value | ||
|
||
gw_pattern = re.compile(r'GW (\w+) sent out (\d+) acks and commands') | ||
gw_matches = gw_pattern.findall(data) | ||
gw_list = [] | ||
for gw, value in gw_matches: | ||
gw_list.append((f'Gateway {gw}', value)) | ||
result['GW'] = gw_list | ||
|
||
node_pattern = re.compile(r'# of nodes with SF(\d+): (\d+), Avg retransmissions: ([\d.]+)') | ||
node_matches = node_pattern.findall(data) | ||
sf_list = [] | ||
for sf, num_nodes, avg_retrans in node_matches: | ||
sf_list.append((f'SF{sf}', num_nodes, avg_retrans)) | ||
result['SF'] = sf_list | ||
|
||
return result | ||
|
||
|
||
class SimulationForm(FlaskForm): | ||
# simulator = SelectField('Protocol', choices=[('eu', 'EU868'), ('us', 'US915')], validators=[DataRequired()]) | ||
packets_per_hour = IntegerField('Packets per hour ', validators=[DataRequired(), NumberRange(min=1)], default=10) | ||
auto_simtime = BooleanField('Auto Simulation Time', id="auto_sim") | ||
simulation_time = IntegerField('Simulation Time (s)', default=3600, validators=[DataRequired(), NumberRange(min=3600)], id="sim") | ||
ack_policy = SelectField('Acknowledgement Policy', choices=[('1', 'First-Come First-Served (RCFS)'), ('2', 'Best Received Signal Strength Indicator (RSSI)'), ('3', 'Least busy Gateway')], validators=[DataRequired()]) | ||
max_retr = IntegerField('Max Retries', validators=[DataRequired(), NumberRange(min=1, max=8),], default=8) | ||
channels = SelectField('Number of Channels', choices=[('3', '3'), ('8', '8')], validators=[DataRequired()], default='8') | ||
rx2sf = IntegerField('RX2 SF', validators=[DataRequired(), NumberRange(min=7, max=12)], default=7) | ||
fixed_packet_size = BooleanField('Fixed Packet Size', id = "fixed") | ||
packet_size = IntegerField('Average Packet Size', validators=[NumberRange(min=1, max=50)], default=16, id="size") | ||
packet_size_distr = SelectField('Packet Size Distribution', choices=[('normal', 'Normal'), ('uniform', 'Uniform')], id = "distr", default='normal') | ||
confirmed_perc = IntegerField('Percentage of EDs that require an ACK', validators=[DataRequired(), NumberRange(min=0, max=100)], default=100) | ||
submit = SubmitField('Simulate') | ||
|
||
class TerrainForm(FlaskForm): | ||
size = IntegerField('Terrain Size', validators=[DataRequired(), NumberRange(min=100)], default="1000") | ||
nodes = IntegerField('Number of Nodes', default="100", validators=[DataRequired(), NumberRange(min=1)]) | ||
gateways = IntegerField('Number of Gateways', default="2", validators=[DataRequired(), NumberRange(min=1)]) | ||
submit = SubmitField('Generate') | ||
|
||
@app.route("/", methods=['GET', 'POST']) | ||
def index(): | ||
form = TerrainForm() | ||
message = "" | ||
if form.validate_on_submit(): | ||
size = form.size.data | ||
nodes = form.nodes.data | ||
gateways = form.gateways.data | ||
session["id"] = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S.%f") | ||
file = "terrains\\" + session["id"] + ".txt" | ||
img_file = "imgs\\" + session["id"] + ".png" | ||
try: | ||
subprocess.run(["perl", "generate_terrain.pl", str(size), str(nodes), str(gateways), ">", "terrains\\" + session["id"] + ".txt"], shell=True) | ||
subprocess.run(["perl", "draw_terrain.pl", file, img_file], shell=True) | ||
session["image"] = "website/imgs/" + session["id"] + ".png" | ||
except Exception as e: | ||
message = e | ||
return redirect("/simulate") | ||
else: | ||
session["image"] = None | ||
return render_template('index.html', form=form, message=message, tab="Generate") | ||
|
||
@app.route("/simulate", methods=['GET', 'POST']) | ||
def simulate(): | ||
form = SimulationForm() | ||
message = "" | ||
|
||
if form.validate_on_submit(): | ||
# form.submit | ||
packets_per_hour = form.packets_per_hour.data | ||
simulation_time = form.simulation_time.data | ||
ack_policy = form.ack_policy.data | ||
max_retr = form.max_retr.data | ||
channels = form.channels.data | ||
rx2sf = form.rx2sf.data | ||
if form.fixed_packet_size.data: | ||
form.packet_size.label.text = "Packet Size" | ||
form.packet_size_distr.render_kw = {'disabled': 'disabled'} | ||
else: | ||
form.packet_size.label.text = "Average Packet Size" | ||
fixed_packet_size = 0 if form.fixed_packet_size.data == False else 1 | ||
packet_size_distr = form.packet_size_distr.data | ||
auto_simtime = 0 if form.auto_simtime.data == False else 1 | ||
packet_size = form.packet_size.data | ||
confirmed_perc = form.confirmed_perc.data / 100 | ||
# device = "LoRaWAN.pl" if form.simulator.data == 'eu' else "LoRaWAN-US915.pl" | ||
if session["id"]: | ||
file = "terrains\\" + session["id"] + ".txt" | ||
if os.path.exists(file): | ||
try: | ||
res = subprocess.check_output(["perl", "LoRaWAN.pl", str(packets_per_hour), str(simulation_time), str(ack_policy), "terrains\\" + session["id"] + ".txt", str(max_retr), str(channels), str(rx2sf), str(fixed_packet_size), str(packet_size_distr), str(auto_simtime), str(packet_size), str(confirmed_perc)], shell=True) | ||
session["result"] = parse_res(res) | ||
except Exception as e: | ||
message = e | ||
else: | ||
session["result"] = None | ||
return render_template('index.html', form=form, message=message, result=session["result"], img_file=session["image"], tab="Simulate") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
<title>LoraWAN simulation</title> | ||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> | ||
<link href="https://getbootstrap.com/docs/5.2/assets/css/docs.css" rel="stylesheet"> | ||
</head> | ||
|
||
<body> | ||
<div class="container" style="width: 800px;"> | ||
<header class="d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom"> | ||
<div class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none"> | ||
<span class="fs-4"> | ||
<a href="/" class="link-dark link-underline-opacity-0">IoT Lab</a> | ||
</span> | ||
</div> | ||
</header> | ||
</div> | ||
|
||
{% block content %} | ||
{% endblock %} | ||
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> | ||
</body> | ||
</html> |
Oops, something went wrong.