Skip to content
Closed
1 change: 1 addition & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
# Killing ndscpp is a bit rude and technically unnecessary, but we've been
# raised to clean up after ourselves, so that's what we do.
run: |
make config.led
./ndscpp > /dev/null 2>&1 &
sleep 1
LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib ./tests/tests
Expand Down
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ DEPFLAGS=-MT $@ -MMD -MP -MF $(DEPDIR)/$*.d

SOURCES=main.cpp
EXECUTABLE=ndscpp
CONFIG_FILES=config.led secrets.h
DEPDIR=.deps
OBJECTS:=$(SOURCES:.cpp=.o)
DEPFILES:=$(SOURCES:%.cpp=$(DEPDIR)/%.d)

# Runtime config files (don't trigger a rebuild)
RUNTIME_CONFIG=config.led
# Compile-time header dependencies
COMPILE_CONFIG=secrets.h

define helptext =
Makefile for ndscpp

Expand Down Expand Up @@ -42,11 +46,11 @@ $(EXECUTABLE): $(OBJECTS)
@echo Linking $@...
@$(CC) $(LDFLAGS) $^ -o $@ $(LIBS)

%.o: %.cpp $(CONFIG_FILES) $(DEPDIR)/%.d | $(DEPDIR)
%.o: %.cpp $(COMPILE_CONFIG) $(DEPDIR)/%.d | $(DEPDIR)
@echo Compiling $<...
@$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@

$(CONFIG_FILES): % : %.example
$(RUNTIME_CONFIG) $(COMPILE_CONFIG): % : %.example
@if [ ! -f $@ ]; then \
echo "Copying $< to $@..."; \
cp $< $@; \
Expand Down
6 changes: 5 additions & 1 deletion effectsmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,16 @@ class EffectsManager : public IEffectsManager
}

// Add an effect to the manager
void AddEffect(shared_ptr<ILEDEffect> effect) override
void AddEffect(shared_ptr<ILEDEffect> effect, shared_ptr<ISchedule> schedule = nullptr) override
{
lock_guard lock(_effectsMutex);

if (!effect)
throw invalid_argument("Cannot add a null effect.");

if (schedule)
effect->SetSchedule(schedule);

_effects.push_back(effect);

// Automatically set the first effect as current if none is selected
Expand Down
2 changes: 1 addition & 1 deletion interfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class IEffectsManager
public:
virtual ~IEffectsManager() = default;

virtual void AddEffect(shared_ptr<ILEDEffect> effect) = 0;
virtual void AddEffect(shared_ptr<ILEDEffect> effect, shared_ptr<ISchedule> schedule = nullptr) = 0;
virtual void RemoveEffect(shared_ptr<ILEDEffect> & effect) = 0;
virtual void StartCurrentEffect(ICanvas& canvas) = 0;
virtual void SetCurrentEffect(size_t index, ICanvas& canvas) = 0;
Expand Down
29 changes: 23 additions & 6 deletions pixeltypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1365,11 +1365,28 @@ inline void to_json(nlohmann::json& j, const CRGB& color)
};
}

inline void from_json(const nlohmann::json& j, CRGB& color)
inline void from_json(const nlohmann::json& j, CRGB& color)
{
color = CRGB(
j.at("r").get<uint8_t>(),
j.at("g").get<uint8_t>(),
j.at("b").get<uint8_t>()
);
if (j.is_string()) {
string s = j.get<string>();
if (s.empty()) {
color = CRGB::Black;
return;
}
if (s[0] == '#') s = s.substr(1);
uint32_t val = static_cast<uint32_t>(stoul(s, nullptr, 16));
color = CRGB(val);
} else if (j.is_array()) {
color = CRGB(
j.at(0).get<uint8_t>(),
j.at(1).get<uint8_t>(),
j.at(2).get<uint8_t>()
);
} else {
color = CRGB(
j.value("r", j.value("red", uint8_t(0))),
j.value("g", j.value("green", uint8_t(0))),
j.value("b", j.value("blue", uint8_t(0)))
);
}
}
41 changes: 28 additions & 13 deletions webserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ using namespace std;

