From 95d57219ee18cfeb88dffb4fdcd323f0b841b0c1 Mon Sep 17 00:00:00 2001
From: shinedday <shinedday@gmail.com>
Date: Fri, 13 May 2022 08:45:40 +0200
Subject: [PATCH] Add Newtwork and experiment menu

---
 db.sqlite3                          | Bin 139264 -> 147456 bytes
 iotamak_ihm/settings.py             |   3 +-
 iotamak_ihm/urls.py                 |   2 +-
 ping/admin.py                       |   6 +-
 ping/migrations/0001_initial.py     |  11 +-
 ping/migrations/0002_experiment.py  |  23 ++++
 ping/models.py                      |  24 +++-
 ping/static/ping/index.css          |  41 +++++++
 ping/templates/ping/entry.html      |  35 ++++++
 ping/templates/ping/experiment.html |  46 ++++++++
 ping/templates/ping/index.html      |  81 +++++++++++---
 ping/urls.py                        |   9 +-
 ping/views.py                       | 164 +++++++++++++++++++++++++++-
 uploads/media/config.json           |  25 +++++
 uploads/media/env.py                |  67 ++++++++++++
 uploads/media/philosophers.zip      | Bin 0 -> 3191 bytes
 16 files changed, 508 insertions(+), 29 deletions(-)
 create mode 100644 ping/migrations/0002_experiment.py
 create mode 100644 ping/static/ping/index.css
 create mode 100644 ping/templates/ping/entry.html
 create mode 100644 ping/templates/ping/experiment.html
 create mode 100644 uploads/media/config.json
 create mode 100644 uploads/media/env.py
 create mode 100644 uploads/media/philosophers.zip

diff --git a/db.sqlite3 b/db.sqlite3
index 432f204be352251f792e0f91a1b7377303918b43..e33654f60a42bd1bdaf59e30dc8587fc89bd53be 100644
GIT binary patch
delta 7218
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

