fix(ragb): hotfix buttons

This commit is contained in:
sinavir 2024-10-15 16:54:11 +02:00
parent 700d967ff3
commit ddfbbe50e3
No known key found for this signature in database
13 changed files with 277 additions and 18 deletions

View file

@ -49,7 +49,7 @@ pub async fn batch_edit_value_handler(
lock.dmx.colors[i.address] = i.value; lock.dmx.colors[i.address] = i.value;
match db.static_state.change_channel.send(DMXAtom::Color(*i)) { match db.static_state.change_channel.send(DMXAtom::Color(*i)) {
Ok(_) => (), Ok(_) => (),
Err(_) => (), Err(e) => tracing::error!("Channel error: {e:?}"),
} }
} }
lock.ratelimit_info.insert(user, Instant::now()); lock.ratelimit_info.insert(user, Instant::now());
@ -63,8 +63,12 @@ pub async fn get_value_handler(
) -> Result<impl IntoResponse, StatusCode> { ) -> Result<impl IntoResponse, StatusCode> {
let lock = db.mut_state.read().await; let lock = db.mut_state.read().await;
check_id(id, &lock.dmx.colors)?; check_id(id, &lock.dmx.colors)?;
if lock.dmx.motor.black_button {
Ok(Json(lock.dmx.motor.bump_color))
} else {
Ok(Json(lock.dmx.colors[id])) Ok(Json(lock.dmx.colors[id]))
} }
}
#[debug_handler] #[debug_handler]
pub async fn edit_value_handler( pub async fn edit_value_handler(
@ -72,6 +76,7 @@ pub async fn edit_value_handler(
State(db): State<DB>, State(db): State<DB>,
Json(body): Json<DMXColor>, Json(body): Json<DMXColor>,
) -> Result<(), StatusCode> { ) -> Result<(), StatusCode> {
tracing::trace!("Edit color {body:?}");
let mut lock = db.mut_state.write().await; let mut lock = db.mut_state.write().await;
check_id(id, &lock.dmx.colors)?; check_id(id, &lock.dmx.colors)?;
lock.dmx.colors[id] = body; lock.dmx.colors[id] = body;
@ -81,7 +86,7 @@ pub async fn edit_value_handler(
.send(DMXAtom::Color(DMXColorAtom::new(id, body))) .send(DMXAtom::Color(DMXColorAtom::new(id, body)))
{ {
Ok(_) => (), Ok(_) => (),
Err(_) => (), Err(e) => tracing::error!("Channel error: {e:?}"),
}; };
Ok(()) Ok(())
@ -101,15 +106,45 @@ pub async fn edit_motor_value_handler(
Json(body): Json<DMXBeamChange>, Json(body): Json<DMXBeamChange>,
) -> Result<(), StatusCode> { ) -> Result<(), StatusCode> {
let mut lock = db.mut_state.write().await; let mut lock = db.mut_state.write().await;
tracing::trace!("Edit motor {body:?}");
if let Some(new) = body.black_button {
if new != lock.dmx.motor.black_button {
for (i, x) in lock
.dmx
.colors
.clone()
.into_iter()
.enumerate()
{
match db
.static_state
.change_channel
.send(DMXAtom::Color(DMXColorAtom {
address: i,
value: if new { lock.dmx.motor.bump_color } else { x },
})) {
Ok(_) => (),
Err(e) => tracing::error!("Channel error: {e:?}"),
}
};
}
}
lock.dmx.motor = DMXBeam { lock.dmx.motor = DMXBeam {
pan: body.pan.unwrap_or(lock.dmx.motor.pan), pan: body.pan.unwrap_or(lock.dmx.motor.pan),
tilt: body.tilt.unwrap_or(lock.dmx.motor.tilt), tilt: body.tilt.unwrap_or(lock.dmx.motor.tilt),
focus: body.focus.unwrap_or(lock.dmx.motor.focus), focus: body.focus.unwrap_or(lock.dmx.motor.focus),
white_button: body.white_button.unwrap_or(lock.dmx.motor.white_button),
black_button: body.black_button.unwrap_or(lock.dmx.motor.black_button),
bump_color: body.bump_color.unwrap_or(lock.dmx.motor.bump_color),
}; };
let _ = db match db
.static_state .static_state
.change_channel .change_channel
.send(DMXAtom::Motor(lock.dmx.motor)); .send(DMXAtom::Motor(lock.dmx.motor))
{
Ok(_) => (),
Err(e) => tracing::error!("Channel error: {e:?}"),
}
Ok(()) Ok(())
} }
@ -127,10 +162,14 @@ pub async fn sse_handler(State(db): State<DB>) -> impl IntoResponse {
let init_data = data let init_data = data
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(i, x)| { .map(move |(i, x)| {
Ok(DMXAtom::Color(DMXColorAtom { Ok(DMXAtom::Color(DMXColorAtom {
address: i, address: i,
value: x, value: if motor.black_button {
motor.bump_color
} else {
x
},
})) }))
}) })
.chain(std::iter::once(Ok(DMXAtom::Motor(motor)))); .chain(std::iter::once(Ok(DMXAtom::Motor(motor))));

View file

@ -22,6 +22,9 @@ pub struct DMXBeam {
pub pan: u8, pub pan: u8,
pub tilt: u8, pub tilt: u8,
pub focus: u8, pub focus: u8,
pub white_button: u8,
pub black_button: bool,
pub bump_color: DMXColor,
} }
#[derive(Debug, Default, Deserialize, Serialize, Copy, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Copy, Clone)]
@ -29,6 +32,9 @@ pub struct DMXBeamChange {
pub pan: Option<u8>, pub pan: Option<u8>,
pub tilt: Option<u8>, pub tilt: Option<u8>,
pub focus: Option<u8>, pub focus: Option<u8>,
pub white_button: Option<u8>,
pub black_button: Option<bool>,
pub bump_color: Option<DMXColor>,
} }
pub type ColorArray = Vec<DMXColor>; pub type ColorArray = Vec<DMXColor>;
@ -114,7 +120,7 @@ pub type DB = Arc<SharedState>;
pub fn make_db() -> DB { pub fn make_db() -> DB {
let state_size: usize = let state_size: usize =
usize::from_str(&dotenvy::var("NB_DMX_VALUES").unwrap_or(String::from("100"))).unwrap(); usize::from_str(&dotenvy::var("NB_DMX_VALUES").unwrap_or(String::from("130"))).unwrap();
let save_path = dotenvy::var("BK_FILE").unwrap_or(String::from("data.json")); let save_path = dotenvy::var("BK_FILE").unwrap_or(String::from("data.json"));
let mut_state = RwLock::new(AppState::new(state_size, &save_path)); let mut_state = RwLock::new(AppState::new(state_size, &save_path));
Arc::new(SharedState { Arc::new(SharedState {

View file

@ -0,0 +1,20 @@
from django.core.exceptions import PermissionDenied
class GroupRequiredMixin(object):
"""
group_required - list of strings, required param
"""
group_required = None
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
raise PermissionDenied
else:
user_groups = []
for group in request.user.groups.values_list("name", flat=True):
user_groups.append(group)
if len(set(user_groups).intersection(self.group_required)) <= 0:
raise PermissionDenied
return super(GroupRequiredMixin, self).dispatch(request, *args, **kwargs)

View file

@ -21,13 +21,23 @@ let startSocket = () => {
socket.addEventListener("message", (event) => { socket.addEventListener("message", (event) => {
const message = JSON.parse(event.data); const message = JSON.parse(event.data);
const color = rgbToHex(message.value); let color;
console.log(color, `light${message.address}`); let address;
const element = document.getElementById(`light${message.address}`); if(message.type === "Motor") {
console.log(message);
color = rgbToHex(message.bump_color);
address = 300;
} else {
color = rgbToHex(message.value);
address = message.address;
}
const element = document.getElementById(`light${address}`);
const inputElement = document.getElementById(`input-light${address}`);
console.log(color, `light${address}`);
if(element !== null) { if(element !== null) {
element.style.fill = color; element.style.fill = color;
} }
const inputElement = document.getElementById(`input-light${message.address}`);
if(inputElement !== null) { if(inputElement !== null) {
inputElement.value = color; inputElement.value = color;
} }
@ -113,4 +123,36 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
}); });
}); });
// Add a click event to save colors
(document.querySelectorAll('.save-bump-color') || []).forEach(($trigger) => {
const lightId = $trigger.dataset.target;
const $input = document.getElementById(`input-light${lightId}`);
const url = `${WEBSOCKET_ENDPOINT}/control-box`;
$trigger.addEventListener('click', async (e) => {
e.preventDefault();
const color = hexToRgb($input.value);
openModal($loadingModal)
await fetch(url, {
method: 'POST',
//mode: "no-cors",
headers: {
'Accept': "application/json",
'Content-Type': "application/json",
'Authorization': `Bearer ${JWT}`
},
body: JSON.stringify({ "bump_color": {"red": color.r, "green": color.g, "blue": color.b}}),
}).then((resp) => {
if(!resp.ok) {
alert(`Request failed. Err code:${resp.status}`);
}
}).catch((e) => {
alert(`Request failed. Err: ${e}`);
console.log(e);
}).finally(() => {
closeAllModals();
});
});
});
}); });

