Compare commits
11
Commits
c7d57604e2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfb3273fb0 | ||
|
|
85e39b6948 | ||
|
|
a15aee45f4 | ||
|
|
cef0723bb3 | ||
|
|
920b800f38 | ||
|
|
de2ab090d7 | ||
|
|
cdb724aa89 | ||
|
|
0048c85d86 | ||
|
|
d33ef4bd97 | ||
|
|
742fc81c1f | ||
|
|
4a764beb9b |
@@ -1,169 +0,0 @@
|
|||||||
const Orientation = Object.freeze({
|
|
||||||
HAUT: Symbol("haut"),
|
|
||||||
BAS: Symbol("bas"),
|
|
||||||
GAUCHE: Symbol("gauche"),
|
|
||||||
DROITE: Symbol("droite")
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
class Noeud {
|
|
||||||
coordX;
|
|
||||||
coordY;
|
|
||||||
chemins;
|
|
||||||
|
|
||||||
constructor(coordX, coordY){
|
|
||||||
this.coordX = coordX;
|
|
||||||
this.coordY = coordY;
|
|
||||||
chemins = new Array();
|
|
||||||
}
|
|
||||||
|
|
||||||
addCheminPossible(chemin){
|
|
||||||
this.chemins.push(chemin)
|
|
||||||
}
|
|
||||||
|
|
||||||
getCheminsPossibles(){
|
|
||||||
return this.chemins
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ModeAutomatique {
|
|
||||||
|
|
||||||
cheminsPossibles = new Array(); // liste des listes de noeuds
|
|
||||||
noeudsVisites = new Array();
|
|
||||||
|
|
||||||
// valeurs pour la première position
|
|
||||||
posX=0;
|
|
||||||
posY=0;
|
|
||||||
rotation=HAUT;
|
|
||||||
|
|
||||||
run(){
|
|
||||||
// TODO while find
|
|
||||||
this.analyseEnvironnement(); // met à jour ses connaissances (à dessiner au fur et à mesure ?)
|
|
||||||
this.move(); // récupère un chemin non encore parcourus en fonction de ses connaissances
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////// ANALYSE ENVIRONNEMENT
|
|
||||||
|
|
||||||
analyseEnvironnement(){
|
|
||||||
currentNode = getCaseAnalysee();
|
|
||||||
|
|
||||||
// SI case déjà visitée, alors on connait ses obstacles
|
|
||||||
if (currentNode == null) {
|
|
||||||
|
|
||||||
// pour les 4 directions possibles
|
|
||||||
for (let i = 0; i < 4; i++) {
|
|
||||||
if (!isObstacleDevant(currentNode, rotation)){
|
|
||||||
addCheminPossible(currentNode);
|
|
||||||
}
|
|
||||||
tourne(); // changement de l'angle de 90 degrés
|
|
||||||
}
|
|
||||||
this.noeudsVisites.push(currentNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isObstacleDevant(){
|
|
||||||
// TODO
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
tourne(){
|
|
||||||
if(this.rotation = Orientation.HAUT){
|
|
||||||
// TODO tourne
|
|
||||||
this.rotation = Orientation.GAUCHE;
|
|
||||||
} else if(this.rotation = Orientation.BAS){
|
|
||||||
// TODO tourne
|
|
||||||
this.rotation = Orientation.GAUCHE;
|
|
||||||
}else if(this.rotation = Orientation.BAS){
|
|
||||||
// TODO tourne
|
|
||||||
this.rotation = Orientation.DROITE;
|
|
||||||
}else if(this.rotation = Orientation.DROITE){
|
|
||||||
// TODO tourne
|
|
||||||
this.rotation = Orientation.HAUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addCheminPossible(){
|
|
||||||
chemin = new Array();
|
|
||||||
if(this.rotation = Orientation.HAUT){
|
|
||||||
chemin.push(new Noeud(posX+1, posY));
|
|
||||||
} else if(this.rotation = Orientation.BAS){
|
|
||||||
chemin.push(new Noeud(posX-1, posY));
|
|
||||||
}else if(this.rotation = Orientation.GAUCHE){
|
|
||||||
chemin.push(new Noeud(posX, posY-1));
|
|
||||||
}else if(this.rotation = Orientation.DROITE){
|
|
||||||
chemin.push(new Noeud(posX, posY+1));
|
|
||||||
}
|
|
||||||
this.cheminsPossibles.push(chemin);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////// MOVE
|
|
||||||
|
|
||||||
move(){
|
|
||||||
// on choisi le prochain noeud
|
|
||||||
nextNode = getNextNode();
|
|
||||||
// on met à jour la liste des chemins en ajoutant le noeud courant dans les chemins non choisis
|
|
||||||
updatePaths(nextNode);
|
|
||||||
// on met à jour les coordonnées
|
|
||||||
this.updateCoordonnees(nextNode);
|
|
||||||
// move
|
|
||||||
this.move(nextNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// parcours en profondeur : il s'agit de LIFOs
|
|
||||||
getNextNode(){
|
|
||||||
if (this.cheminsPossibles[this.cheminsPossibles.length - 1] > 0) {
|
|
||||||
// on récupère la dernière liste
|
|
||||||
cheminChoisi = this.cheminsPossibles[this.cheminsPossibles.length - 1];
|
|
||||||
// on récupère le dernier élément de cette liste
|
|
||||||
nodeChoisi = cheminChoisi[cheminChoisi.length - 1];
|
|
||||||
// on créé un nouveau noeud
|
|
||||||
return new Noeud(nodeChoisi.posX, nodeChoisi.posY);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePaths(nextNode){
|
|
||||||
nextNode = this.getCaseAnalysee();
|
|
||||||
// on ajoute le noeud visité à toutes les listes sauf la dernière
|
|
||||||
for (let i=0; i < this.cheminsPossibles -1; i++){
|
|
||||||
cheminPossible = this.cheminsPossibles[i];
|
|
||||||
if (nextNode == null){
|
|
||||||
// on ajoute le noeud en cours à la liste des noeuds visités
|
|
||||||
cheminPossible.push(nextNode);
|
|
||||||
} else {
|
|
||||||
// on vire le noeud visité de toutes les listes s'il a déjà été visité avant (on fait demi tour)
|
|
||||||
cheminPossible.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// on supprime les listes vides
|
|
||||||
cheminsPossibles = cheminsPossibles.filter(function (el) {
|
|
||||||
return el.length > 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCoordonnees() {
|
|
||||||
if(this.rotation = Orientation.HAUT){
|
|
||||||
this.posX++;
|
|
||||||
} else if(this.rotation = Orientation.BAS){
|
|
||||||
this.posX--;
|
|
||||||
}else if(this.rotation = Orientation.GAUCHE){
|
|
||||||
this.posY--;
|
|
||||||
}else if(this.rotation = Orientation.DROITE){
|
|
||||||
this.pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////// GETTERS
|
|
||||||
|
|
||||||
getCaseAnalysee(){
|
|
||||||
noeudsVisites.forEach((noeud) => {
|
|
||||||
if (noeud.posX == this.posX && noeud.posY == this.posY){
|
|
||||||
return noeud;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
from aiohttp import web, WSCloseCode
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def http_handler(request):
|
||||||
|
payload = await request.text()
|
||||||
|
print(payload)
|
||||||
|
|
||||||
|
url = 'http://192.168.185.166/turtle/send'
|
||||||
|
headers = {'content-type': 'application/json'}
|
||||||
|
r = requests.post(url, data=payload, headers=headers)
|
||||||
|
|
||||||
|
print(r.text)
|
||||||
|
|
||||||
|
return web.Response(text=r.text)
|
||||||
|
|
||||||
|
|
||||||
|
def create_runner():
|
||||||
|
app = web.Application()
|
||||||
|
app.add_routes([
|
||||||
|
web.post('/', http_handler),
|
||||||
|
])
|
||||||
|
return web.AppRunner(app)
|
||||||
|
|
||||||
|
|
||||||
|
async def start_server(host="127.0.0.1", port=4444):
|
||||||
|
runner = create_runner()
|
||||||
|
await runner.setup()
|
||||||
|
site = web.TCPSite(runner, host, port)
|
||||||
|
await site.start()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(start_server())
|
||||||
|
loop.run_forever()
|
||||||
|
|
||||||
@@ -1,23 +1,34 @@
|
|||||||
const URL = "192.168.185.166";
|
const ROBOT_URL = "192.168.185.166";
|
||||||
const INFO_POSITION = 0x01;
|
const CURL_URL = "localhost:4444";
|
||||||
const INFO_LED = 0x02;
|
const INFOS_POSITION = 0x01;
|
||||||
|
const INFOS_LED = 0x02;
|
||||||
|
const INFOS_MOTORS = 0x04;
|
||||||
|
const INFOS_WHEELS = 0x08;
|
||||||
|
const INFOS_SPEED = 0x10;
|
||||||
|
const INFOS_RANGEFINDER = 0x20;
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = true;
|
||||||
|
|
||||||
|
const MUR_THRESHOLD = 80;
|
||||||
|
const MASTER_SLEEP_TIME = 500;
|
||||||
|
|
||||||
let socketMoteur = undefined;
|
let socketMoteur = undefined;
|
||||||
let socketInfos = undefined;
|
let socketInfos = undefined;
|
||||||
let moteur = undefined;
|
let moteur = undefined;
|
||||||
let led = undefined;
|
let led = undefined;
|
||||||
let position = undefined;
|
let position = undefined;
|
||||||
let pid = undefined;
|
let turtle = undefined;
|
||||||
|
|
||||||
|
const sleep = async (time) => {
|
||||||
|
await new Promise(r => setTimeout(r, time));
|
||||||
|
}
|
||||||
|
|
||||||
const connectSockets = () => {
|
const connectSockets = () => {
|
||||||
if ((socketMoteur != undefined) || (socketInfos != undefined)) {
|
if ((socketMoteur != undefined) || (socketInfos != undefined)) {
|
||||||
disconnectSocket();
|
disconnectSocket();
|
||||||
}
|
}
|
||||||
socketMoteur = new WebSocket("ws://" + URL + "/motors.ws");
|
socketMoteur = new WebSocket("ws://" + ROBOT_URL + "/motors.ws");
|
||||||
socketInfos = new WebSocket("ws://" + URL + "/infos.ws");
|
socketInfos = new WebSocket("ws://" + ROBOT_URL + "/infos.ws");
|
||||||
socketInfos.binaryType = "arraybuffer";
|
socketInfos.binaryType = "arraybuffer";
|
||||||
|
|
||||||
socketMoteur.onclose = function(event) {
|
socketMoteur.onclose = function(event) {
|
||||||
@@ -36,6 +47,10 @@ const connectSockets = () => {
|
|||||||
console.log("[socketInfos] Connection established");
|
console.log("[socketInfos] Connection established");
|
||||||
position.reset();
|
position.reset();
|
||||||
led.green();
|
led.green();
|
||||||
|
new Promise(r => setTimeout(r, 2000)).then(() => {
|
||||||
|
led.off();
|
||||||
|
});
|
||||||
|
socketInfos.send(new Uint8Array([0x32, 0x20]));
|
||||||
};
|
};
|
||||||
|
|
||||||
socketInfos.onclose = function(event) {
|
socketInfos.onclose = function(event) {
|
||||||
@@ -105,82 +120,12 @@ class Moteur {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PID {
|
|
||||||
|
|
||||||
kpx = 0;
|
|
||||||
kix = 0;
|
|
||||||
kdx = 0;
|
|
||||||
|
|
||||||
kpy = 0;
|
|
||||||
kiy = 0;
|
|
||||||
kdy = 0;
|
|
||||||
|
|
||||||
kpr = 0;
|
|
||||||
kir = 0;
|
|
||||||
kdr = 0;
|
|
||||||
|
|
||||||
px = 0;
|
|
||||||
ix = 0;
|
|
||||||
dx = 0;
|
|
||||||
|
|
||||||
py = 0;
|
|
||||||
iy = 0;
|
|
||||||
dy = 0;
|
|
||||||
|
|
||||||
pr = 0;
|
|
||||||
ir = 0;
|
|
||||||
dr = 0;
|
|
||||||
|
|
||||||
tagetX = 0;
|
|
||||||
tagetY = 0;
|
|
||||||
targetR = 0;
|
|
||||||
run = false;
|
|
||||||
|
|
||||||
lastTime = undefined;
|
|
||||||
|
|
||||||
set(targetX, targetY, targetR) {
|
|
||||||
this.targetX = targetX;
|
|
||||||
this.px = 0;
|
|
||||||
this.ix = 0;
|
|
||||||
this.dx = 0;
|
|
||||||
|
|
||||||
this.targetY = targetY;
|
|
||||||
this.py = 0;
|
|
||||||
this.iy = 0;
|
|
||||||
this.dy = 0;
|
|
||||||
|
|
||||||
this.targetR = targetR;
|
|
||||||
this.pr = 0;
|
|
||||||
this.ir = 0;
|
|
||||||
this.dr = 0;
|
|
||||||
|
|
||||||
this.lastTime = Date.now().valueOf();
|
|
||||||
this.run = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get(currentX, currentY, currentR) {
|
|
||||||
if (!this.run) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const currentTime = Date.now().valueOf();
|
|
||||||
const deltaTime = (currentTime - this.lastTime) / 1000.0;
|
|
||||||
|
|
||||||
this.p
|
|
||||||
|
|
||||||
|
|
||||||
console.log("Time delta : " + deltaTime);
|
|
||||||
this.lastTime = currentTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Led {
|
class Led {
|
||||||
|
|
||||||
http = undefined;
|
http = undefined;
|
||||||
|
|
||||||
async send(color){
|
async send(color){
|
||||||
await fetch('http://' + URL + '/led/set_color', {
|
await fetch('http://' + ROBOT_URL + '/led/set_color', {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
mode: "no-cors",
|
mode: "no-cors",
|
||||||
@@ -211,78 +156,434 @@ class Position {
|
|||||||
posX = 0;
|
posX = 0;
|
||||||
posY = 0;
|
posY = 0;
|
||||||
Rot = 0;
|
Rot = 0;
|
||||||
|
capteur_distance = 0;
|
||||||
|
distance_mur_moyenne = 0;
|
||||||
|
|
||||||
constructor(socket) {
|
constructor(socket) {
|
||||||
socket.addEventListener("message", e => {
|
socket.addEventListener("message", e => {
|
||||||
const view = new DataView(e.data);
|
const view = new DataView(e.data);
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
|
||||||
const mask = view.getUint8(0);
|
const mask = view.getUint8(pos);
|
||||||
|
pos++;
|
||||||
if (mask & INFO_POSITION) {
|
|
||||||
const posX = view.getFloat32(1);
|
if (mask & INFOS_POSITION) {
|
||||||
const poxY = view.getFloat32(5);
|
this.posX = view.getFloat32(pos + 0);
|
||||||
const posR = view.getFloat32(9);
|
this.posY = view.getFloat32(pos + 4);
|
||||||
|
this.Rot = view.getFloat32(pos + 8);
|
||||||
|
pos += 3*4;
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.log(
|
console.log(
|
||||||
{"posX": posX, "posY": poxY, "Rot": posR}
|
{"posX": this.posX, "posY": this.posY, "Rot": this.Rot}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pid.get(posX, this.posY, posR);
|
if (mask & INFOS_LED) {
|
||||||
|
const led_r = view.getUint8(pos + 0);
|
||||||
|
const led_g = view.getUint8(pos + 1);
|
||||||
|
const led_b = view.getUint8(pos + 2);
|
||||||
|
pos += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_MOTORS) {
|
||||||
|
const ml = view.getFloat32(pos + 0);
|
||||||
|
const mr = view.getFloat32(pos + 4);
|
||||||
|
pos += 2*4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_WHEELS) {
|
||||||
|
const wl = view.getInt16(pos + 0);
|
||||||
|
const wr = view.getInt16(pos + 2);
|
||||||
|
const tl = view.getInt16(pos + 4);
|
||||||
|
const tr = view.getInt16(pos + 6);
|
||||||
|
pos += 4*2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_SPEED) {
|
||||||
|
const w = view.getFloat32(pos + 0);
|
||||||
|
const v = view.getFloat32(pos + 4);
|
||||||
|
pos += 4*2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_RANGEFINDER) {
|
||||||
|
this.capteur_distance = view.getUint16(pos + 0);
|
||||||
|
this.distance_mur_moyenne = (this.distance_mur_moyenne*2 + this.capteur_distance)/3;
|
||||||
|
pos += 2;
|
||||||
|
if (DEBUG) {
|
||||||
|
console.log("Capteur distance : " + this.capteur_distance);
|
||||||
|
console.log("Distance moyenne : " + this.distance_mur_moyenne);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async reset() {
|
async reset() {
|
||||||
await fetch("http://" + URL + "/position/reset", {
|
await fetch("http://" + ROBOT_URL + "/position/reset", {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
mode: "no-cors"
|
mode: "no-cors"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCapteurDistance() {
|
||||||
|
return this.capteur_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
isMurDevant() {
|
||||||
|
return this.distance_mur_moyenne < MUR_THRESHOLD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Turtle {
|
||||||
|
|
||||||
|
async avancer(distance) {
|
||||||
|
const data = {
|
||||||
|
'type': 'dist',
|
||||||
|
'dist': distance,
|
||||||
|
}
|
||||||
|
await fetch("http://" + CURL_URL, {
|
||||||
|
method: "POST",
|
||||||
|
mode: "no-cors",
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
await sleep(MASTER_SLEEP_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
async tourner(angle) {
|
||||||
|
const data = {
|
||||||
|
'type': 'angle',
|
||||||
|
'angle': angle,
|
||||||
|
}
|
||||||
|
await fetch("http://" + CURL_URL, {
|
||||||
|
method: "POST",
|
||||||
|
mode: "no-cors",
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
await sleep(MASTER_SLEEP_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const Orientation = Object.freeze({
|
||||||
|
HAUT: 0,
|
||||||
|
DROITE: 1,
|
||||||
|
BAS: 2,
|
||||||
|
GAUCHE: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
class Noeud {
|
||||||
|
coordX;
|
||||||
|
coordY;
|
||||||
|
chemins;
|
||||||
|
|
||||||
|
constructor(coordX, coordY){
|
||||||
|
this.coordX = coordX;
|
||||||
|
this.coordY = coordY;
|
||||||
|
this.chemins = new Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
addCheminPossible(chemin){
|
||||||
|
this.chemins.push(chemin);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCheminsPossibles(){
|
||||||
|
return this.chemins;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(){
|
||||||
|
console.log("node : (" + this.getX + "; " + this.getY + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
toStringWithVoisins(){
|
||||||
|
console.log("node : (" + this.getX + "; " + this.getY + ")");
|
||||||
|
console.log("voisins connus : " + this.chemins.forEach((chemin) => chemin.toString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ModeAutomatique {
|
||||||
|
|
||||||
|
cheminsPossibles = new Array(); // liste des listes de noeuds
|
||||||
|
noeudsVisites = new Array();
|
||||||
|
|
||||||
|
// valeurs pour la première position
|
||||||
|
posX=0;
|
||||||
|
posY=0;
|
||||||
|
rotation = Orientation.HAUT;
|
||||||
|
lastRotation = this.rotation;
|
||||||
|
|
||||||
|
isFirstNode = true;
|
||||||
|
|
||||||
|
async run(){
|
||||||
|
led.blue();
|
||||||
|
let iter = 0;
|
||||||
|
while(!this.isObjectifTrouve()){
|
||||||
|
if (iter >= 50)
|
||||||
|
break;
|
||||||
|
console.log("Iteration " + iter++);
|
||||||
|
console.log(this.cheminsPossibles);
|
||||||
|
let currentNode = this.getCaseAnalysee();
|
||||||
|
currentNode = await this.analyseEnvironnement(currentNode); // met à jour ses connaissances (à dessiner au fur et à mesure ?)
|
||||||
|
await this.deplacer(currentNode); // récupère un chemin non encore parcourus en fonction de ses connaissances
|
||||||
|
}
|
||||||
|
led.off();
|
||||||
|
}
|
||||||
|
|
||||||
|
isObjectifTrouve(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////// ANALYSE ENVIRONNEMENT
|
||||||
|
|
||||||
|
async isObstacleDevant () {
|
||||||
|
//await turtle.avancer(20);
|
||||||
|
await sleep(2000);
|
||||||
|
const mur = position.isMurDevant();
|
||||||
|
//await turtle.avancer(-20);
|
||||||
|
return mur;
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyseEnvironnement(currentNode){
|
||||||
|
// SI case déjà visitée, alors on connait ses obstacles
|
||||||
|
if (currentNode == null) {
|
||||||
|
console.log(">>>>>>>>> iteration ")
|
||||||
|
currentNode = new Noeud(0, 0);
|
||||||
|
|
||||||
|
// pour les 4 directions possibles
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
if (!await this.isObstacleDevant()){
|
||||||
|
this.addCheminPossible(currentNode);
|
||||||
|
console.log("chemins possibles : ");
|
||||||
|
console.log(this.cheminsPossibles);
|
||||||
|
}
|
||||||
|
await this.tourne(); // changement de l'angle de 90 degrés
|
||||||
|
if (i == 1 && !this.isFirstNode) {
|
||||||
|
this.tourne(); // On scan pas notre pt d'entrée
|
||||||
|
this.isFirstNode = false;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.noeudsVisites.push(currentNode);
|
||||||
|
console.log("noeuds visités : " + this.noeudsVisites)
|
||||||
|
}
|
||||||
|
return currentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
async tourne(){
|
||||||
|
await turtle.tourner(90);
|
||||||
|
this.rotation--;
|
||||||
|
if (this.rotation < 0) {
|
||||||
|
this.rotation = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addCheminPossible(){
|
||||||
|
const chemin = new Array();
|
||||||
|
console.log("rotation : " + this.rotation);
|
||||||
|
if(this.rotation == 0){
|
||||||
|
console.log("rotation 0 ");
|
||||||
|
chemin.push(new Noeud(this.posX, this.posY+1));
|
||||||
|
} else if(this.rotation == 2){
|
||||||
|
console.log("rotation 2 ");
|
||||||
|
chemin.push(new Noeud(this.posX, this.posY-1));
|
||||||
|
}else if(this.rotation == 3){
|
||||||
|
console.log("rotation 3 ");
|
||||||
|
chemin.push(new Noeud(this.posX-1, this.posY));
|
||||||
|
}else if(this.rotation == 1){
|
||||||
|
console.log("rotation 1");
|
||||||
|
chemin.push(new Noeud(this.posX+1, this.posY));
|
||||||
|
}
|
||||||
|
console.log(chemin);
|
||||||
|
this.cheminsPossibles.push(chemin);
|
||||||
|
console.log(this.cheminsPossibles);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////// MOVE
|
||||||
|
|
||||||
|
async deplacer(currentNode){
|
||||||
|
// on choisi le prochain noeud
|
||||||
|
const nextNode = this.getNextNode();
|
||||||
|
if (nextNode != null) {
|
||||||
|
console.log("next node : ");
|
||||||
|
nextNode.toString();
|
||||||
|
}
|
||||||
|
// on met à jour la liste des chemins en ajoutant le noeud courant dans les chemins non choisis
|
||||||
|
this.updatePaths(nextNode);
|
||||||
|
// on met à jour les coordonnées
|
||||||
|
this.updateCoordonnees(currentNode, nextNode);
|
||||||
|
// move
|
||||||
|
await this.move();
|
||||||
|
}
|
||||||
|
|
||||||
|
// parcours en profondeur : il s'agit de LIFOs
|
||||||
|
getNextNode(){
|
||||||
|
if (this.cheminsPossibles.length > 0) {
|
||||||
|
// on récupère la dernière liste
|
||||||
|
const cheminChoisi = this.cheminsPossibles[this.cheminsPossibles.length - 1];
|
||||||
|
// on récupère le dernier élément de cette liste
|
||||||
|
const nodeChoisi = cheminChoisi[cheminChoisi.length - 1];
|
||||||
|
console.log("noeud choisi :");
|
||||||
|
nodeChoisi.toString();
|
||||||
|
// on créé un nouveau noeud
|
||||||
|
return new Noeud(nodeChoisi.posX, nodeChoisi.posY);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePaths(nextNode){
|
||||||
|
// on ajoute le noeud visité à toutes les listes sauf la dernière
|
||||||
|
for (let i=0; i < this.cheminsPossibles.length -1; i++){
|
||||||
|
const cheminPossible = this.cheminsPossibles[i];
|
||||||
|
|
||||||
|
if (this.getCaseAnalyseeWithCoord(nextNode.getX, nextNode.getY == null)){
|
||||||
|
// on ajoute le noeud en cours à la liste des noeuds visités
|
||||||
|
cheminPossible.push(nextNode);
|
||||||
|
} else {
|
||||||
|
// on vire le noeud visité de toutes les listes s'il a déjà été visité avant (on fait demi tour)
|
||||||
|
cheminPossible.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// on supprime les listes vides
|
||||||
|
this.cheminsPossibles = this.cheminsPossibles.filter(function (el) {
|
||||||
|
return el.length > 0;
|
||||||
|
});
|
||||||
|
console.log("nombre de chemins restants : " + this.cheminsPossibles.length)
|
||||||
|
console.log("chemins possibles : " + this.cheminsPossibles)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCoordonnees(currentNode, nextNode) {
|
||||||
|
console.log("coordonnees : (" + this.posX + "; " + this.posY + ")");
|
||||||
|
this.lastRotation = this.rotation;
|
||||||
|
if (currentNode.getX > nextNode.getX){
|
||||||
|
this.rotation = 3
|
||||||
|
this.posY--;
|
||||||
|
} else if (currentNode.getX < nextNode.getX){
|
||||||
|
this.rotation = 1
|
||||||
|
this.pos++;
|
||||||
|
} else if (currentNode.getY > nextNode.getY){
|
||||||
|
this.rotation = 2
|
||||||
|
this.posX--;
|
||||||
|
} else {
|
||||||
|
this.rotation = 0
|
||||||
|
this.posX++;
|
||||||
|
}
|
||||||
|
console.log("new coordonnees : (" + this.posX + "; " + this.posY + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
async move(){
|
||||||
|
// déplacer robot (les coordonnées sont déjà à jour)
|
||||||
|
let aRotater = this.rotation - this.lastRotation;
|
||||||
|
if (aRotater > 2) {
|
||||||
|
aRotater -= 2;
|
||||||
|
}
|
||||||
|
if (aRotater < 2) {
|
||||||
|
aRotater += 2;
|
||||||
|
}
|
||||||
|
if (aRotater > 0) {
|
||||||
|
aRotater = 4 - aRotater;
|
||||||
|
}
|
||||||
|
aRotater *= 90;
|
||||||
|
turtle.tourner(aRotater);
|
||||||
|
await turtle.avancer(190);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////// GETTERS
|
||||||
|
|
||||||
|
getCaseAnalysee(){
|
||||||
|
let index = 0;
|
||||||
|
console.log(this.noeudsVisites)
|
||||||
|
this.noeudsVisites.forEach((noeud) => {
|
||||||
|
if (noeud == null) {
|
||||||
|
console.error(this.noeudsVisites);
|
||||||
|
console.error("INDEX NULL : " + (index++));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((noeud.posX == this.posX) && (noeud.posY == this.posY)){
|
||||||
|
return noeud;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCaseAnalyseeWithCoord(x, y){
|
||||||
|
let index = 0;
|
||||||
|
console.log(this.noeudsVisites)
|
||||||
|
this.noeudsVisites.forEach((noeud) => {
|
||||||
|
if (noeud == null) {
|
||||||
|
console.error(this.noeudsVisites);
|
||||||
|
console.error("INDEX NULL : " + (index++));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((noeud.posX == x) && (noeud.posY == y)){
|
||||||
|
return noeud;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let doRun = false;
|
||||||
|
|
||||||
|
|
||||||
|
async function isMurDevant() {
|
||||||
|
turtle.avancer(20);
|
||||||
|
await sleep(2000);
|
||||||
|
const mur = position.isMurDevant();
|
||||||
|
turtle.avancer(-20);
|
||||||
|
|
||||||
|
return mur;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function algo() {
|
||||||
|
doRun = true;
|
||||||
|
while(doRun) {
|
||||||
|
|
||||||
|
await turtle.avancer(1500);
|
||||||
|
await turtle.avancer(-20);
|
||||||
|
await turtle.tourner(30);
|
||||||
|
/*
|
||||||
|
|
||||||
|
let mur = isMurDevant();
|
||||||
|
if (!mur) {
|
||||||
|
await turtle.avancer(75);
|
||||||
|
}
|
||||||
|
await turtle.tourner(90);
|
||||||
|
mur = isMurDevant();
|
||||||
|
if (mur) {
|
||||||
|
await turtle.tourner(-90);
|
||||||
|
await turtle.tourner(-90);
|
||||||
|
await turtle.tourner(-90);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
|
|
||||||
|
|
||||||
connectSockets();
|
connectSockets();
|
||||||
moteur = new Moteur();
|
moteur = new Moteur();
|
||||||
led = new Led();
|
led = new Led();
|
||||||
position = new Position(socketInfos);
|
position = new Position(socketInfos);
|
||||||
pid = new PID();
|
turtle = new Turtle();
|
||||||
led.red();
|
led.red();
|
||||||
|
|
||||||
|
algo();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
led.red();
|
||||||
|
await sleep(400);
|
||||||
const socket = new WebSocket("ws://127.0.0.1:1337/ws");
|
|
||||||
socket.onclose = function(event) {
|
|
||||||
if (event.wasClean) {
|
|
||||||
console.log(`[socket] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
|
|
||||||
} else {
|
|
||||||
console.log('[socket] Connection died');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.onopen = function(e) {
|
|
||||||
console.log("[socket] Connection established");
|
|
||||||
socket.send("Test");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.onerror = function(error) {
|
|
||||||
console.log(`[socket] ERROR : ${error}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.addEventListener("message", e => {
|
led.green();
|
||||||
const data = e.data;
|
await sleep(400);
|
||||||
console.log(data);
|
|
||||||
});
|
led.blue();
|
||||||
|
await sleep(400);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+24
-1
@@ -12118,4 +12118,27 @@ input:checked + .slider:before {
|
|||||||
|
|
||||||
.slider.round:before {
|
.slider.round:before {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#map {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pico canvas {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pico :where(audio, canvas, iframe, img, svg, video) {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, ::after, ::before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
overflow-clip-margin: content-box;
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
+98
-28
@@ -4472,26 +4472,39 @@
|
|||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const motors_buf = new Float32Array(2);
|
||||||
|
const motors_ml = document.getElementById("motors_ml")
|
||||||
|
const motors_mr = document.getElementById("motors_mr")
|
||||||
|
const motors_stop = document.getElementById("motors_stop")
|
||||||
|
const motors_joystick = document.getElementById('motors_joystick');
|
||||||
|
const motors_canvas = document.getElementById('motors_canvas');
|
||||||
|
|
||||||
|
let motors_ws = null;
|
||||||
|
|
||||||
|
const send_motors = (vl, vr) => {
|
||||||
|
/*if (motors_ws && motors_ws.readyState == WebSocket.OPEN) {
|
||||||
|
const lim = v => Math.max(-1, Math.min(v, 1));
|
||||||
|
motors_buf[0] = lim(vl);
|
||||||
|
motors_buf[1] = lim(vr);
|
||||||
|
motors_ml.value = vl * 100;
|
||||||
|
motors_mr.value = vr * 100;
|
||||||
|
motors_ws.send(motors_buf);
|
||||||
|
} else if (!motors_ws || motors_ws.readyState != WebSocket.CONNECTING) {
|
||||||
|
motors_ws = new WebSocket('ws://' + document.location.host + '/motors.ws');
|
||||||
|
motors_ws.addEventListener("open", () => send_motors(vl, vr));
|
||||||
|
}*/
|
||||||
|
};
|
||||||
|
|
||||||
|
const update_motors = () => {
|
||||||
|
const vl = motors_ml.value / 100;
|
||||||
|
const vr = motors_mr.value / 100;
|
||||||
|
send_motors(vl, vr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const index_umd = {
|
|
||||||
Alert,
|
|
||||||
Button,
|
|
||||||
Carousel,
|
|
||||||
Collapse,
|
|
||||||
Dropdown,
|
|
||||||
Modal,
|
|
||||||
Offcanvas,
|
|
||||||
Popover,
|
|
||||||
ScrollSpy,
|
|
||||||
Tab,
|
|
||||||
Toast,
|
|
||||||
Tooltip
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("colorLed").onchange=function(){
|
|
||||||
// do whatever you want with value
|
|
||||||
console.log(this.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const btnsz = 40;
|
const btnsz = 40;
|
||||||
let positionX = 0, positionY = 0;
|
let positionX = 0, positionY = 0;
|
||||||
@@ -4503,20 +4516,19 @@
|
|||||||
const h = motors_joystick.height;
|
const h = motors_joystick.height;
|
||||||
ctx.clearRect(0, 0, w, h);
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
ctx.fillStyle = "#00aa0040";
|
ctx.fillStyle = "#00aaff40";
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(w/2, h/2, w/2-btnsz, 0, 2 * Math.PI);
|
ctx.arc(w/2, h/2, w/2-btnsz, 0, 2 * Math.PI);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
ctx.strokeStyle = "#009900";
|
ctx.strokeStyle = "#0099ee";
|
||||||
ctx.fillStyle = "#00aa00";
|
ctx.fillStyle = "#00aaff";
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(w/2 + positionX, h/2 + positionY, btnsz, 0, 2 * Math.PI);
|
ctx.arc(w/2 + positionX, h/2 + positionY, btnsz, 0, 2 * Math.PI);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePosition = e => {
|
const updatePosition = e => {
|
||||||
const clientX = e.clientX || e.touches[0].clientX;
|
const clientX = e.clientX || e.touches[0].clientX;
|
||||||
const clientY = e.clientY || e.touches[0].clientY;
|
const clientY = e.clientY || e.touches[0].clientY;
|
||||||
@@ -4531,11 +4543,9 @@
|
|||||||
}
|
}
|
||||||
positionX = vx * kw;
|
positionX = vx * kw;
|
||||||
positionY = vy * kw;
|
positionY = vy * kw;
|
||||||
console.log("PositionX : "+ Math.round(positionX))
|
|
||||||
console.log("PositionY : "+ Math.round(positionY))
|
|
||||||
vy *= -1;
|
vy *= -1;
|
||||||
if (performance.now() - lastsend > 250) {
|
if (performance.now() - lastsend > 250) {
|
||||||
//send_motors(vy+vx, vy-vx);
|
send_motors(vy+vx, vy-vx);
|
||||||
lastsend = performance.now();
|
lastsend = performance.now();
|
||||||
}
|
}
|
||||||
joystick_draw();
|
joystick_draw();
|
||||||
@@ -4552,7 +4562,7 @@
|
|||||||
};
|
};
|
||||||
const onup = () => {
|
const onup = () => {
|
||||||
if (clicked) {
|
if (clicked) {
|
||||||
//send_motors(0, 0);
|
send_motors(0, 0);
|
||||||
motors_joystick.style.cursor = ''
|
motors_joystick.style.cursor = ''
|
||||||
clicked = false;
|
clicked = false;
|
||||||
positionX = positionY = 0;
|
positionX = positionY = 0;
|
||||||
@@ -4568,10 +4578,70 @@
|
|||||||
motors_joystick.addEventListener('touchmove', onmove);
|
motors_joystick.addEventListener('touchmove', onmove);
|
||||||
motors_joystick.addEventListener('touchend', onup);
|
motors_joystick.addEventListener('touchend', onup);
|
||||||
motors_joystick.addEventListener('touchcancel', onup);
|
motors_joystick.addEventListener('touchcancel', onup);
|
||||||
|
|
||||||
|
//const text_color = getComputedStyle(motors_canvas).color;
|
||||||
|
document.body.addEventListener('h:infos:motors', e => {
|
||||||
|
const ctx = motors_canvas.getContext("2d");
|
||||||
|
const w = motors_canvas.width;
|
||||||
|
const h = motors_canvas.height;
|
||||||
|
const m = 5;
|
||||||
|
const ml = e.detail[0];
|
||||||
|
const mr = e.detail[1];
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.strokeStyle = "#808080"
|
||||||
|
ctx.fillStyle = "#008000"
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(m, m, w-2*m, (h/2-m));
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(m, (h/2-m)+m, w-2*m, (h/2-m));
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
if (ml > 0)
|
||||||
|
ctx.rect(w/2, m+1, ml*(w/2-m), (h/2-m)-2);
|
||||||
|
else
|
||||||
|
ctx.rect(w/2+ml*(w/2-m), m+1, -ml*(w/2-m), (h/2-m)-2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
if (ml > 0)
|
||||||
|
ctx.rect(w/2, (h/2-m)+m+1, mr*(w/2-m), (h/2-m)-2);
|
||||||
|
else
|
||||||
|
ctx.rect(w/2+mr*(w/2-m), (h/2-m)+m+1, -mr*(w/2-m), (h/2-m)-2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
|
||||||
|
/*document.body.addEventListener('h:section:statechanged', e => {
|
||||||
|
infos_mask(INFOS_MOTORS, e.detail['on']);
|
||||||
|
});
|
||||||
|
infos_mask(INFOS_MOTORS, true);*/
|
||||||
|
|
||||||
|
document.getElementById("colorLed").value = "#ffffff"
|
||||||
|
document.getElementById("colorLed").onchange=function(){
|
||||||
|
// do whatever you want with value
|
||||||
|
console.log(this.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const index_umd = {
|
||||||
|
Alert,
|
||||||
|
Button,
|
||||||
|
Carousel,
|
||||||
|
Collapse,
|
||||||
|
Dropdown,
|
||||||
|
Modal,
|
||||||
|
Offcanvas,
|
||||||
|
Popover,
|
||||||
|
ScrollSpy,
|
||||||
|
Tab,
|
||||||
|
Toast,
|
||||||
|
Tooltip
|
||||||
|
};
|
||||||
|
|
||||||
return index_umd;
|
return index_umd;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
//# sourceMappingURL=bootstrap.js.map
|
//# sourceMappingURL=bootstrap.js.map
|
||||||
|
|||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Vendored
+482
@@ -0,0 +1,482 @@
|
|||||||
|
//Infos
|
||||||
|
|
||||||
|
const INFOS_POSITION = 0x01;
|
||||||
|
const INFOS_LED = 0x02;
|
||||||
|
const INFOS_MOTORS = 0x04;
|
||||||
|
const INFOS_WHEELS = 0x08;
|
||||||
|
const INFOS_SPEED = 0x10;
|
||||||
|
const INFOS_RANGEFINDER = 0x20;
|
||||||
|
|
||||||
|
let infos_ws = null;
|
||||||
|
let period_ms = 500;
|
||||||
|
let mask = 0;
|
||||||
|
|
||||||
|
function on_msg(e) {
|
||||||
|
const view = new DataView(e.data);
|
||||||
|
let pos = 0;
|
||||||
|
|
||||||
|
const mask = view.getUint8(pos);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (mask & INFOS_POSITION) {
|
||||||
|
const position_x = view.getFloat32(pos + 0);
|
||||||
|
const position_y = view.getFloat32(pos + 4);
|
||||||
|
const position_a = view.getFloat32(pos + 8);
|
||||||
|
pos += 3*4;
|
||||||
|
document.body.dispatchEvent(new CustomEvent('h:infos:position', {
|
||||||
|
'detail': [position_x, position_y, position_a]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_LED) {
|
||||||
|
const led_r = view.getUint8(pos + 0);
|
||||||
|
const led_g = view.getUint8(pos + 1);
|
||||||
|
const led_b = view.getUint8(pos + 2);
|
||||||
|
pos += 3;
|
||||||
|
|
||||||
|
document.body.dispatchEvent(new CustomEvent('h:infos:led', {
|
||||||
|
'detail': [led_r, led_g, led_b]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_MOTORS) {
|
||||||
|
const ml = view.getFloat32(pos + 0);
|
||||||
|
const mr = view.getFloat32(pos + 4);
|
||||||
|
pos += 2*4;
|
||||||
|
|
||||||
|
document.body.dispatchEvent(new CustomEvent('h:infos:motors', {
|
||||||
|
'detail': [ml, mr]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_WHEELS) {
|
||||||
|
const wl = view.getInt16(pos + 0);
|
||||||
|
const wr = view.getInt16(pos + 2);
|
||||||
|
const tl = view.getInt16(pos + 4);
|
||||||
|
const tr = view.getInt16(pos + 6);
|
||||||
|
pos += 4*2;
|
||||||
|
|
||||||
|
document.body.dispatchEvent(new CustomEvent('h:infos:wheels', {
|
||||||
|
'detail': [wl, wr, tl, tr]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_SPEED) {
|
||||||
|
const w = view.getFloat32(pos + 0);
|
||||||
|
const v = view.getFloat32(pos + 4);
|
||||||
|
pos += 4*2;
|
||||||
|
|
||||||
|
document.body.dispatchEvent(new CustomEvent('h:infos:speed', {
|
||||||
|
'detail': [w, v]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & INFOS_RANGEFINDER) {
|
||||||
|
const d = view.getUint16(pos + 0);
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
console.log(d);
|
||||||
|
|
||||||
|
document.body.dispatchEvent(new CustomEvent('h:infos:rangefinder', {
|
||||||
|
'detail': [d]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function send_mask() {
|
||||||
|
/*if (mask == 0) {
|
||||||
|
if (infos_ws) {
|
||||||
|
infos_ws.close();
|
||||||
|
infos_ws = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (infos_ws) {
|
||||||
|
if (infos_ws.readyState == WebSocket.OPEN) {
|
||||||
|
const buf = new Uint8Array(2);
|
||||||
|
buf[0] = period_ms / 10;
|
||||||
|
buf[1] = mask;
|
||||||
|
infos_ws.send(buf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
infos_ws = new WebSocket('ws://' + document.location.host + '/infos.ws');
|
||||||
|
infos_ws.binaryType = "arraybuffer";
|
||||||
|
infos_ws.addEventListener("open", send_mask);
|
||||||
|
infos_ws.addEventListener("message", on_msg);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function infos_mask(v, on) {
|
||||||
|
if (on)
|
||||||
|
mask |= v;
|
||||||
|
else
|
||||||
|
mask &= ~v;
|
||||||
|
send_mask();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
|
//main
|
||||||
|
document.body.innerHTML = `
|
||||||
|
<header class="pico">
|
||||||
|
<details class="dropdown">
|
||||||
|
<summary>Sections…</summary>
|
||||||
|
<ul id="ul_header_menu"></ul>
|
||||||
|
</details>
|
||||||
|
<h1>MinHAUMtaure</h1>
|
||||||
|
</header>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const sections_opened = new Set();
|
||||||
|
if (sessionStorage.getItem('sections_opened')) {
|
||||||
|
for (const s of sessionStorage.getItem('sections_opened').split('\n'))
|
||||||
|
sections_opened.add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sections = [
|
||||||
|
{ 'id': 'led', 'title': 'Led' },
|
||||||
|
{ 'id': 'motors', 'title': 'Moteurs' },
|
||||||
|
{ 'id': 'wheels', 'title': 'Retour roues' },
|
||||||
|
{ 'id': 'position', 'title': 'Position' },
|
||||||
|
{ 'id': 'turtle', 'title': 'Turtle' },
|
||||||
|
{ 'id': 'rangefinder', 'title': 'Télémètre' },
|
||||||
|
{ 'id': 'config', 'title': 'Config' },
|
||||||
|
{ 'id': 'wlan', 'title': 'Wifi' },
|
||||||
|
];
|
||||||
|
const ul_header_menu = document.getElementById('ul_header_menu');
|
||||||
|
for (const d of sections) {
|
||||||
|
const s = document.createElement('section');
|
||||||
|
const id = d['id'];
|
||||||
|
const title = d['title'];
|
||||||
|
s.id = 'section_' + id;
|
||||||
|
document.body.append(s);
|
||||||
|
|
||||||
|
const li = document.createElement('li');
|
||||||
|
ul_header_menu.append(li);
|
||||||
|
const label = document.createElement('label');
|
||||||
|
li.append(label);type="module"
|
||||||
|
const checkbox = document.createElement('input');
|
||||||
|
checkbox.type = 'checkbox';
|
||||||
|
checkbox.checked = false;
|
||||||
|
label.append(checkbox, title);
|
||||||
|
|
||||||
|
const h2 = document.createElement('h2');
|
||||||
|
const b_spans = document.createElement('b');
|
||||||
|
const span_fs = document.createElement('span');
|
||||||
|
span_fs.innerText = "⛶";
|
||||||
|
const span_close = document.createElement('span');
|
||||||
|
span_close.innerText = "×";
|
||||||
|
b_spans.append(span_fs, span_close);
|
||||||
|
h2.append(title, b_spans);
|
||||||
|
s.insertBefore(h2, s.firstChild);
|
||||||
|
s.classList.add('pico');
|
||||||
|
span_close.addEventListener('click', () => {
|
||||||
|
if (document.fullscreenElement != null)
|
||||||
|
document.exitFullscreen();
|
||||||
|
toggle(false);
|
||||||
|
});
|
||||||
|
span_fs.addEventListener('click', () => {
|
||||||
|
if (document.fullscreenElement == null)
|
||||||
|
s.requestFullscreen();
|
||||||
|
else
|
||||||
|
document.exitFullscreen();
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggle = on => {
|
||||||
|
s.classList.toggle('opened', on);
|
||||||
|
checkbox.checked = on;
|
||||||
|
if (on)
|
||||||
|
sections_opened.add(id);
|
||||||
|
else
|
||||||
|
sections_opened.delete(id);
|
||||||
|
sessionStorage.setItem('sections_opened', Array.from(sections_opened).join('\n'));
|
||||||
|
if (on)
|
||||||
|
import('./' + id + '.js');
|
||||||
|
document.body.dispatchEvent(new CustomEvent('h:section:statechanged', {
|
||||||
|
'detail': {
|
||||||
|
'id': id,
|
||||||
|
'on': on,
|
||||||
|
'section': s
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
checkbox.addEventListener('click', e => toggle(e.target.checked));
|
||||||
|
if (sections_opened.has(id)) toggle(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function form_post(form) {
|
||||||
|
const data = new URLSearchParams();
|
||||||
|
for (const pair of new FormData(form)) {
|
||||||
|
data.append(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
return await fetch(form.action, {
|
||||||
|
method: 'post',
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
body: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
|
//motors
|
||||||
|
|
||||||
|
const section = document.getElementById('section_motors');
|
||||||
|
section.insertAdjacentHTML('beforeend', `
|
||||||
|
<div class="grid">
|
||||||
|
<p><canvas id="motors_joystick" width="400" height="400"></canvas></p>
|
||||||
|
<p>
|
||||||
|
<input type="range" min="-100" max="100" value="0" id="motors_ml" /><br/>
|
||||||
|
<input type="range" min="-100" max="100" value="0" id="motors_mr" /><br/>
|
||||||
|
<input type="button" id="motors_stop" value="Stop" />
|
||||||
|
<canvas id="motors_canvas" width="350" height="40"></canvas>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const motors_buf = new Float32Array(2);
|
||||||
|
const motors_ml = document.getElementById("motors_ml")
|
||||||
|
const motors_mr = document.getElementById("motors_mr")
|
||||||
|
const motors_stop = document.getElementById("motors_stop")
|
||||||
|
const motors_joystick = document.getElementById('motors_joystick');
|
||||||
|
const motors_canvas = document.getElementById('motors_canvas');
|
||||||
|
|
||||||
|
let motors_ws = null;
|
||||||
|
|
||||||
|
const send_motors = (vl, vr) => {
|
||||||
|
/*if (motors_ws && motors_ws.readyState == WebSocket.OPEN) {
|
||||||
|
const lim = v => Math.max(-1, Math.min(v, 1));
|
||||||
|
motors_buf[0] = lim(vl);
|
||||||
|
motors_buf[1] = lim(vr);
|
||||||
|
motors_ml.value = vl * 100;
|
||||||
|
motors_mr.value = vr * 100;
|
||||||
|
motors_ws.send(motors_buf);
|
||||||
|
} else if (!motors_ws || motors_ws.readyState != WebSocket.CONNECTING) {
|
||||||
|
motors_ws = new WebSocket('ws://' + document.location.host + '/motors.ws');
|
||||||
|
motors_ws.addEventListener("open", () => send_motors(vl, vr));
|
||||||
|
}*/
|
||||||
|
};
|
||||||
|
|
||||||
|
const update_motors = () => {
|
||||||
|
const vl = motors_ml.value / 100;
|
||||||
|
const vr = motors_mr.value / 100;
|
||||||
|
send_motors(vl, vr);
|
||||||
|
}
|
||||||
|
|
||||||
|
motors_ml.addEventListener('change', update_motors);
|
||||||
|
motors_mr.addEventListener('change', update_motors);
|
||||||
|
motors_stop.addEventListener('click', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
motors_ml.value = 0;
|
||||||
|
motors_mr.value = 0;
|
||||||
|
update_motors();
|
||||||
|
});
|
||||||
|
|
||||||
|
const btnsz = 40;
|
||||||
|
let positionX = 0, positionY = 0;
|
||||||
|
let clicked = false;
|
||||||
|
let lastsend = performance.now();
|
||||||
|
const joystick_draw = () => {
|
||||||
|
const ctx = motors_joystick.getContext("2d");
|
||||||
|
const w = motors_joystick.width;
|
||||||
|
const h = motors_joystick.height;
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
|
ctx.fillStyle = "#00aaff40";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(w/2, h/2, w/2-btnsz, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeStyle = "#0099ee";
|
||||||
|
ctx.fillStyle = "#00aaff";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(w/2 + positionX, h/2 + positionY, btnsz, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
const updatePosition = e => {
|
||||||
|
const clientX = e.clientX || e.touches[0].clientX;
|
||||||
|
const clientY = e.clientY || e.touches[0].clientY;
|
||||||
|
const r = motors_joystick.getBoundingClientRect();
|
||||||
|
const kw = r.width/2 - btnsz;
|
||||||
|
let vx = Math.max(-1, Math.min((clientX - r.left - r.width/2) / kw, 1));
|
||||||
|
let vy = Math.max(-1, Math.min((clientY - r.top - r.height/2) / kw, 1));
|
||||||
|
if (vx*vx + vy*vy > 1) {
|
||||||
|
const a = Math.atan2(vy, vx);
|
||||||
|
vx = Math.cos(a);
|
||||||
|
vy = Math.sin(a);
|
||||||
|
}
|
||||||
|
positionX = vx * kw;
|
||||||
|
positionY = vy * kw;
|
||||||
|
vy *= -1;
|
||||||
|
if (performance.now() - lastsend > 250) {
|
||||||
|
send_motors(vy+vx, vy-vx);
|
||||||
|
lastsend = performance.now();
|
||||||
|
}
|
||||||
|
joystick_draw();
|
||||||
|
}
|
||||||
|
const ondown = e => {
|
||||||
|
clicked = true;
|
||||||
|
motors_joystick.style.cursor = 'none'
|
||||||
|
updatePosition(e);
|
||||||
|
};
|
||||||
|
const onmove = e => {
|
||||||
|
if (clicked) {
|
||||||
|
updatePosition(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onup = () => {
|
||||||
|
if (clicked) {
|
||||||
|
send_motors(0, 0);
|
||||||
|
motors_joystick.style.cursor = ''
|
||||||
|
clicked = false;
|
||||||
|
positionX = positionY = 0;
|
||||||
|
joystick_draw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
joystick_draw();
|
||||||
|
motors_joystick.addEventListener('mousedown', ondown);
|
||||||
|
motors_joystick.addEventListener('mousemove', onmove);
|
||||||
|
motors_joystick.addEventListener('mouseup', onup);
|
||||||
|
motors_joystick.addEventListener('mouseleave', onup);
|
||||||
|
motors_joystick.addEventListener('touchstart', ondown);
|
||||||
|
motors_joystick.addEventListener('touchmove', onmove);
|
||||||
|
motors_joystick.addEventListener('touchend', onup);
|
||||||
|
motors_joystick.addEventListener('touchcancel', onup);
|
||||||
|
|
||||||
|
const text_color = getComputedStyle(motors_canvas).color;
|
||||||
|
document.body.addEventListener('h:infos:motors', e => {
|
||||||
|
const ctx = motors_canvas.getContext("2d");
|
||||||
|
const w = motors_canvas.width;
|
||||||
|
const h = motors_canvas.height;
|
||||||
|
const m = 5;
|
||||||
|
const ml = e.detail[0];
|
||||||
|
const mr = e.detail[1];
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.strokeStyle = "#808080"
|
||||||
|
ctx.fillStyle = "#008000"
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(m, m, w-2*m, (h/2-m));
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(m, (h/2-m)+m, w-2*m, (h/2-m));
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
if (ml > 0)
|
||||||
|
ctx.rect(w/2, m+1, ml*(w/2-m), (h/2-m)-2);
|
||||||
|
else
|
||||||
|
ctx.rect(w/2+ml*(w/2-m), m+1, -ml*(w/2-m), (h/2-m)-2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
if (ml > 0)
|
||||||
|
ctx.rect(w/2, (h/2-m)+m+1, mr*(w/2-m), (h/2-m)-2);
|
||||||
|
else
|
||||||
|
ctx.rect(w/2+mr*(w/2-m), (h/2-m)+m+1, -mr*(w/2-m), (h/2-m)-2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener('h:section:statechanged', e => {
|
||||||
|
infos_mask(INFOS_MOTORS, e.detail['on']);
|
||||||
|
});
|
||||||
|
infos_mask(INFOS_MOTORS, true);
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
//position
|
||||||
|
|
||||||
|
const section2 = document.getElementById('section_position');
|
||||||
|
section2.insertAdjacentHTML('beforeend', `
|
||||||
|
<p>
|
||||||
|
<input type="range" min="190" max="1000" value="1000" id="map_scale" /><br/>
|
||||||
|
<input type="button" value="RàZ position" id="position_reset" /><br/>
|
||||||
|
<canvas id="map" width="800" height="800"></canvas>
|
||||||
|
</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const position_reset = document.getElementById('position_reset');
|
||||||
|
position_reset.addEventListener('click', async () => {
|
||||||
|
position_reset.disabled = true;
|
||||||
|
await fetch('/position/reset')
|
||||||
|
position_reset.disabled = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const map_canvas = document.getElementById('map');
|
||||||
|
const map_scale_range = document.getElementById('map_scale');
|
||||||
|
const pos_hist = [];
|
||||||
|
const text_color2 = getComputedStyle(map_canvas).color;
|
||||||
|
const map_draw = (x, y, a) => {
|
||||||
|
const ctx = map_canvas.getContext("2d");
|
||||||
|
const w = map_canvas.width;
|
||||||
|
const h = map_canvas.height;
|
||||||
|
const map_scale = Number(map_scale_range.value);
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
|
pos_hist.unshift([x, y]);
|
||||||
|
if (pos_hist.length > 10) pos_hist.pop();
|
||||||
|
|
||||||
|
ctx.font = '30px sans-serif';
|
||||||
|
ctx.fillStyle = text_color2;
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.fillText(Math.round(x*1000) + ' mm <X>', w-5, 30);
|
||||||
|
ctx.fillText(Math.round(y*1000) + ' mm <Y>', w-5, 60);
|
||||||
|
ctx.fillText(Math.round(a*180/Math.PI) + ' deg <A>', w-5, 90);
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(w/2, h/2);
|
||||||
|
ctx.scale(map_scale/1000, map_scale/1000);
|
||||||
|
ctx.lineWidth = Math.round(2*1000/map_scale);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let i = -10; i <= 11; i++) {
|
||||||
|
ctx.moveTo(-95+190*i, -95-2090);
|
||||||
|
ctx.lineTo(-95+190*i, -95+2090+190);
|
||||||
|
ctx.moveTo(-95-2090, -95+190*i);
|
||||||
|
ctx.lineTo(-95+2090+190, -95+190*i);
|
||||||
|
}
|
||||||
|
ctx.strokeStyle = 'rgba(180, 180, 180, 50%)';
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
const hl = pos_hist.length;
|
||||||
|
for (let i = 1; i < hl; i++) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(1000*pos_hist[i-1][0], -1000*pos_hist[i-1][1]);
|
||||||
|
ctx.lineTo(1000*pos_hist[i][0], -1000*pos_hist[i][1]);
|
||||||
|
const v = Math.round((hl-i)/(hl-1)*100);
|
||||||
|
ctx.strokeStyle = 'rgba(180, 200, 0, ' + v + '%)';
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.translate(x*1000, -y*1000);
|
||||||
|
ctx.rotate(-a);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(40, 0);
|
||||||
|
ctx.lineTo(35, 77/4);
|
||||||
|
ctx.lineTo(24, 77/2);
|
||||||
|
ctx.lineTo(-68, 77/2);
|
||||||
|
ctx.lineTo(-68, -77/2);
|
||||||
|
ctx.lineTo(24, -77/2);
|
||||||
|
ctx.lineTo(35, -77/4);
|
||||||
|
ctx.lineTo(40, 0);
|
||||||
|
ctx.strokeStyle = '#008000'
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.body.addEventListener('h:infos:position', e => {
|
||||||
|
map_draw(...e.detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener('h:section:statechanged', e => {
|
||||||
|
infos_mask(INFOS_POSITION, e.detail['on']);
|
||||||
|
});
|
||||||
|
infos_mask(INFOS_POSITION, true);
|
||||||
|
|
||||||
Vendored
+166
@@ -0,0 +1,166 @@
|
|||||||
|
import { infos_mask, INFOS_MOTORS } from "./infos.js";
|
||||||
|
|
||||||
|
const section = document.getElementById('section_motors');
|
||||||
|
section.insertAdjacentHTML('beforeend', `
|
||||||
|
<div class="grid">
|
||||||
|
<p><canvas id="motors_joystick" width="400" height="400"></canvas></p>
|
||||||
|
<p>
|
||||||
|
<input type="range" min="-100" max="100" value="0" id="motors_ml" /><br/>
|
||||||
|
<input type="range" min="-100" max="100" value="0" id="motors_mr" /><br/>
|
||||||
|
<input type="button" id="motors_stop" value="Stop" />
|
||||||
|
<canvas id="motors_canvas" width="350" height="40"></canvas>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const motors_buf = new Float32Array(2);
|
||||||
|
const motors_ml = document.getElementById("motors_ml")
|
||||||
|
const motors_mr = document.getElementById("motors_mr")
|
||||||
|
const motors_stop = document.getElementById("motors_stop")
|
||||||
|
const motors_joystick = document.getElementById('motors_joystick');
|
||||||
|
const motors_canvas = document.getElementById('motors_canvas');
|
||||||
|
|
||||||
|
let motors_ws = null;
|
||||||
|
|
||||||
|
const send_motors = (vl, vr) => {
|
||||||
|
if (motors_ws && motors_ws.readyState == WebSocket.OPEN) {
|
||||||
|
const lim = v => Math.max(-1, Math.min(v, 1));
|
||||||
|
motors_buf[0] = lim(vl);
|
||||||
|
motors_buf[1] = lim(vr);
|
||||||
|
motors_ml.value = vl * 100;
|
||||||
|
motors_mr.value = vr * 100;
|
||||||
|
motors_ws.send(motors_buf);
|
||||||
|
} else if (!motors_ws || motors_ws.readyState != WebSocket.CONNECTING) {
|
||||||
|
motors_ws = new WebSocket('ws://' + document.location.host + '/motors.ws');
|
||||||
|
motors_ws.addEventListener("open", () => send_motors(vl, vr));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const update_motors = () => {
|
||||||
|
const vl = motors_ml.value / 100;
|
||||||
|
const vr = motors_mr.value / 100;
|
||||||
|
send_motors(vl, vr);
|
||||||
|
}
|
||||||
|
|
||||||
|
motors_ml.addEventListener('change', update_motors);
|
||||||
|
motors_mr.addEventListener('change', update_motors);
|
||||||
|
motors_stop.addEventListener('click', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
motors_ml.value = 0;
|
||||||
|
motors_mr.value = 0;
|
||||||
|
update_motors();
|
||||||
|
});
|
||||||
|
|
||||||
|
const btnsz = 40;
|
||||||
|
let positionX = 0, positionY = 0;
|
||||||
|
let clicked = false;
|
||||||
|
let lastsend = performance.now();
|
||||||
|
const joystick_draw = () => {
|
||||||
|
const ctx = motors_joystick.getContext("2d");
|
||||||
|
const w = motors_joystick.width;
|
||||||
|
const h = motors_joystick.height;
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
|
ctx.fillStyle = "#00aaff40";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(w/2, h/2, w/2-btnsz, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeStyle = "#0099ee";
|
||||||
|
ctx.fillStyle = "#00aaff";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(w/2 + positionX, h/2 + positionY, btnsz, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
const updatePosition = e => {
|
||||||
|
const clientX = e.clientX || e.touches[0].clientX;
|
||||||
|
const clientY = e.clientY || e.touches[0].clientY;
|
||||||
|
const r = motors_joystick.getBoundingClientRect();
|
||||||
|
const kw = r.width/2 - btnsz;
|
||||||
|
let vx = Math.max(-1, Math.min((clientX - r.left - r.width/2) / kw, 1));
|
||||||
|
let vy = Math.max(-1, Math.min((clientY - r.top - r.height/2) / kw, 1));
|
||||||
|
if (vx*vx + vy*vy > 1) {
|
||||||
|
const a = Math.atan2(vy, vx);
|
||||||
|
vx = Math.cos(a);
|
||||||
|
vy = Math.sin(a);
|
||||||
|
}
|
||||||
|
positionX = vx * kw;
|
||||||
|
positionY = vy * kw;
|
||||||
|
vy *= -1;
|
||||||
|
if (performance.now() - lastsend > 250) {
|
||||||
|
send_motors(vy+vx, vy-vx);
|
||||||
|
lastsend = performance.now();
|
||||||
|
}
|
||||||
|
joystick_draw();
|
||||||
|
}
|
||||||
|
const ondown = e => {
|
||||||
|
clicked = true;
|
||||||
|
motors_joystick.style.cursor = 'none'
|
||||||
|
updatePosition(e);
|
||||||
|
};
|
||||||
|
const onmove = e => {
|
||||||
|
if (clicked) {
|
||||||
|
updatePosition(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onup = () => {
|
||||||
|
if (clicked) {
|
||||||
|
send_motors(0, 0);
|
||||||
|
motors_joystick.style.cursor = ''
|
||||||
|
clicked = false;
|
||||||
|
positionX = positionY = 0;
|
||||||
|
joystick_draw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
joystick_draw();
|
||||||
|
motors_joystick.addEventListener('mousedown', ondown);
|
||||||
|
motors_joystick.addEventListener('mousemove', onmove);
|
||||||
|
motors_joystick.addEventListener('mouseup', onup);
|
||||||
|
motors_joystick.addEventListener('mouseleave', onup);
|
||||||
|
motors_joystick.addEventListener('touchstart', ondown);
|
||||||
|
motors_joystick.addEventListener('touchmove', onmove);
|
||||||
|
motors_joystick.addEventListener('touchend', onup);
|
||||||
|
motors_joystick.addEventListener('touchcancel', onup);
|
||||||
|
|
||||||
|
const text_color = getComputedStyle(motors_canvas).color;
|
||||||
|
document.body.addEventListener('h:infos:motors', e => {
|
||||||
|
const ctx = motors_canvas.getContext("2d");
|
||||||
|
const w = motors_canvas.width;
|
||||||
|
const h = motors_canvas.height;
|
||||||
|
const m = 5;
|
||||||
|
const ml = e.detail[0];
|
||||||
|
const mr = e.detail[1];
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.strokeStyle = "#808080"
|
||||||
|
ctx.fillStyle = "#008000"
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(m, m, w-2*m, (h/2-m));
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(m, (h/2-m)+m, w-2*m, (h/2-m));
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
if (ml > 0)
|
||||||
|
ctx.rect(w/2, m+1, ml*(w/2-m), (h/2-m)-2);
|
||||||
|
else
|
||||||
|
ctx.rect(w/2+ml*(w/2-m), m+1, -ml*(w/2-m), (h/2-m)-2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
if (ml > 0)
|
||||||
|
ctx.rect(w/2, (h/2-m)+m+1, mr*(w/2-m), (h/2-m)-2);
|
||||||
|
else
|
||||||
|
ctx.rect(w/2+mr*(w/2-m), (h/2-m)+m+1, -mr*(w/2-m), (h/2-m)-2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener('h:section:statechanged', e => {
|
||||||
|
infos_mask(INFOS_MOTORS, e.detail['on']);
|
||||||
|
});
|
||||||
|
infos_mask(INFOS_MOTORS, true);
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
import { infos_mask, INFOS_POSITION } from "./infos.js";
|
||||||
|
|
||||||
|
const section = document.getElementById('section_position');
|
||||||
|
section.insertAdjacentHTML('beforeend', `
|
||||||
|
<p>
|
||||||
|
<input type="range" min="190" max="1000" value="1000" id="map_scale" /><br/>
|
||||||
|
<input type="button" value="RàZ position" id="position_reset" /><br/>
|
||||||
|
<canvas id="map" width="800" height="800"></canvas>
|
||||||
|
</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const position_reset = document.getElementById('position_reset');
|
||||||
|
position_reset.addEventListener('click', async () => {
|
||||||
|
position_reset.disabled = true;
|
||||||
|
await fetch('/position/reset')
|
||||||
|
position_reset.disabled = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const map_canvas = document.getElementById('map');
|
||||||
|
const map_scale_range = document.getElementById('map_scale');
|
||||||
|
const pos_hist = [];
|
||||||
|
const text_color = getComputedStyle(map_canvas).color;
|
||||||
|
const map_draw = (x, y, a) => {
|
||||||
|
const ctx = map_canvas.getContext("2d");
|
||||||
|
const w = map_canvas.width;
|
||||||
|
const h = map_canvas.height;
|
||||||
|
const map_scale = Number(map_scale_range.value);
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
|
pos_hist.unshift([x, y]);
|
||||||
|
if (pos_hist.length > 10) pos_hist.pop();
|
||||||
|
|
||||||
|
ctx.font = '30px sans-serif';
|
||||||
|
ctx.fillStyle = text_color;
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.fillText(Math.round(x*1000) + ' mm <X>', w-5, 30);
|
||||||
|
ctx.fillText(Math.round(y*1000) + ' mm <Y>', w-5, 60);
|
||||||
|
ctx.fillText(Math.round(a*180/Math.PI) + ' deg <A>', w-5, 90);
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(w/2, h/2);
|
||||||
|
ctx.scale(map_scale/1000, map_scale/1000);
|
||||||
|
ctx.lineWidth = Math.round(2*1000/map_scale);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let i = -10; i <= 11; i++) {
|
||||||
|
ctx.moveTo(-95+190*i, -95-2090);
|
||||||
|
ctx.lineTo(-95+190*i, -95+2090+190);
|
||||||
|
ctx.moveTo(-95-2090, -95+190*i);
|
||||||
|
ctx.lineTo(-95+2090+190, -95+190*i);
|
||||||
|
}
|
||||||
|
ctx.strokeStyle = 'rgba(180, 180, 180, 50%)';
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
const hl = pos_hist.length;
|
||||||
|
for (let i = 1; i < hl; i++) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(1000*pos_hist[i-1][0], -1000*pos_hist[i-1][1]);
|
||||||
|
ctx.lineTo(1000*pos_hist[i][0], -1000*pos_hist[i][1]);
|
||||||
|
const v = Math.round((hl-i)/(hl-1)*100);
|
||||||
|
ctx.strokeStyle = 'rgba(180, 200, 0, ' + v + '%)';
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.translate(x*1000, -y*1000);
|
||||||
|
ctx.rotate(-a);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(40, 0);
|
||||||
|
ctx.lineTo(35, 77/4);
|
||||||
|
ctx.lineTo(24, 77/2);
|
||||||
|
ctx.lineTo(-68, 77/2);
|
||||||
|
ctx.lineTo(-68, -77/2);
|
||||||
|
ctx.lineTo(24, -77/2);
|
||||||
|
ctx.lineTo(35, -77/4);
|
||||||
|
ctx.lineTo(40, 0);
|
||||||
|
ctx.strokeStyle = '#008000'
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.body.addEventListener('h:infos:position', e => {
|
||||||
|
map_draw(...e.detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener('h:section:statechanged', e => {
|
||||||
|
infos_mask(INFOS_POSITION, e.detail['on']);
|
||||||
|
});
|
||||||
|
infos_mask(INFOS_POSITION, true);
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 169 KiB |
+42
-21
@@ -6,30 +6,51 @@
|
|||||||
<link rel="stylesheet" href="./bootstrap-5.3.3-dist/css/bootstrap.css">
|
<link rel="stylesheet" href="./bootstrap-5.3.3-dist/css/bootstrap.css">
|
||||||
<title>MinHAUMTaure</title>
|
<title>MinHAUMTaure</title>
|
||||||
</header>
|
</header>
|
||||||
<body>
|
<body background="./images/labyrinth-1080p-wallpaper-1920x1080.jpg">
|
||||||
<h1 class="text-center">Bienvenue sur MinHAUMTaure</h1>
|
|
||||||
|
|
||||||
<h2> Fonctionnalités</h2>
|
<div class="container-fluid text-white">
|
||||||
|
<div class="row mb-5 mt-5">
|
||||||
|
<div class="col">
|
||||||
|
<h1 class="text-center">Bienvenue sur MinHAUMTaure</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-around">
|
||||||
|
<div class="col-5 mt-5">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<h2 class="text-center">Fonctionnalités</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row flex-nowrap align-items-center bg-dark bg-opacity-75 rounded-5">
|
||||||
|
<div class="col ">
|
||||||
|
<h2 class="text-center pt-4">Directions</h2>
|
||||||
|
<canvas id="motors_joystick"width="400"height="400">
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<h2 >Changement de couleur LED</h2>
|
||||||
|
<p>Sélection de la couleur</p>
|
||||||
|
<input type="color" id="colorLed">
|
||||||
|
|
||||||
|
<h2>Automatique</h2>
|
||||||
|
<div class="form-check-inline form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDefault">
|
||||||
|
<label class="form-check-label" for="flexSwitchCheckDefault">Mode Auto</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br/>
|
<br>
|
||||||
<br/>
|
<br>
|
||||||
|
|
||||||
<p>Sélection de la couleur</p>
|
<button type="button" class="btn btn-lg btn-primary btn-danger px-xxl-5 py-4">STOP</button>
|
||||||
<input type="color" id="colorLed">
|
</div>
|
||||||
|
</div>
|
||||||
<br/>
|
</div>
|
||||||
<br/>
|
<div class="col-5 mt-5">
|
||||||
|
<div class="row mb-3">
|
||||||
<p>Directions</p>
|
<h2 class="text-center">Repère Spatial</h2>
|
||||||
<canvas id="motors_joystick" width="400" height="400"></canvas>
|
</div>
|
||||||
|
<div class="row flex-nowrap align-items-center bg-dark bg-opacity-75 rounded-5">
|
||||||
<br/>
|
|
||||||
<br/>
|
</div>
|
||||||
|
</div>
|
||||||
<p>Automatique</p>
|
</div>
|
||||||
<div class="form-check-inline form-switch ">
|
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDefault">
|
|
||||||
<label class="form-check-label" for="flexSwitchCheckDefault">Mode Auto</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user