Skip to content
Snippets Groups Projects
Commit 039ce8c5 authored by shinedday's avatar shinedday
Browse files

Update .gitignore, add client selection, better db managment, simplify...

Update .gitignore, add client selection, better db managment, simplify experiment managment, simplify experiment killing process
parent aeb3e1e8
Branches
No related tags found
No related merge requests found
Showing
with 168 additions and 236 deletions
......@@ -4,4 +4,9 @@ desktop.ini
.idea
venv
*.bat
__pycache__/
\ No newline at end of file
__pycache__/
ping/migrations/*
db.sqlite3
uploads/tmp/*
uploads/media/*
uploads/current_experiment/*
File deleted
# Generated by Django 4.0.4 on 2022-05-12 08:50
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Agent',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('ip', models.CharField(max_length=16)),
('command', models.CharField(max_length=256)),
],
),
migrations.CreateModel(
name='Client',
fields=[
('hostname', models.CharField(max_length=16, primary_key=True, serialize=False, unique=True)),
('username', models.CharField(max_length=16)),
('password', models.CharField(max_length=16)),
('status', models.CharField(default='Offline', max_length=8)),
],
),
]
# Generated by Django 4.0.4 on 2022-05-12 12:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ping', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Experiment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=60)),
('status', models.CharField(default='Not checked', max_length=60)),
('description', models.TextField()),
('media', models.FileField(blank=True, null=True, upload_to='')),
],
),
]
# Generated by Django 4.0.4 on 2022-05-13 12:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ping', '0002_experiment'),
]
operations = [
migrations.CreateModel(
name='CurrentExperiment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=60)),
('cycle', models.PositiveIntegerField(default=0)),
('nbr_agent', models.PositiveIntegerField(default=0)),
],
),
migrations.CreateModel(
name='Metrics',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('experiment_name', models.CharField(max_length=60)),
('cycle', models.PositiveIntegerField()),
('agent_id', models.PositiveIntegerField()),
('_metrics', models.BinaryField()),
],
),
migrations.AlterField(
model_name='experiment',
name='media',
field=models.FileField(blank=True, null=True, upload_to='media'),
),
]
......@@ -5,10 +5,11 @@ from django.forms import ModelForm
class Client(models.Model):
hostname = models.CharField(max_length=16, primary_key=True, unique=True)
hostname = models.CharField(max_length=16)
username = models.CharField(max_length=16)
password = models.CharField(max_length=16)
status = models.CharField(max_length=8, default="Offline")
active = models.BooleanField(default=True)
def __str__(self):
return "Hostname : " + self.hostname + " Username : " + self.username + " Status : " + self.status
......@@ -34,9 +35,7 @@ class ExperimentForm(ModelForm):
fields = ["name", "description", "media"]
class Metrics(models.Model):
experiment_name = models.CharField(max_length=60)
cycle = models.PositiveIntegerField()
agent_id = models.PositiveIntegerField()
_metrics = models.BinaryField()
def set_data(self, data):
......@@ -52,4 +51,8 @@ class CurrentExperiment(models.Model):
name = models.CharField(max_length=60)
cycle = models.PositiveIntegerField(default=0)
nbr_agent = models.PositiveIntegerField(default=0)
\ No newline at end of file
nbr_agent = models.PositiveIntegerField(default=0)
amas_pid = models.PositiveIntegerField(default=0)
scheduler_pid = models.PositiveIntegerField(default=0)
env_pid = models.PositiveIntegerField(default=0)
\ No newline at end of file
......@@ -28,18 +28,19 @@
<tr>
<th>Name</th>
<th>Status</th>
<th>Check</th>
</tr>
{% for experiment in experiments %}
<tr>
<td>{{ experiment.name }}</td>
<td>{{ experiment.status }}</td>
<td><a href="/experiment/{{ experiment.id }}/check">Check</td>
<td><a href="/experiment/{{ experiment.id }}/select">Select</td>
<td><a href="/experiment/{{ experiment.id }}/delete">Delete</td>
</tr>
{% endfor %}
</table>
{% else %}
<p>No agents are available.</p>
<p>No experiment are available.</p>
{% endif %}
</body>
......
......@@ -37,12 +37,15 @@
<th>Hostname</th>
<th>Username</th>
<th>Status</th>
<th>Active</th>
</tr>
{% for client in host_list %}
<tr>
<td>{{ client.hostname }}</td>
<td>{{ client.username }}</td>
<td>{{ client.status }}</td>
<td>{{ client.active }}</td>
<td><a href="/network/{{ client.id }}/activate">Enable/Disable</td>
</tr>
{% endfor %}
</table>
......
......@@ -10,51 +10,48 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
}
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
}
function canvasApp() {
var metrics = {{metrics}};
function canvasApp() {
var metrics = {{ metrics }};
var cycle = 0;
var cycle = 0;
theCanvas = document.getElementById('canvasOne');
context = theCanvas.getContext('2d');
theCanvas = document.getElementById('canvasOne');
context = theCanvas.getContext('2d');
function erraseCanvas() {
context.clearRect(0,0,theCanvas.width,theCanvas.height);
}
function erraseCanvas() {
context.clearRect(0, 0, theCanvas.width, theCanvas.height);
}
function drawScreen () {
function drawScreen() {
erraseCanvas();
var count = metrics[cycle].length;
for (let id_agent = 0; id_agent<count; id_agent++){
for (let id_agent = 0; id_agent < count; id_agent++) {
context.fillStyle = "#000000";
context.beginPath();
context.arc(metrics[cycle][id_agent][0],metrics[cycle][id_agent][1],15,0,Math.PI*2,true);
context.arc(metrics[cycle][id_agent][0], metrics[cycle][id_agent][1], 1, 0, Math.PI * 2, true);
context.closePath();
context.fill();
}
}
function gameLoop() {
if (cycle < metrics.length) {
window.setTimeout(gameLoop, 20);
drawScreen();
cycle = cycle + 1 ;
}
}
function gameLoop() {
if (cycle < metrics.length) {
window.setTimeout(gameLoop, 20);
drawScreen();
cycle = cycle + 1;
}
}
gameLoop();
}
gameLoop();
}
</script>
</script>
</head>
......@@ -98,9 +95,14 @@ function eventWindowLoaded() {
<button type='submit'> Load</button>
</form>
<canvas id="canvasOne" width="500" height="500" style="border:1px solid #000000;">
Your browser does not support the HTML 5 Canvas.
</canvas>
<form action='./global/kill' method='GET'>
<button type='submit'> Kill Amas, Env, Scheduler</button>
</form>
<canvas id="canvasOne" width="500" height="500" style="border:1px solid #000000;">
Your browser does not support the HTML 5 Canvas.
</canvas>
</body>
</html>
\ No newline at end of file
......@@ -9,9 +9,12 @@ urlpatterns = [
path('network/update/', views.update, name='update'),
path('network/agents/', views.agents, name='agents'),
path('network/agent/kill', views.kill, name='kill'),
path('network/<int:client_id>/activate/', views.client_activate, name='active'),
path('experiment/new/', views.entry, name='entry'),
path('experiment/', views.experiment, name='experiment'),
path('experiment/<int:experiment_id>/check/', views.check, name='check'),
path('experiment/<int:experiment_id>/select/', views.select, name='select'),
path('experiment/<int:experiment_id>/delete/', views.delete, name='delete'),
path('play/', views.main_play, name='play'),
path('play/scheduler/start', views.scheduler_start, name='start'),
path('play/scheduler/step', views.scheduler_step, name='step'),
......@@ -20,4 +23,5 @@ urlpatterns = [
path('play/global/share', views.experiment_share, name='share'),
path('play/global/start', views.experiment_start, name='start_exp'),
path('play/global/load', views.experiment_load, name='load_exp'),
path('play/global/kill', views.experiment_kill, name='kill_exp'),
]
\ No newline at end of file
import os
import shutil
import zipfile
from django.conf import settings
......@@ -8,6 +7,7 @@ from django.http import HttpResponse, HttpResponseRedirect
from django.template import loader
from django.urls import reverse
from .tool import delete_folder
from ..models import Experiment, ExperimentForm
......@@ -26,19 +26,6 @@ def entry(request):
return HttpResponse(template.render(context, request))
def empty_tmp():
folder = str(settings.MEDIA_ROOT) + "/tmp"
for filename in os.listdir(folder):
file_path = os.path.join(folder, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception as e:
print('Failed to delete %s. Reason: %s' % (file_path, e))
def check(request, experiment_id):
# get the path
exp = Experiment.objects.get(pk=experiment_id)
......@@ -57,7 +44,7 @@ def check(request, experiment_id):
if not os.path.isdir(folder_path):
exp.status = "Wrong format : zip should contain a folder"
exp.save()
empty_tmp()
delete_folder(str(settings.MEDIA_ROOT) + "/tmp")
return HttpResponseRedirect(reverse('ping:experiment'))
required_files = ["amas.py", "agent.py", "env.py", "scheduler.py"]
......@@ -65,14 +52,33 @@ def check(request, experiment_id):
if not os.path.exists(folder_path + "/" + required_file):
exp.status = "Wrong format : " + required_file + " file is expected"
exp.save()
empty_tmp()
delete_folder(str(settings.MEDIA_ROOT) + "/tmp")
return HttpResponseRedirect(reverse('ping:experiment'))
exp.status = "Checked"
exp.save()
empty_tmp()
delete_folder(str(settings.MEDIA_ROOT) + "/tmp")
return HttpResponseRedirect(reverse('ping:experiment'))
def select(request, experiment_id):
exp = Experiment.objects.get(pk=experiment_id)
if exp.status == "Checked":
exp_list = Experiment.objects.filter(status="Selected")
for expe in exp_list:
expe.status = "Checked"
expe.save()
exp.status = "Selected"
exp.save()
return HttpResponseRedirect(reverse('ping:experiment'))
def delete(request, experiment_id):
exp = Experiment.objects.get(pk=experiment_id)
os.remove(str(settings.MEDIA_ROOT) + str(exp.media))
Experiment.objects.get(pk=experiment_id).delete()
return HttpResponseRedirect(reverse('ping:experiment'))
def experiment(request):
template = loader.get_template('ping/experiment.html')
......
......@@ -5,27 +5,15 @@ import re
from django.http import HttpResponse, HttpResponseRedirect
from django.template import loader
from django.urls import reverse
from iotAmak.tool.remote_client import RemoteClient
from iotAmak.tool.ssh_client import SSHClient, Cmd
from iotAmak.tool.ssh_client import Cmd
from .tool import get_ssh_client
from ..models import Client, Agent
def get_remote_client():
res = []
for client in Client.objects.all():
if client.status == "Online":
res.append(RemoteClient(client.hostname, client.username, client.password))
return res
def get_ssh_client():
return SSHClient(get_remote_client())
def update(request):
ssh = get_ssh_client()
version = "0.0.1"
version = "0.0.2"
commands = [
Cmd(
cmd="cd Desktop/mqtt_goyon/iotamak-core"
......@@ -69,6 +57,7 @@ def agents(request):
return HttpResponseRedirect(reverse('ping:index'))
def kill(request):
ssh = get_ssh_client()
......@@ -82,7 +71,6 @@ def kill(request):
return HttpResponseRedirect(reverse('ping:index'))
def index(request):
template = loader.get_template('ping/index.html')
context = {
......@@ -106,4 +94,11 @@ def pressed(request):
c.status = "Offline"
c.save()
return HttpResponseRedirect(reverse('ping:index'))
def client_activate(request, client_id):
client = Client.objects.get(pk=client_id)
client.active = not client.active
client.save()
return HttpResponseRedirect(reverse('ping:index'))
\ No newline at end of file
import os
import shutil
import sys
import zipfile
from ast import literal_eval
......@@ -7,16 +6,15 @@ from subprocess import Popen
from time import sleep
from django.conf import settings
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.http import HttpResponse, HttpResponseRedirect
from django.template import loader
from django.urls import reverse
from iotAmak.tool.ssh_client import Cmd
from paho.mqtt.client import Client as MQTTClient
from .tool import delete_folder, get_ssh_client
from ..models import Experiment, CurrentExperiment, Metrics
from .network import get_ssh_client
client = MQTTClient(client_id="django-ihm")
def subscribe(mqttclient, topic, fun):
......@@ -32,12 +30,10 @@ def agent_log(client, userdata, message) -> None:
def agent_metric(client, userdata, message) -> None:
result = literal_eval(message.payload.decode("utf-8"))
agent_id = result.get("id")
results = literal_eval(message.payload.decode("utf-8"))
exp = CurrentExperiment.objects.all()[0]
metric = Metrics(experiment_name=exp.name, cycle=exp.cycle, agent_id=agent_id, metrics=result)
metric = Metrics(cycle=exp.cycle, metrics=results)
metric.save()
def cycle_done(client, userdata, message) -> None:
......@@ -48,26 +44,12 @@ def cycle_done(client, userdata, message) -> None:
def update_nbr_agent(client, userdata, message) -> None:
exp = CurrentExperiment.objects.all()[0]
subscribe(client, "agent/"+str(exp.nbr_agent)+"/metric", agent_metric)
subscribe(client, "scheduler/cycledone", cycle_done)
exp.nbr_agent += 1
exp.save()
def empty_current_experiment():
folder = str(settings.MEDIA_ROOT) + "current_experiment"
for filename in os.listdir(folder):
file_path = os.path.join(folder, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception as e:
print('Failed to delete %s. Reason: %s' % (file_path, e))
def experiment_load(request):
empty_current_experiment()
delete_folder(str(settings.MEDIA_ROOT) + "current_experiment")
exp = Experiment.objects.get(status="Selected")
......@@ -86,7 +68,7 @@ def experiment_load(request):
def experiment_start(request):
broker_ip = "192.168.201.209"
broker_ip = "192.168.157.209"
ssh = get_ssh_client()
experiment_path = str(settings.MEDIA_ROOT) + "current_experiment/" + Experiment.objects.get(status="Selected").name
......@@ -96,6 +78,8 @@ def experiment_start(request):
client.connect(host=broker_ip)
client.loop_start()
subscribe(client, "amas/agent/new", update_nbr_agent)
subscribe(client, "scheduler/cycledone", cycle_done)
subscribe(client, "amas/all_metric", agent_metric)
# start subprocess scheduler
p1 = Popen([sys.executable, experiment_path+'/scheduler.py', broker_ip])
......@@ -105,16 +89,26 @@ def experiment_start(request):
p2 = Popen([sys.executable, experiment_path+'/amas.py', broker_ip, str(send_client)])
# start subprocess env
p3 = Popen([sys.executable, experiment_path+'/env.py', broker_ip])
exp = CurrentExperiment.objects.all()[0]
exp.amas_pid = p2.pid
exp.scheduler_pid = p1.pid
exp.env_pid = p3.pid
exp.save()
return HttpResponseRedirect(reverse('ping:play'))
def main_play(request):
cycle = CurrentExperiment.objects.all()[0].cycle
if len(CurrentExperiment.objects.all()) > 0:
cycle = CurrentExperiment.objects.all()[0].cycle
else:
cycle = 0
metrics = []
for i in range(cycle):
# get all the agent 0 metrics
raw_context = Metrics.objects.filter(cycle=i)
raw_context = Metrics.objects.filter(cycle=i)[0].metrics
cycle_metrics = [[metric.metrics.get("x"), metric.metrics.get("y")] for metric in raw_context]
cycle_metrics = [[metric.get("x"), metric.get("y")] for metric in raw_context if metric != {}]
metrics.append(cycle_metrics)
......@@ -162,3 +156,14 @@ def experiment_stop(request):
client.publish("ihm/exit")
client.publish("ihm/step")
return HttpResponseRedirect(reverse('ping:play'))
def experiment_kill(request):
exp = CurrentExperiment.objects.all()[0]
if exp.amas_pid != 0 :
os.system("kill "+str(exp.amas_pid))
if exp.env_pid != 0 :
os.system("kill "+str(exp.env_pid))
if exp.scheduler_pid != 0 :
os.system("kill "+str(exp.scheduler_pid))
return HttpResponseRedirect(reverse('ping:play'))
import os
import shutil
from iotAmak.tool.remote_client import RemoteClient
from iotAmak.tool.ssh_client import SSHClient
from ping.models import Client
def delete_folder(folder_name):
for filename in os.listdir(folder_name):
file_path = os.path.join(folder_name, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception as e:
print('Failed to delete %s. Reason: %s' % (file_path, e))
def get_remote_client():
res = []
for client in Client.objects.all():
if client.status == "Online" and client.active:
res.append(RemoteClient(client.hostname, client.username, client.password))
return res
def get_ssh_client():
return SSHClient(get_remote_client())
\ No newline at end of file
......@@ -11,10 +11,24 @@ class Ant(Agent):
self.y = 250
super().__init__(identifier, broker_ip)
def on_initialization(self) -> None:
pass
def on_cycle_begin(self) -> None:
pass
def on_perceive(self) -> None:
pass
def on_decide(self) -> None:
pass
def on_act(self) -> None:
self.x += random.randint(-5, +5)
self.y += random.randint(-5, +5)
self.log("X : "+str(self.x)+" Y : "+str(self.y))
def on_cycle_end(self) -> None:
pass
def send_metric(self):
metric = super(Ant, self).send_metric()
......
......@@ -16,5 +16,5 @@ class AntAmas(Amas):
if __name__ == '__main__':
s = AntAmas(str(sys.argv[1]), sys.argv[2], 5)
s = AntAmas(str(sys.argv[1]), sys.argv[2], 150)
s.run()
import sys
from iotAmak.amas import Amas
class PhiAmas(Amas):
def __init__(self, broker_ip: str, clients, nbr_agent):
self.agent_to_create = nbr_agent
super().__init__(broker_ip, clients)
def on_initial_agents_creation(self):
for _ in range(self.agent_to_create):
self.add_agent("philosophers", [str(self.agent_to_create)])
if __name__ == '__main__':
s = PhiAmas(str(sys.argv[1]), sys.argv[2], 5)
s.run()
No preview for this file type
File deleted
{
"broker" : "192.168.30.209",
"clients_ssh" : [
{
"hostname" : "192.168.30.18",
"user" : "pi",
"password" : "raspberry"
},
{
"hostname" : "192.168.30.227",
"user" : "pi",
"password" : "raspberry"
},
{
"hostname" : "192.168.30.61",
"user" : "pi",
"password" : "raspberry"
},
{
"hostname" : "192.168.30.75",
"user" : "pi",
"password" : "raspberry"
}
]
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment