See site in english Voir le site en francais
Website skin:
home  download  forum  link  contact

Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

Author Topic: ECAM XR  (Read 852 times)

0 Members and 1 Guest are viewing this topic.

Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
17 October 2019, 18:26:38
Salut!

Un retour de plus après une longue pause. J'avais essayé, à l'époque, de construire un cockpit de XR à partir d'une Arduino. J'avais suivi les travaux de Mars Bleu et puis un peu perdu dans le codage lua, j'avais mis le projet de côte faute de temps. À l'époque, j'avais imaginé qu'avoir une sorte d'ECAM (Electronic centralised aircraft monitor) comme sur Airbus ou Boeing aurait été nécessaire pour afficher toutes les données du vaisseau sans avoir à passer par multitude d'afficheur 7 segments ou LED. En Gros, un ECAM, est un écran qui permet d'afficher, sur plusieurs pages, tous les systèmes de l'avions, ou du vaisseau dans mon cas.



En me basant sur Orb:connect, j'ai réussi à créer un petit bout d'ECAM pour XR2/XR5.
C'est un screenshot de ma table 10". Je pense en utiliser au moins 2.


Cette première page regroupera, les paramètres moteurs, AoA, Pitch, Bank, Slide, Acc XYZ, System status display et le Master Warning System.

C'est un long processus, car il y a peu de docs sur orbconnect et peu d'exemple sur le net. J'ai, tout de même, réussi à extraire tous les paramètres moteurs et je pense pouvoir extraire pas mal d'autres paramètres mais certains sont plus compliqués que d'autres. La par exemple je galère à récupérer l'AoA, Pitch, Slide et Bank. Le code nécessaire pour cela semble être différent que pour les moteurs. Le code n'est pas ma spécialité :D

Au final, je voudrais avoir différentes Tabs qui regrouperont les différents systèmes XR. Moteurs, fuel, oxygène, doors, .... Il y aura aussi une tab regroupant les différents boutons (APU, Prograde,....)

Beaucoup de challenge, surtout pour essayer d'écrire un code pas trop lourd.

hysot

Offline antoo

  • Legend
  • ******
  • Posts: 3614
  • Country: France fr
  • Karma: 165
  • 2, breaking left!
    • Forum de mon Asso d'Echasses Urbaines
Reply #1 - 17 October 2019, 18:55:28
Ça a l'ai bien sympa tout ça :) !

---------------------------------------------------------------------------------------------------
"ET C´EST PARTI!!" Youri Gagarine au lancement de vostok 1 le 12 avril 1961
Ma chaîne Youtube : Airsoft, FPS, Simulation : http://www.youtube.com/channel/UCrzIPMeULZU6lR4M6DVsH2g

Offline Gingin

  • Sr. Member
  • ****
  • Posts: 367
  • Country: France fr
  • Karma: 70
Reply #2 - 17 October 2019, 19:41:43
Super Hysot.
Ca va etre hyper complet

Visez les étoiles , au pire vous tomberez sur la Lune.

Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
Reply #3 - 19 October 2019, 00:51:38
J'ai finalement réussi à récupérer l'AoA :). Le pitch, slipe et bank ne devrais pas être un problème. Une étape de plus de surmontée.
Les températures de carlingue vont être un autre challenge :/

hysot

Offline antoo

  • Legend
  • ******
  • Posts: 3614
  • Country: France fr
  • Karma: 165
  • 2, breaking left!
    • Forum de mon Asso d'Echasses Urbaines
Reply #4 - 19 October 2019, 15:07:11
Cool ! Je suis curieux de savoir comment tu récupéreras ces infos... ça m'intéresse  :beer: !

---------------------------------------------------------------------------------------------------
"ET C´EST PARTI!!" Youri Gagarine au lancement de vostok 1 le 12 avril 1961
Ma chaîne Youtube : Airsoft, FPS, Simulation : http://www.youtube.com/channel/UCrzIPMeULZU6lR4M6DVsH2g

Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
Reply #5 - 22 October 2019, 00:03:51
Cool ! Je suis curieux de savoir comment tu récupéreras ces infos... ça m'intéresse  :beer: !

Bien sûr:)
Mon code est assez basique. Je voudrais le simplifier pour éviter de réécrire chaque ligne de code pour chaque moteur par exemple. Pour écrire le code ci-dessous, je me suis inspire du fichier "index.html" et "example.html" fournis avec orbconnect 3.19.
Je te copie directement une version simplifiée et te mets une copie du fichier complet à la fin.

Voilà la première partie du code (voir un peu plus bas). Elle regroupe principalement la mise en page CSS. Rien de bien compliqué et rien de scellé dans le marbre. Je réfléchis encore sur quoi je viendrais afficher l'ECAM (plusieurs tablettes 10"? un écran large tactile?). La solution finale dictera comment le tout se présentera. Le point important à noter ici est comment sont appelées les différentes valeurs récupérées depuis Orbiter par le script javascript:
Code: [Select]
<span id="FlowRate0"></span>Ici j'appelle la valeur du FlowRate du Moteur principale numero 1. Je reviendrais sur l'agencement des differents moteurs.

Le code

Code: [Select]
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">.
<title>Example</title>


<script type="text/javascript" src="jquery-1.10.2.js"></script>
<script type="text/javascript" src="orbiterclient.js"></script>
<script type="text/javascript" src="gauge.min.radial.js"></script>
<script type="text/javascript" src="gauge.min.linear.js"></script>
<style>
body {
padding: 20px;
margin: 0;
background: #000;
color: #64a0c8;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
  table-layout: fixed;
  font-family: sans-serif;
  font-weight: bold;
  color: #64a0c8;
  font-size: 20px;
  border-bottom: 3px solid #64a0c8;
}
.table {
  position: absolute;
  right: 200px ;
  top: 20px
}

td:nth-child(1) {
  text-align: right;
  color: #04ff00;
}

td:nth-child(2) {
  text-align: center;
  font-size: 15px;
}

td:nth-child(3) {
  text-align: left;
  color: #04ff00;
}

.vl {
  border-left: 3px solid #64a0c8;
  height: 1100px;
  position: absolute;
  top: 20px;
  left: 420px;
}


</style>
</head>

<body>


<!--Main engines status-->
<table>
<tr>
<th colspan="3">MAIN</th>
</tr>
<tr>
<td colspan="3"><canvas id="gauge-eng0"></canvas><canvas id="gauge-eng1"></canvas></td>
<tr>
<tr>
<td><span id="Thrust0"></span></td>
<td>THRUST kN</td>
<td><span id="Thrust1"></span></td>
</tr>
<tr>
<td><span id="TSFC0"></span></td>
<td>TSFC kg/kNs</td>
<td><span id="TSFC1"></span></td>
</tr>
<tr>
<td><span id="FlowRate0"></span></td>
<td>FLOW kg/s</td>
<td><span id="FlowRate1"></span></td>
</tr>
<tr>
<td><span id="FuelLevelQ0"></span></td>
<td>QTY kg</td>
<td><span id="FuelLevelQ1"></span></td>
</tr>
</table>

Ensuite, le javascript (voir tout en bas). C'est là que la magie opère :D.

Je n'ai mis que la partie pour le moteur principale #1 et l'angle d'attaque. Pour le moment, tout n'est qu'une réptition des mêmes bloques pour chaque moteur (MAIN, SCRAM, HOVER). Le premier bloque "DATA DEFINITION" commence par la souscription au message d'Orbiter "XCTL:FOCUS:Engine:0"

D'où ça vient ce bignou. Si tu vas fouiller dans C:/Orbiter2010/Doc/OrbConnect/, tu y trouveras le fichier "OrbConnectUserGuide-2.1.doc" qui regroupe tous les messages requis. Lis le document ça aidera un peu à comprendre la suite.

Dans ce document on retrouve les détails suivant (p19)

Quote
XCTL:<"FOCUS", vessel name or index>:Engine
   Reference API method      GetEngineState
   Additional Arguments      engineId (int)
   Return Type                        engineId (int),
                                                throttle level (double),
                                                pitch gimbal position (double),
                                                yaw gimbal position (double),
                                                balance position (double),
                                                pitch centering mode engaged (bool),
                                                yaw centering mode engaged (bool),
                                                balance centering mode engaged (bool),
                                                auto mode engaged (bool),
                                                divergent mode engaged (bool),
                                                tsfc (double),
                                                flow rate (double),
                                                thrust (double),
                                                fuel level (double),
                                                max fuel level (double),
                                                diffuser temp (double),
                                                burner temp (double),
                                                exhaust temp (double)

Tu peux voir que la première ligne présente la structure du message à utiliser. La seule différence est que je ne focus pas sur un vaisseau en particulier, mais juste sur celui en cours. Il y a aussi le "0" après "engine" qui est là à cause du "Additional Arguments engineId (int)". C'est logique, on a plusieurs moteurs donc on veut savoir lequel dont on parle. Le "function(data)" est là pour extraire les données. Ici les données prélevées sont une succession de chiffres qui définissent la valeur de chacun des paramètres moteurs. Ces paramètres sont listés dans le .doc comme "return type".

"var chars = data.split(',') " je ne suis pas un spécialiste mais de ce que j'ai compris, c'est que l'on vient séparer chacuns des paramètres en variable pour pouvoir les appeler individuellement.

Le Data, brut, se présente comme suit:
Code: [Select]
0,0,0,0,0,0,0,0,0,0,0.0385173,0,0,0.615,230880,-1,-1,-1,0
Chaque virgule définis une nouvelle variable. le "split(',')" fait la separation au niveau des virgules. Au final on aura quelques choses comme ça:

0chars[0]engineId (int)
0chars[1]throttle level (double)
0chars[2]pitch gimbal position (double)
0chars[3]yaw gimbal position (double)
0chars[4]balance position (double)
0chars[5]pitch centering mode engaged (bool)
0chars[6]yaw centering mode engaged (bool)
0chars[7]balance centering mode engaged (bool)
0chars[8]auto mode engaged (bool)
0chars[9]divergent mode engaged (bool)
0.0385173chars[10]tsfc (double)
0chars[11]flow rate (double)
0chars[12]thrust (double)
0.615chars[13]fuel lever (double)
230880chars[14]max fuel level (double)
-1chars[15]diffuseur temp (double)
-1chars[16]burner temp (double)
-1chars[17]exhaut temp (double)

À partir de là on crée une variable pour chaque paramètre que l'on veut et la façon dont on veut les utiliser. Dans mon cas je voulais afficher en texte le flow rate, le TSFC, le Thrust, la quanity de carburant (diviser par 2) et incorporer la valeur du Throttle dans une gauge.

J'ai aussi corrigé une bonne partie des "chars" pour qu'ils s'affichent sous la bonne forme (ex: x100 ou /1000).

$("#Flowrate0") est le terme utilise dans le CSS pour appeler la valeur extraite (cf plus haut)
"toFixed()" est pour limiter le nombre de décimal après la virgule.

Le second groupe "GAUGES DEFINITIONS" vient créer la gauge moteur.
Pour les gauges, j'utilise ca https://canvas-gauges.com/download/.
Tu peux avoir des gauges linéaires ou radiales. Ils suffit juste d'installer le gauge.js dans le meme dossier que le fichier index.html de orbconnect.
Le fichier ALL-IN-ONE ne marchait pas pour moi, du coup j'ai télécharge ONLY-RADIAL-ONE et ONLY-LINEAR-ONE et renommé les fichiers. (gauge.min.linear.js et gauge.min.radial.js)
Tu integre quelques lignes de code au début du fichier index.html.
Code: [Select]
<script type="text/javascript" src="gauge.min.radial.js"></script>
<script type="text/javascript" src="gauge.min.linear.js"></script>

Enfin tu construis la gauge a l'aide de tous pleins de parametre (voir l'aide du site internet).
Fais attention a ne pas oublier de mettre "gaugeEng0.value = Throttle0" dans le DATA DEFINITION. C'est ce qui lie les deux. "Eng0" et "Throttle0" sont en fonction de comment tu nommes tes variables.

Regarde les deux bloques suivant de l'Aoa. C'est similaire. je n'ai extrait que l'aoa dans mon cas (pour le moment) mais le message "attitude" contient aussi pitch, bank et slide. Ca fonctionne exactement de la meme facon. Il faut juste faire des essaies pour voir sous quelle forme ils affichent. Pour l'aoa, l'angle est en radian. J'ai du le convertir en degree. Rien de bien complique. Je soupconne que pour les 3 autres soient en radian aussi.

J'espere que ca t'aidera. Je mets en lien, mon dossier orbconnect. Tu as juste besoin de le copier dans ton dossier Orbiter, activer le module et acceder a l'adresse http://127.0.0.1:38888/.

https://drive.google.com/file/d/12hivFa4Uvdqr9SIu-W0UfC0AfLxx6FA9/view?usp=sharing

Code: [Select]
<script>


var oc = new OrbiterClient();
oc.start(200); // Update interval (ms)



//DATA DEFINITION

//Main engine 1
oc.subscribe("XCTL:FOCUS:Engine:0", function(data) {
var chars = data.split(',');
var Throttle0 = chars[1]*100;
var TSFC0 = chars[10]*1000;
var FlowRate0 = chars[11];
var Thrust0 = chars[12]/1000;
var FuelLevel0 = chars[13]*100;
var FuelLevelQ0 = chars[14]*chars[13];
$("#FlowRate0").text(Throttle0);
$("#Thrust0").text(Thrust0.toFixed(1));
$("#TSFC0").text(TSFC0.toFixed(3));
$("#FuelLevelQ0").text(FuelLevelQ0.toFixed(0));
gaugeEng0.value = Throttle0;

});

//GAUGES DEFINITION

//Main engine 1 gauge
var gaugeEng0 = new LinearGauge({
renderTo: 'gauge-eng0',
width: 200,
height: 200,
minValue: 0,
startAngle: 90,
ticksAngle: 180,
valueBox: false,
maxValue: 100,
majorTicks: [
"0",
"",
"20",
"",
"40",
"",
"60",
"",
"80",
"",
"100"
],
minorTicks: 2,
strokeTicks: true,
highlights: [
{
"from": 100,
"to": 110,
"color": "rgba(200, 50, 50, .75)"
}
],
colorPlate: "#000",
barBeginCircle: 0,
borderShadowWidth: 0,
borderMiddleWidth: 30,
borders: false,
needleType: "arrow",
needleWidth: 10,
needleCircleSize: 7,
needleCircleOuter: true,
needleCircleInner: true,
tickSide: "left",
needleSide: "left",
numberSide: "left",
colorBarProgress: "#64a0c8",
colorBar: "000",
colorNumbers: "#64a0c8",
colorMajorTicks: "#64a0c8",
colorMinorTicks: "#64a0c8",
borders: 0,
valueInt: 1,
valueDec: 0,
animationDuration: 1500,
animationRule: "dequint",
barWidth: 10,
animatedValue: true
});
gaugeEng0.draw();

//Attitude

oc.subscribe("SHIP:FOCUS:Attitude", function(data) {
var att = data;
var chars = att.split(',');
var pi = Math.PI;
var aoa = chars[0]*(180/pi);
$("#AoA").text(aoa.toFixed(1));
});

//Angle of Attack gauge
var gaugeAoA = new RadialGauge({
renderTo: 'gauge-AoA',
width: 100,
height: 100,
minValue: 0,
startAngle: 90,
ticksAngle: 180,
valueBox: false,
maxValue: 100,
majorTicks: [
"0",
"",
"20",
"",
"40",
"",
"60",
"",
"80",
"",
"100"
],
minorTicks: 2,
strokeTicks: true,
highlights: [
{
"from": 100,
"to": 110,
"color": "rgba(200, 50, 50, .75)"
}
],
colorPlate: "#000",
barBeginCircle: 0,
borderShadowWidth: 0,
borderMiddleWidth: 30,
borders: false,
needleType: "arrow",
needleWidth: 10,
needleCircleSize: 7,
needleCircleOuter: true,
needleCircleInner: true,
tickSide: "right",
needleSide: "right",
numberSide: "right",
colorBarProgress: "#64a0c8",
colorBar: "000",
colorNumbers: "#64a0c8",
colorMajorTicks: "#64a0c8",
colorMinorTicks: "#64a0c8",
borders: 0,
valueInt: 1,
valueDec: 0,
animationDuration: 1500,
animationRule: "dequint",
barWidth: 10,
animatedValue: true
});
gaugeEng3.draw();