View file

@ -1,4 +1,5 @@
{% load static %} {% load static %}
{% load has_group %}
<!doctype html> <!doctype html>
<html data-theme="light" lang="en"> <html data-theme="light" lang="en">
<head> <head>
@ -27,6 +28,11 @@
<li class="hide-before-sm"> <li class="hide-before-sm">
<a class="contrast" href="/api-docs/">API</a> <a class="contrast" href="/api-docs/">API</a>
</li> </li>
{% if user|has_group:"cof" %}
<li class="hide-before-sm">
<a class="contrast" href="{% url "frontend:bump" %}">Cof only</a>
</li>
{% endif %}
{% if user.is_staff %} {% if user.is_staff %}
<li class="hide-before-sm"> <li class="hide-before-sm">
<a class="contrast" href="{% url "admin:index" %}">Admin</a> <a class="contrast" href="{% url "admin:index" %}">Admin</a>

View file

@ -0,0 +1,17 @@
{% extends "frontend/base.html" %}
{% load static %}
{% block content %}
<section>
<h1>Couleur du bump</h1>
<form>
<input id="input-light300" class="input" type="color" name="light300">
<button class="save-bump-color" data-target="300">Enregistrer</button>
</form>
</section>
<dialog open id="modal-loading">
<article aria-busy="true"></article>
</dialog>
<script>const WEBSOCKET_ENDPOINT = "{{ websocket_endpoint }}";</script>
<script>const JWT = "{{ jwt }}";</script>
<script src="{% static "js/update-color.js" %}" defer></script>
{% endblock %}

