めのん@ひとりプログラミング同好会

このブログはすべてフィクションであり、実在の人物、団体とは一切関係ありません

OpenSiv3Dで作ったLED器具とsACNの送受信を結合

こんにちは、めのんです!

前回はOpenSiv3Dで作ったLED器具のエミュレーション部分のお話でした。 今回は前々回までに作ったsACNのパケットを実際にUDPで送受信する部分と結合しました。

まずは受信側です。

#include <Siv3D.hpp> // OpenSiv3D v0.4.3
#include <boost/asio.hpp>
#include <thread>
#include <vector>
#include <cstring>
#include <csignal>
#include "sACN.h"

volatile std::sig_atomic_t available = 0;
char buffer[sizeof(sACN_DataPacket)];

void 受信スレッドMain()
{
    using namespace boost::asio::ip;
    for (;;)
    {
        try
        {
            boost::asio::io_service io_service;
            udp::socket socket(io_service, udp::endpoint(udp::v4(), 5568));
            socket.set_option(multicast::join_group(address::from_string("239.255.0.1").to_v4()));

            udp::endpoint remote_endpoint;
            boost::system::error_code error;
            size_t len = socket.receive_from(boost::asio::buffer(buffer), remote_endpoint, 0, error);
            available = 1;
        }
        catch (std::exception& e)
        {
            std::cerr << e.what() << std::endl;
        }
    }
}

void Main()
{
    const auto 幅 = Scene::Width();
    const auto 高さ = Scene::Height();
    constexpr int 行数 = 10;
    constexpr int 列数 = 17;
    const auto 器具ピッチ = 幅 / 列数;
    const int 器具直径 = 器具ピッチ - 2 * 2;
    const auto 器具半径 = 器具直径 / 2.0;
    const auto 上端位置 = (高さ % (器具ピッチ * 行数)) / 2.0;
    const auto 左端位置 = (幅 % (器具ピッチ * 列数)) / 2.0;
    sACN_DataPacket packet{};

    std::thread 受信スレッド(受信スレッドMain);

    while (System::Update())
    {
        if (available)
        {
            std::memcpy(&packet, buffer, sizeof(packet));
            available = 0;
        }

        for (int row = 0; row < 行数; row++)
        {
            for (int column = 0; column < 列数; column++)
            {
                const auto x = 左端位置 + 器具ピッチ * column + 器具ピッチ / 2.0;
                const auto y = 上端位置 + 器具ピッチ * row + 器具ピッチ / 2.0;
                int channel = (row * 列数 + column) * 3;
                int r = packet.DMP_Layer.PropertyValues[1 + channel + 0];
                int g = packet.DMP_Layer.PropertyValues[1 + channel + 1];
                int b = packet.DMP_Layer.PropertyValues[1 + channel + 2];
                Circle(x, y, 器具半径).draw(Color(r, g, b));
            }
        }
    }
}

次は送信側です。

#include <iostream>
#include <boost/asio.hpp>
#include <cmath>
#include "sACN.h"

int main()
{
  using namespace boost::asio::ip;

    try
    {
        boost::asio::io_service io_service;
        udp::resolver resolver(io_service);
        udp::resolver::query query(udp::v4(), "239.255.0.1", "5568");
        udp::endpoint receiver_endpoint = *resolver.resolve(query);
        union
        {
            sACN_DataPacket packet;
            char buffer[sizeof(sACN_DataPacket)];
        };
        unsigned char data[512]{};

        udp::socket socket(io_service);
        socket.open(udp::v4());
        socket.set_option(multicast::join_group(address::from_string("239.255.0.1").to_v4()));

        for (unsigned frame = 0; ; frame++)
        {
            constexpr int 器具台数 = 170;
            for (int i = 0; i < 器具台数; i++)
            {
                int channel = i * 3;
                int intensity = static_cast<int>(std::sin(frame * 0.1) * 255);
                data[channel + 0] = data[channel + 1] = data[channel + 2] = intensity;
            }
            sACN_make_DataPacket(&packet, 1, 1, data, sizeof(data));
            socket.send_to(boost::asio::buffer(buffer), receiver_endpoint);
            Sleep(100);
        }
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
}

せっかくなので、つまずいたところも記録しておきますね。

まず、前々回作ったsACN_make_DataPacket関数でリンクエラーが出て困りました。 間違いなくsACN.cをリンクしているのにどうしてだろうと思っていたのですが、Cの関数をC++で使うときはextern "C"で囲まないといけないんですね。

次に、Boost.Asioでマルチキャストの使い方がわからずにちょっと苦戦しました。

今回はパケットのチェックとかも全然やっていない手抜きですし、送信側も時間の管理が手抜きなので歪に変化します。 このあたりは次回以降の課題だと思います。

とりあえずいったんキリのいいところまでいきましたので、今週末のLチカはここまでにしたいと思います。

それでは!!