delta 1829
zcmZo@;A}X+F+o~Tje&tdi2(teC+Zk8s%=b|&(9&u%fP@O%q>5eT|j+f<43N^^3n=|
z1`39zR>qcA29|ne7M6yV#*+i2RrnyH=2oWWdd8;albfV%pb8;EMiyokhLg`r8={CB
zSXdYuO%{|fK^8T#G&M9dn;a%%fg);dWM*PGdA^J}il~XHrIG36=Q1d^85vm^n;K0v
zku^e5YiMe2W-+;3))+<9z{t?ZX!34Z6qgyAn;ILMPyQ>5;xa>1b3;?3$xd>X$R-$?
z7+Dw_P3}PxH8L|ZGnsr%&I(1Xfsui+*<?j|BUz*XGq5l-GBPz_W@Qjpbxcgj&CHv;
zP+k;SpMj}`k(ue_Y4Wh(2ZySGv4w@9<>ce?8tTZV85)}yne+0mU|{1fW8i<ue}#WH
ze;I!_|1AEBjg1NXqQY#<+Kh>%B^mJrsYSV&#l@NVc`}o`^%X>g5vc&o5@%&LW}N=f
ziBZ^@U6`3!mop_RF)uwo9_+^WocwfAMrM6Rs9<hpdQoCYW`16=Fc$*@13P~;1OGez
z)BLyi_wp~`ujcRG*qF-C!_Oir$ykt?m!6!HIl0}wR*0Ryih=(v|4sf={CoK4^LO!A
zZEQ^8=jCH(VGxysEA+2rmf^iJnLWUWosahy?=#*j8ylzax(l)}Dl@W|Wu}(LC+B3Q
z=9S0@voNYNvZtixq?V*Y#iSu($r*`x=`b-V0TxC@M)t&%6p*6L(sAK}F&0jNUS6g_
zslMgGY30f0j^!2k#zh$_sbzj1##QE_VQDVmfkhr+7Lg`d<{`ytE+K(V9$t<H77=Ob
zF7DZ>rum_^Mg~Skx(24YhT!-xF#u%}19LMIi^=Em6`>JoX=Q4pXKrR_W^S^n;IUqP
zadxF)R#IhlnNdzrN?vwBj&V_DMM-&*g^59lX+fT;UaD6`SeA>ce|eUpZ&0dkuzP5@
zf2eMuYj%`DRggziX0W5XVQ7h;f2L1~OF^Jnd3;$~YMN=DtC^ush*4gUe_)kso{2?O
zMoLA1ze|OIaj3tEg>hzLQh~91l#_YAds1XxTB5moXrXRaRhfB-rITq`V!U63i<6Oa
zc8IG<XtAqLk*>36R=%e}Qc$6jr-f&HUVczYwpVe6bD*PtP*H@LV_uMMlBHvckE5TF
ztCeA1eo&>URe*nBS&^5AVR@-nkawDQRgiy0p_i9wWO1=Ygr9|JMrveUVqUO(x|wOB
z2`u1@OhBazB;X7!jLghTHZ6F-uRth_Hf>;0V3y&Joy>m0a$}<`|K`~H%M+BiSf(=Y
ztMFaoyvlZnx16VoO@uX^Wh(b~7-agpUCn`UA>+mg=a{$}?S<IIvvnC8i>EXCFv@M;
z>%wTo%*6O$W8)ph?N>b+Ll_w^PHa58{ku0Kmxuua1A{0-00Vy(Ujgq{o)=utxokLB
za@^$*Vtd6V!kWwUow=L&0Mil1b4=}w<_rOh6C35Z8g<Rt#g&yAn-r(p<TFY$Do&5c
zXY6FwG_{!SpTKCssI*-_kuij6dv`Kp4Ey%uj*RD-rrUZm?%wX^!Pw8p$hDdA0kZ)M
zBmZxPjRk%D6DOqdvoJGqvV$@kBmW-;{y&=q8{YE^GB7YOGjno+if%^!X$<_+HVYQ?
z@Ec?>axyq-Mr$*2a>4=|no3QrOfB@xEKLnT$Vku7%tFu5*m8QqF-9?MLt{%lLkoq_
z;?$zN#N1Q`D}{p0=xU|Jl$6vIB`byM+S*ulkWDh&m!`8{U^HTP;F03~!hLCD<0Nh^
z1sPTbMrCc}{OO#SrvS>X3XtroBF)OcsIJdGz3eulJi8Q>Gv_v=yo@9(1EZoQEQhA%
zm4LJ+rlf#$ZjZggI7wL=Bdvg3!pQ%Sf&bxV!Gi1jnzD>6nu79%mT*t$8Cn!%79|!J
dB&8M=RYF4U9RvTn&4L9__=N<SH91ig0|4gj?{5GA

diff --git a/iotamak_ihm/settings.py b/iotamak_ihm/settings.py
index fd177df..3f506ac 100644
--- a/iotamak_ihm/settings.py
+++ b/iotamak_ihm/settings.py
@@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/4.0/topics/settings/
 For the full list of settings and their values, see
 https://docs.djangoproject.com/en/4.0/ref/settings/
 """
-
+import os
 from pathlib import Path
 
 # Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -27,6 +27,7 @@ DEBUG = True
 
 ALLOWED_HOSTS = []
 
+MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads/')
 
 # Application definition
 
diff --git a/iotamak_ihm/urls.py b/iotamak_ihm/urls.py
index d502811..a05677f 100644
--- a/iotamak_ihm/urls.py
+++ b/iotamak_ihm/urls.py
@@ -17,6 +17,6 @@ from django.contrib import admin
 from django.urls import path, include
 
 urlpatterns = [
-    path('ping/', include('ping.urls')),
+    path('', include('ping.urls')),
     path('admin/', admin.site.urls),
 ]
diff --git a/ping/admin.py b/ping/admin.py
index 7e490c7..f8dd658 100644
--- a/ping/admin.py
+++ b/ping/admin.py
@@ -1,5 +1,7 @@
 from django.contrib import admin
 
-from .models import Client
+from .models import Client, Agent, Experiment
 
-admin.site.register(Client)
\ No newline at end of file
+admin.site.register(Client)
+admin.site.register(Agent)
+admin.site.register(Experiment)
\ No newline at end of file
diff --git a/ping/migrations/0001_initial.py b/ping/migrations/0001_initial.py
index 7eda40b..ae0c37f 100644
--- a/ping/migrations/0001_initial.py
+++ b/ping/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.0.4 on 2022-05-10 15:39
+# Generated by Django 4.0.4 on 2022-05-12 08:50
 
 from django.db import migrations, models
 
@@ -11,12 +11,21 @@ class Migration(migrations.Migration):
     ]
 
     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)),
             ],
         ),
     ]
diff --git a/ping/migrations/0002_experiment.py b/ping/migrations/0002_experiment.py
new file mode 100644
index 0000000..c06c6d6
--- /dev/null
+++ b/ping/migrations/0002_experiment.py
@@ -0,0 +1,23 @@
+# 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='')),
+            ],
+        ),
+    ]
diff --git a/ping/models.py b/ping/models.py
index 7a1725a..c6a47a1 100644
--- a/ping/models.py
+++ b/ping/models.py
@@ -1,10 +1,32 @@
 from django.db import models
+from django.forms import ModelForm
 
 
 class Client(models.Model):
     hostname = models.CharField(max_length=16, primary_key=True, unique=True)
     username = models.CharField(max_length=16)
     password = models.CharField(max_length=16)
+    status = models.CharField(max_length=8, default="Offline")
 
     def __str__(self):
-        return "Hostname : " + self.hostname + " Username : " + self.username
\ No newline at end of file
+        return "Hostname : " + self.hostname + " Username : " + self.username + " Status : " + self.status
+
+
+class Agent(models.Model):
+    ip = models.CharField(max_length=16)
+    command = models.CharField(max_length=256)
+
+    def __str__(self):
+        return self.command
+
+class Experiment(models.Model):
+    name = models.CharField(max_length=60)
+    status = models.CharField(max_length=60, default="Not checked")
+    description = models.TextField()
+    media = models.FileField(upload_to="media", null=True, blank=True)
+
+
+class ExperimentForm(ModelForm):
+    class Meta:
+        model = Experiment
+        fields = ["name", "description", "media"]
diff --git a/ping/static/ping/index.css b/ping/static/ping/index.css
new file mode 100644
index 0000000..3e4d206
--- /dev/null
+++ b/ping/static/ping/index.css
@@ -0,0 +1,41 @@
+body {
+  margin: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+}
+
+.topnav {
+  overflow: hidden;
+  background-color: #333;
+}
+
+.topnav a {
+  float: left;
+  color: #f2f2f2;
+  text-align: center;
+  padding: 14px 16px;
+  text-decoration: none;
+  font-size: 17px;
+}
+
+.topnav a:hover {
+  background-color: #ddd;
+  color: black;
+}
+
+.topnav a.active {
+  background-color: #0486aa;
+  color: white;
+}
+
+th {
+  text-align: center;
+}
+tr:nth-child(even) {background-color: #f2f2f2;}
+td {
+  height: 25px;
+  vertical-align: bottom;
+}
+
+th, td {
+  border-bottom: thin solid #ddd;
+}
\ No newline at end of file
diff --git a/ping/templates/ping/entry.html b/ping/templates/ping/entry.html
new file mode 100644
index 0000000..b718dd6
--- /dev/null
+++ b/ping/templates/ping/entry.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  {% load static %}
+  <link rel="stylesheet" type="text/css" href="{% static 'ping/index.css' %}">
+
+  <title>Experiment - IOTAMAK</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+
+</head>
+
+<body>
+
+  <div class="topnav">
+    <a href="/network">Network</a>
+    <a class="active" href="/experiment">Experiment</a>
+  </div>
+    <form action="/experiment/new/" method="POST" enctype="multipart/form-data">
+        {% csrf_token %}
+        {% for entry in form %}
+           <div>
+                {{ entry.label_tag }}
+           </div>
+           <div>
+               {{entry}}
+           </div>
+        {% endfor %}
+        <button>
+            Save!
+        </button>
+    </form>
+</body>
+</html>
\ No newline at end of file
diff --git a/ping/templates/ping/experiment.html b/ping/templates/ping/experiment.html
new file mode 100644
index 0000000..86454db
--- /dev/null
+++ b/ping/templates/ping/experiment.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  {% load static %}
+  <link rel="stylesheet" type="text/css" href="{% static 'ping/index.css' %}">
+
+  <title>Experiment - IOTAMAK</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+
+</head>
+
+<body>
+
+  <div class="topnav">
+    <a href="/network">Network</a>
+    <a class="active" href="/experiment">Experiment</a>
+  </div>
+
+  <form action='new' method='GET'>
+    <button type='submit'> Add experiment</button>
+  </form>
+
+  {% if experiments %}
+  <table>
+    <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>
+    </tr>
+    {% endfor %}
+  </table>
+  {% else %}
+  <p>No agents are available.</p>
+  {% endif %}
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/ping/templates/ping/index.html b/ping/templates/ping/index.html
index 02097b4..e39a748 100644
--- a/ping/templates/ping/index.html
+++ b/ping/templates/ping/index.html
@@ -1,19 +1,68 @@
 <!DOCTYPE html>
 <html>
-  <head>
-    <meta charset="utf-8">
-    <title>Main menu</title>
-  </head>
-  <body>
-    <form action='pressed' method='GET'>
-        <button type='submit'> Ping clients</button>
-    </form>
-
-    <ul>
-        {% for client in host_list %}
-            <li>{{client}}</li>
-        {% endfor %}
-    </ul>
-
-  </body>
+
+<head>
+  <meta charset="utf-8">
+  {% load static %}
+  <link rel="stylesheet" type="text/css" href="{% static 'ping/index.css' %}">
+
+  <title>Network - IOTAMAK</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+
+</head>
+
+<body>
+  <div class="topnav">
+    <a class="active" href="/network">Network</a>
+    <a href="/experiment">Experiment</a>
+  </div>
+
+
+
+  <form action='pressed' method='GET'>
+    <button type='submit'> Ping clients</button>
+  </form>
+
+  <form action='update' method='GET'>
+    <button type='submit'> Update clients</button>
+  </form>
+
+  <form action='agents' method='GET'>
+    <button type='submit'> agents</button>
+  </form>
+
+  <table>
+    <tr>
+      <th>Hostname</th>
+      <th>Username</th>
+      <th>Status</th>
+    </tr>
+    {% for client in host_list %}
+    <tr>
+      <td>{{ client.hostname }}</td>
+      <td>{{ client.username }}</td>
+      <td>{{ client.status }}</td>
+    </tr>
+    {% endfor %}
+  </table>
+
+  {% if agents %}
+  <table>
+    <tr>
+      <th>Hostname</th>
+      <th>Agent</th>
+    </tr>
+    {% for agent in agents %}
+    <tr>
+      <td>{{ agent.ip }}</td>
+      <td>{{ agent.command }}</td>
+    </tr>
+    {% endfor %}
+  </table>
+  {% else %}
+  <p>No agents are available.</p>
+  {% endif %}
+
+</body>
+
 </html>
\ No newline at end of file
diff --git a/ping/urls.py b/ping/urls.py
index de7ace3..f7f3002 100644
--- a/ping/urls.py
+++ b/ping/urls.py
@@ -4,6 +4,11 @@ from . import views
 
 app_name = 'ping'
 urlpatterns = [
-    path('', views.index, name='index'),
-    path('pressed/', views.pressed, name='pressed'),
+    path('network/', views.index, name='index'),
+    path('network/pressed/', views.pressed, name='pressed'),
+    path('network/update/', views.update, name='update'),
+    path('network/agents/', views.agents, name='agents'),
+    path('experiment/new/', views.entry, name='entry'),
+    path('experiment/', views.experiment, name='experiment'),
+    path('experiment/<int:experiment_id>/check/', views.check, name='check'),
 ]
\ No newline at end of file
diff --git a/ping/views.py b/ping/views.py
index 948a0fa..9cd9305 100644
--- a/ping/views.py
+++ b/ping/views.py
@@ -1,21 +1,97 @@
+import os
 import platform
+import shutil
 import subprocess
+import re
+import zipfile
+
+from django.conf import settings
 
 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 .models import Client, Agent, Experiment, ExperimentForm
+
+
+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())
+
 
-from .models import Client
+def update(request):
+    ssh = get_ssh_client()
+    version = "0.0.1"
+    commands = [
+        Cmd(
+            cmd="cd Desktop/mqtt_goyon/iotamak-core"
+        ),
+        Cmd(
+            cmd="git pull"
+        ),
+        Cmd(
+            cmd="git checkout main"
+        ),
+        Cmd(
+            cmd="git pull"
+        ),
+        Cmd(
+            cmd="python3 -m pip install --force-reinstall dist/iotAmak-" + version + "-py3-none-any.whl"
+        ),
+        Cmd(
+            cmd="cd ../../../"
+        )  # ,
+        # Cmd(
+        #    cmd="rm -r Desktop/mqtt_goyon/example/" + self.experiment_name
+        # )
+
+    ]
+    for i_client in range(len(ssh.clients)):
+        print("Hostname :", ssh.clients[i_client].hostname, " User :", ssh.clients[i_client].user)
+        ssh.run_cmd(i_client, commands)
+
+    return HttpResponseRedirect(reverse('ping:index'))
+
+
+def agents(request):
+    ssh = get_ssh_client()
+    Agent.objects.all().delete()
+    commands = [
+        Cmd(
+            cmd="ps -ef | tr -s ' ' | cut -d ' ' -f 8-",
+            do_print=False
+        )]
+    ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
+
+    for i_client in range(len(ssh.clients)):
+        raw_string = ssh.run_cmd(i_client, commands)[0].split("\r\n")
+        raw_string = [i for i in raw_string if "python D" in i]
+        for line in raw_string:
+            new_entry = Agent(ip=ssh.clients[i_client].hostname, command=ansi_escape.sub('', line))
+            new_entry.save()
+
+    return HttpResponseRedirect(reverse('ping:index'))
 
 
 def index(request):
     template = loader.get_template('ping/index.html')
-    host_list = Client.objects.all()
     context = {
-        "host_list": host_list,
+        "host_list": Client.objects.all(),
+        "agents": Agent.objects.all()
     }
     return HttpResponse(template.render(context, request))
 
+
 def pressed(request):
     for client in Client.objects.all():
         param = '-n' if platform.system().lower() == 'windows' else '-c'
@@ -23,6 +99,84 @@ def pressed(request):
 
         response = subprocess.call(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
 
-        print(response)
+        c = Client.objects.get(hostname=client.hostname)
+        if response:
+            c.status = "Online"
+        else:
+            c.status = "Offline"
+        c.save()
+
+    return HttpResponseRedirect(reverse('ping:index'))
 
-    return HttpResponseRedirect(reverse('ping:index'))
\ No newline at end of file
+
+def entry(request):
+    if request.method == 'POST':
+        form = ExperimentForm(request.POST, request.FILES)
+
+        if form.is_valid():
+            form.save()
+            return HttpResponseRedirect(reverse('ping:experiment'))
+
+    else:
+        form = ExperimentForm()
+        template = loader.get_template('ping/entry.html')
+        context = {"form": form}
+        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
+    print(experiment_id)
+    exp = Experiment.objects.get(pk=experiment_id)
+    print(str(exp.media))
+    print(str(settings.MEDIA_ROOT) + str(exp.media))
+    # check if it's a zip -> wrong format : not zip
+    print()
+
+    if not zipfile.is_zipfile(str(settings.MEDIA_ROOT) + str(exp.media)):
+        exp.status = "Wrong format : zip file expected"
+        exp.save()
+        return HttpResponseRedirect(reverse('ping:experiment'))
+
+    with zipfile.ZipFile(str(settings.MEDIA_ROOT) + str(exp.media), 'r') as zip_ref:
+        zip_ref.extractall(str(settings.MEDIA_ROOT) + "/tmp")
+
+    folder_path = str(settings.MEDIA_ROOT) + "/tmp/" + exp.name
+
+    if not os.path.isdir(folder_path):
+        exp.status = "Wrong format : zip should contain a folder"
+        exp.save()
+        empty_tmp()
+        return HttpResponseRedirect(reverse('ping:experiment'))
+
+    required_files = ["amas.py", "agent.py", "env.py", "scheduler.py"]
+    for required_file in required_files:
+        if not os.path.exists(folder_path + "/" + required_file):
+            exp.status = "Wrong format : " + required_file + " file is expected"
+            exp.save()
+            empty_tmp()
+            return HttpResponseRedirect(reverse('ping:experiment'))
+
+    exp.status = "Checked"
+    exp.save()
+    empty_tmp()
+    return HttpResponseRedirect(reverse('ping:experiment'))
+
+
+def experiment(request):
+    template = loader.get_template('ping/experiment.html')
+    context = {
+        "experiments": Experiment.objects.all(),
+    }
+    return HttpResponse(template.render(context, request))
diff --git a/uploads/media/config.json b/uploads/media/config.json
new file mode 100644
index 0000000..56c5b0c
--- /dev/null
+++ b/uploads/media/config.json
@@ -0,0 +1,25 @@
+{
+  "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
diff --git a/uploads/media/env.py b/uploads/media/env.py
new file mode 100644
index 0000000..c8a767f
--- /dev/null
+++ b/uploads/media/env.py
@@ -0,0 +1,67 @@
+
+import sys
+
+
+from iotAmak.environment import Environment
+from fork import Fork
+
+
+class PhiEnv(Environment):
+
+    def __init__(self, broker_ip, nbr_phil):
+        self.nbr_phil = nbr_phil
+        super().__init__(broker_ip)
+
+    def on_initialization(self):
+        self.forks = []
+        for i in range(self.nbr_phil):
+            self.forks.append(Fork(i))
+            self.subscribe("agent/" + str(i) + "/ask_spoon", self.ask_spoon)
+            self.subscribe("agent/" + str(i) + "/done_eating", self.done_eating)
+
+    def ask_spoon(self, client, userdata, message):
+        res = str(message.payload.decode("utf-8"))
+        agent_id = int(str(message.topic).split("/")[1])
+
+        if res == "left":
+            fork_id = agent_id
+        else:
+            fork_id = (agent_id - 1) % self.nbr_phil
+
+        if self.forks[fork_id].state == 1:
+            message = {
+                "response": "False"
+            }
+        elif self.forks[fork_id].taken_by == -1:
+            message = {
+                "response": "True",
+                "side": res,
+                "state": 0
+            }
+            self.forks[fork_id].taken_by = agent_id
+        else:
+            message = {
+                "response": "True",
+                "side": res,
+                "state": 1
+            }
+            self.forks[fork_id].taken_by = agent_id
+            self.forks[fork_id].state = 1
+
+        self.client.publish("env/agent/" + str(agent_id) + "/ask_spoon", str(message))
+
+    def done_eating(self, client, userdata, message):
+        agent_id = int(str(message.topic).split("/")[1])
+
+        for fork_id in [agent_id, (agent_id - 1) % self.nbr_phil]:
+            self.forks[fork_id].state = 0
+
+    def on_cycle_begin(self) -> None:
+        for fork in self.forks:
+            self.client.publish("env/fork/" + str(fork.identifier), str(fork.to_msg()))
+            print("Fork : ", fork.identifier," taken by ", fork.taken_by, " and is :", fork.state)
+
+
+if __name__ == '__main__':
+    s = PhiEnv(str(sys.argv[1]), 5)
+    s.run()
\ No newline at end of file
diff --git a/uploads/media/philosophers.zip b/uploads/media/philosophers.zip
new file mode 100644
index 0000000000000000000000000000000000000000..7c2deb36c8adeb6b9c9f9e60519c28d58b2724f3
GIT binary patch
literal 3191
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
Z069Shc(byBlyWd|Fvu`5Ft~DocmQ*rAXoqZ

literal 0
HcmV?d00001

-- 
GitLab