View file

@ -1877,7 +1877,7 @@
xlink:href="/light/blinder"> xlink:href="/light/blinder">
<g <g
id="g5280" id="g5280"
transform="matrix(0.22165795,0,0,0.22165795,16.4358,15.843397)"> transform="matrix(0.22165795,0,0,0.22165795,46.4358,25.843397)">
<rect <rect
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.909091;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none" style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.909091;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect374" id="rect374"
@ -1984,6 +1984,50 @@
r="4.5454545" /> r="4.5454545" />
</g> </g>
</a> </a>
<a
id="a6346"
xlink:href="/light/spot_pont_0"
style="fill-opacity:1">
<circle
style="fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="light{{lights.spot_pont_0.channels.0}}"
cx="25.063156"
cy="30.8321233"
r="2.4541624" />
</a>
<a
id="a6346"
xlink:href="/light/spot_pont_3"
style="fill-opacity:1">
<circle
style="fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="light{{lights.spot_pont_3.channels.0}}"
cx="80.063156"
cy="30.8321233"
r="2.4541624" />
</a>
<a
id="a6346"
xlink:href="/light/spot_pont_1"
style="fill-opacity:1">
<circle
style="fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="light{{lights.spot_pont_1.channels.0}}"
cx="43.063156"
cy="32.8321233"
r="2.4541624" />
</a>
<a
id="a6346"
xlink:href="/light/spot_pont_2"
style="fill-opacity:1">
<circle
style="fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="light{{lights.spot_pont_2.channels.0}}"
cx="61.063156"
cy="32.8321233"
r="2.4541624" />
</a>
<a <a
id="a6346" id="a6346"
xlink:href="/light/screen_right" xlink:href="/light/screen_right"
@ -2037,6 +2081,28 @@
y="3.4841893" y="3.4841893"
transform="rotate(16.718917)" /> transform="rotate(16.718917)" />
</a> </a>
<a
id="a6260"
xlink:href="/light/barre_led_4">
<rect
style="fill:url(#linearGradient6214);fill-opacity:1;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
width="13.756657"
height="1.6411031"
x="62.915016"
y="28.4841893"
transform="rotate(0)" />
</a>
<a
id="a6260"
xlink:href="/light/barre_led_5">
<rect
style="fill:url(#linearGradient6135);fill-opacity:1;stroke:#000000;stroke-width:0.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
width="13.756657"
height="1.6411031"
x="27.915016"
y="28.4841893"
transform="rotate(0)" />
</a>
<a <a
id="a6340" id="a6340"
xlink:href="/light/barre_led_1"> xlink:href="/light/barre_led_1">

View file

