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チカはここまでにしたいと思います。
それでは!!