</script>


</body>
</html>


hysot

Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
Reply #6 - 22 October 2019, 01:35:50
Au fait, il est conçu pour Orbiter 2010 p1.
J'ai essayé sur Orbiter 2016 et ça ne marche pas. Enfin il me semble que les fonctions de base oui mais pas celle propre au XR. Je ne me suis pas encore penche sur le problème.
L'autre problème est que rien n'a vraiment été fait pour extraire les températures de carlingue. Le seul truc que j'ai trouvé se base sur XRVesselCtrlDemo 3.1 qui ne fonctionne pas sur Orbiter 2010. Je suis déçu.

« Last Edit: 23 October 2019, 07:07:44 by hysot »
hysot

Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
Reply #7 - 23 October 2019, 19:53:42
Voila la derniere mise a jour. Je prevois de l'uliser sur un ecran tactile de 15"/16"
J'ai quelques petits bugs a resoudre, notament au niveau des gauges. Mon screenshot en est un tres bon exemple. Les gauges affiches 39% alors que les moteurs sont arretes. Il y a des sauts aleatoires.


hysot

Offline antoo

  • Legend
  • ******
  • Posts: 3614
  • Country: France fr
  • Karma: 165
  • 2, breaking left!
    • Forum de mon Asso d'Echasses Urbaines
Reply #8 - 23 October 2019, 20:08:35
Trop cool !

---------------------------------------------------------------------------------------------------
"ET C´EST PARTI!!" Youri Gagarine au lancement de vostok 1 le 12 avril 1961
Ma chaîne Youtube : Airsoft, FPS, Simulation : http://www.youtube.com/channel/UCrzIPMeULZU6lR4M6DVsH2g

Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
Reply #9 - 23 October 2019, 20:10:13
Trop cool !
As tu vu les details de la programmation que j'ai poste plus haut? j'ai eu peur que ca se perde dans mes derniers messages.

hysot

Offline antoo

  • Legend
  • ******
  • Posts: 3614
  • Country: France fr
  • Karma: 165
  • 2, breaking left!
    • Forum de mon Asso d'Echasses Urbaines
Reply #10 - 23 October 2019, 22:15:40
Trop cool !
As tu vu les details de la programmation que j'ai poste plus haut? j'ai eu peur que ca se perde dans mes derniers messages.

Ah oui ! Merci beaucoup !  :beer:

---------------------------------------------------------------------------------------------------
"ET C´EST PARTI!!" Youri Gagarine au lancement de vostok 1 le 12 avril 1961
Ma chaîne Youtube : Airsoft, FPS, Simulation : http://www.youtube.com/channel/UCrzIPMeULZU6lR4M6DVsH2g

Offline Milouse

  • Legend
  • ******
  • Posts: 1401
  • Country: France fr
  • Karma: 125
Reply #11 - 24 October 2019, 16:37:08
Bonjour,

[...] L'autre problème est que rien n'a vraiment été fait pour extraire les températures de carlingue. Le seul truc que j'ai trouvé se base sur XRVesselCtrlDemo 3.1 qui ne fonctionne pas sur Orbiter 2010. Je suis déçu.

En fouillant dans l'Internet Archive, on peut y trouver une ancienne v3.0 qui d'après le Readme fonctionne avec Orbiter 2010.
https://web.archive.org/web/20160704233715/http://www.alteaaerospace.com/downloads/releases/XRVesselCtrlDemo-3.0.zip

Bon courage pour ce projet ! :top:


Milouse


Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
Reply #12 - 25 October 2019, 02:28:15
Bonjour,

[...] L'autre problème est que rien n'a vraiment été fait pour extraire les températures de carlingue. Le seul truc que j'ai trouvé se base sur XRVesselCtrlDemo 3.1 qui ne fonctionne pas sur Orbiter 2010. Je suis déçu.