class WebServer
{
mutable shared_mutex _apiMutex;
mutable recursive_mutex _apiMutex;
string _controllerFileName;

struct HeaderMiddleware
Expand All @@ -27,7 +27,9 @@ class WebServer

void after_handle(crow::request &req, crow::response &res, context &ctx)
{
res.set_header("Content-Type", "application/json");
if (req.url.starts_with("/api/"))
res.set_header("Content-Type", "application/json");

res.add_header("Access-Control-Allow-Origin", "*");
res.add_header("Access-Control-Allow-Methods", "GET, OPTIONS, POST, DELETE");
res.add_header("Access-Control-Allow-Headers", "Content-Type");
Expand Down Expand Up @@ -86,7 +88,7 @@ class WebServer
{
try
{
shared_lock readLock(_apiMutex);
unique_lock readLock(_apiMutex);
return nlohmann::json{{"controller", _controller}}.dump();
}
catch(const exception& e)
Expand All @@ -103,7 +105,7 @@ class WebServer
{
try
{
shared_lock readLock(_apiMutex);
unique_lock readLock(_apiMutex);
return nlohmann::json{{"sockets", _controller.GetSockets()}}.dump();
}
catch(const exception& e)
Expand All @@ -121,7 +123,7 @@ class WebServer
{
try
{
shared_lock readLock(_apiMutex);
unique_lock readLock(_apiMutex);
return nlohmann::json{{"socket", _controller.GetSocketById(socketId)}}.dump();
}
catch(const exception& e)
Expand All @@ -138,7 +140,7 @@ class WebServer
{
try
{
shared_lock readLock(_apiMutex);
unique_lock readLock(_apiMutex);
return nlohmann::json(_controller.Canvases()).dump();
}
catch(const exception& e)
Expand All @@ -155,7 +157,7 @@ class WebServer
{
try
{
shared_lock readLock(_apiMutex);
unique_lock readLock(_apiMutex);
return nlohmann::json(*_controller.GetCanvasById(id)).dump();
}
catch(const exception& e)
Expand Down Expand Up @@ -213,7 +215,6 @@ class WebServer
return {crow::BAD_REQUEST, "Error, likely canvas with that ID already exists."};

PersistController(req);
writeLock.unlock();

auto& effectsManager = canvas->Effects();
// Start the effects manager of the new canvas if it wants to run and has effects
Expand Down Expand Up @@ -241,7 +242,6 @@ class WebServer
unique_lock writeLock(_apiMutex);
auto newId = _controller.GetCanvasById(canvasId)->AddFeature(feature);
PersistController(req);
writeLock.unlock();

return nlohmann::json{{"id", newId}}.dump();

Expand All @@ -264,7 +264,6 @@ class WebServer
auto canvas = _controller.GetCanvasById(canvasId);
canvas->RemoveFeatureById(featureId);
PersistController(req);
writeLock.unlock();

return crow::response(crow::OK);
}
Expand All @@ -285,7 +284,6 @@ class WebServer
unique_lock writeLock(_apiMutex);
_controller.DeleteCanvasById(id);
PersistController(req);
writeLock.unlock();

return crow::response(crow::OK);
}
Expand All @@ -305,17 +303,20 @@ class WebServer
auto reqJson = nlohmann::json::parse(req.body);
auto effect = reqJson.get<shared_ptr<ILEDEffect>>();

shared_ptr<ISchedule> schedule = nullptr;
if (reqJson.contains("schedule"))
schedule = reqJson["schedule"].get<shared_ptr<ISchedule>>();

unique_lock writeLock(_apiMutex);
auto canvas = _controller.GetCanvasById(canvasId);

// Get current effects
auto& effectsManager = canvas->Effects();

// Add the new effect
effectsManager.AddEffect(effect);
effectsManager.AddEffect(effect, schedule);

PersistController(req);
writeLock.unlock();

return crow::response(crow::OK);
}
Expand All @@ -326,6 +327,20 @@ class WebServer
}
});

CROW_ROUTE(_crowApp, "/")
([](const crow::request&, crow::response& res)
{
res.set_static_file_info("static/index.html");
res.end();
});

CROW_ROUTE(_crowApp, "/<path>")
([](const crow::request&, crow::response& res, string path)
{
res.set_static_file_info("static/" + path);
res.end();
});

// Start the server
_crowApp.port(_controller.GetPort()).multithreaded().run();
}
Expand Down
Loading