@ -0,0 +1,8 @@
from django import template
register = template.Library()
@register.filter(name="has_group")
def has_group(user, group_name):
return user.groups.filter(name=group_name).exists()

View file

@ -1,10 +1,11 @@
from django.urls import path from django.urls import path
from .views import HomeView, LightView, TokenView from .views import BumpView, HomeView, LightView, TokenView
app_name = "frontend" app_name = "frontend"
urlpatterns = [ urlpatterns = [
path("", HomeView.as_view(), name="home"), path("", HomeView.as_view(), name="home"),
path("light/<str:light>/", LightView.as_view()), path("light/<str:light>/", LightView.as_view()),
path("bump", BumpView.as_view(), name="bump"),
path("token", TokenView.as_view()), path("token", TokenView.as_view()),
] ]

View file

@ -5,6 +5,7 @@ from django.http import Http404, JsonResponse
from django.views import View from django.views import View
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from .mixins import GroupRequiredMixin
from .utils import craft_token from .utils import craft_token
@ -39,10 +40,15 @@ def get_context_from_proj(kind, chans):
class TokenView(LoginRequiredMixin, View): class TokenView(LoginRequiredMixin, View):
def get(self, request, *arg, **kwargs): def get(self, request, *arg, **kwargs):
return JsonResponse(craft_token(self.request.user.username, self.request.user.groups.filter(name="cof").exists())) return JsonResponse(
craft_token(
self.request.user.username,
self.request.user.groups.filter(name="cof").exists(),
)
)
class LightView(TemplateView): class LightView(LoginRequiredMixin, TemplateView):
def get_template_names(self): def get_template_names(self):
lights = settings.LIGHTS["lights"][self.kwargs["light"]] lights = settings.LIGHTS["lights"][self.kwargs["light"]]
return [f"frontend/{lights['kind']}.html"] return [f"frontend/{lights['kind']}.html"]
@ -50,7 +56,10 @@ class LightView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
if self.request.user.is_authenticated: if self.request.user.is_authenticated:
context["jwt"] = craft_token(self.request.user.username, self.request.user.groups.filter(name="cof").exists())["token"] context["jwt"] = craft_token(
self.request.user.username,
self.request.user.groups.filter(name="cof").exists(),
)["token"]
context["websocket_endpoint"] = settings.WEBSOCKET_ENDPOINT context["websocket_endpoint"] = settings.WEBSOCKET_ENDPOINT
light = self.kwargs["light"] light = self.kwargs["light"]
if light not in settings.LIGHTS["lights"]: if light not in settings.LIGHTS["lights"]:
@ -62,6 +71,21 @@ class LightView(TemplateView):
return context return context
class BumpView(GroupRequiredMixin, TemplateView):
template_name = "frontend/bump.html"
group_required = ["cof"]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.user.is_authenticated:
context["jwt"] = craft_token(
self.request.user.username,
self.request.user.groups.filter(name="cof").exists(),
)["token"]
context["websocket_endpoint"] = settings.WEBSOCKET_ENDPOINT
return context
class HomeView(TemplateView): class HomeView(TemplateView):
template_name = "frontend/home.html" template_name = "frontend/home.html"

View file

@ -44,6 +44,36 @@
"name": "Barre led 4", "name": "Barre led 4",
"kind": "led_tub", "kind": "led_tub",
"channels" : [68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83] "channels" : [68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83]
},
"barre_led_4": {
"name": "Barre led 5",
"kind": "led_tub",
"channels" : [84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
},
"barre_led_5": {
"name": "Barre led 6",
"kind": "led_tub",
"channels" : [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115]
},
"spot_pont_0": {
"name": "Ecran-gauche",
"kind": "spot",
"channels": [ 116 ]
},
"spot_pont_1": {
"name": "Ecran-gauche",
"kind": "spot",
"channels": [ 117 ]
},
"spot_pont_2": {
"name": "Ecran-gauche",
"kind": "spot",
"channels": [ 118 ]
},
"spot_pont_3": {
"name": "Ecran-gauche",
"kind": "spot",
"channels": [ 119 ]
} }
} }
} }

View file

@ -33,7 +33,7 @@ DEV_KEY = "supersecret"
WEBSOCKET_PORT = 9999 WEBSOCKET_PORT = 9999
WEBSOCKET_HOST = "127.0.0.1" WEBSOCKET_HOST = "127.0.0.1"
WEBSOCKET_ENDPOINT = f"http://{WEBSOCKET_HOST}:{WEBSOCKET_PORT}/api" WEBSOCKET_ENDPOINT = "https://agb.hackens.org/api"
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True