En fouillant dans l'Internet Archive, on peut y trouver une ancienne v3.0 qui d'après le Readme fonctionne avec Orbiter 2010.
https://web.archive.org/web/20160704233715/http://www.alteaaerospace.com/downloads/releases/XRVesselCtrlDemo-3.0.zip

Bon courage pour ce projet ! :top:


Milouse

Merci Milouse! Me reste plus qu'a trouver comment ca marche  :wonder:

J'ai reussi a combiner chaque bloques de code moteur en un seul. Ca simplifie pas mal l'ensemble du script :).

Code: [Select]
//Engines Definition-------------------
var engId = new Array(8);
var throttle = new Array(8);
var pitchGimb = new Array(8);
var yawGimb = new Array(8);
var balancePos = new Array(8);
var pitchCtrMd = new Array(8);
var yawCtrMd = new Array(8);
var balanceCtrMd = new Array(8);
var autoMd = new Array(8);
var divergentMd = new Array(8);
var tsfc = new Array(8);
var flowRate = new Array(8);
var thrust = new Array(8);
var fuelLvl = new Array(8);
var maxFuelLvl = new Array(8);
var fuelLvlKg = new Array(8)
var diffTemp = new Array(8);
var burnerTemp = new Array(8);
var exhaustTemp = new Array(8);

var gaugeEng = new Array(8);

oc.subscribe("XCTL:FOCUS:Engines", function(data) {
var chars = data.split(',');
for (i=0; i<8; i++) {
engId[i] = chars[0+(i*18)];
throttle[i] = chars[1+(i*18)]*100;
pitchGimb[i] = chars[2+(i*18)];
yawGimb[i] = chars[3+(i*18)];
balancePos[i] = chars[4+(i*18)];
pitchCtrMd[i] = chars[5+(i*18)];
yawCtrMd[i] = chars[6+(i*18)];
balanceCtrMd[i] = chars[7+(i*18)];
autoMd[i] = chars[8+(i*18)];
divergentMd[i] = chars[9+(i*18)];
tsfc[i] = chars[10+(i*18)]*1;
flowRate[i] = chars[11+(i*18)]*1;
thrust[i] = chars[12+(i*18)]/1000;
fuelLvl[i] = chars[13+(i*18)];
maxFuelLvl[i] = chars[14+(i*18)];
fuelLvlKg[i] = fuelLvl[i]*maxFuelLvl[i];
diffTemp[i] = chars[15+(i*18)]/10;
burnerTemp[i] = chars[16+(i*18)]/10;
exhaustTemp[i] = chars[17+(i*18)]/10;
$("#Throttle"+i).text(throttle[i].toFixed(0));
$("#FlowRate"+i).text(flowRate[i].toFixed(0));
$("#Thrust"+i).text(thrust[i].toFixed(1));
$("#TSFC"+i).text(tsfc[i].toFixed(3));
$("#FuelLevelQ"+i).text(fuelLvlKg[i].toFixed(0));
$("#DiffTemp"+i).text(diffTemp[i].toFixed(1));
$("#BurnTemp"+i).text(burnerTemp[i].toFixed(1));
$("#ExhTemp"+i).text(exhaustTemp[i].toFixed(1));
}
gaugeEng0.value = throttle[0];
gaugeEng1.value = throttle[1];
gaugeEng2.value = throttle[2];
gaugeEng3.value = throttle[3];
gaugeEng4.value = throttle[4];
gaugeEng5.value = throttle[5];
});

hysot

Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
Reply #13 - 16 November 2019, 18:24:07
I've been quite busy lately at work but since few days I've been able to developp a bit more my ECAM.
I divided the screen in 2. The left side for all the systems with different tabs. The right side for all the A/P commands. They are always accessible whatever system page display on the left.




hysot

Offline hysot

  • Full Member
  • ***
  • Posts: 120
  • Country: Canada ca
  • Karma: 8
Reply #14 - 19 November 2019, 23:13:14
A bit more work. I've done most work on the Door page.
Green is opened
Yellow is either closing or openning
Grey is closed


hysot