webthrottle: improved handling of world load/close and added auto reconnect
see #178
Dieser Commit ist enthalten in:
Ursprung
994cc4d99c
Commit
541a6f5fc9
@ -57,7 +57,7 @@ public:
|
|||||||
WebSocketConnection(Server& server, std::shared_ptr<boost::beast::websocket::stream<boost::beast::tcp_stream>> ws, std::string_view idPrefix);
|
WebSocketConnection(Server& server, std::shared_ptr<boost::beast::websocket::stream<boost::beast::tcp_stream>> ws, std::string_view idPrefix);
|
||||||
virtual ~WebSocketConnection();
|
virtual ~WebSocketConnection();
|
||||||
|
|
||||||
void start();
|
virtual void start();
|
||||||
|
|
||||||
virtual void disconnect();
|
virtual void disconnect();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -44,6 +44,13 @@ WebThrottleConnection::~WebThrottleConnection()
|
|||||||
{
|
{
|
||||||
assert(isEventLoopThread());
|
assert(isEventLoopThread());
|
||||||
|
|
||||||
|
// disconnect all signals:
|
||||||
|
m_traintasticPropertyChanged.disconnect();
|
||||||
|
m_trainPropertyChanged.clear();
|
||||||
|
m_throttleReleased.clear();
|
||||||
|
m_throttleDestroying.clear();
|
||||||
|
|
||||||
|
// destroy all throttles:
|
||||||
for(auto& it : m_throttles)
|
for(auto& it : m_throttles)
|
||||||
{
|
{
|
||||||
it.second->destroy();
|
it.second->destroy();
|
||||||
@ -51,6 +58,27 @@ WebThrottleConnection::~WebThrottleConnection()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebThrottleConnection::start()
|
||||||
|
{
|
||||||
|
WebSocketConnection::start();
|
||||||
|
|
||||||
|
EventLoop::call(
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
m_traintasticPropertyChanged = Traintastic::instance->propertyChanged.connect(
|
||||||
|
[this](BaseProperty& property)
|
||||||
|
{
|
||||||
|
if(property.name() == "world")
|
||||||
|
{
|
||||||
|
assert(m_throttles.empty());
|
||||||
|
sendWorld(static_cast<ObjectProperty<World>&>(property).value());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sendWorld(Traintastic::instance->world.value());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void WebThrottleConnection::doRead()
|
void WebThrottleConnection::doRead()
|
||||||
{
|
{
|
||||||
assert(isServerThread());
|
assert(isServerThread());
|
||||||
@ -294,6 +322,23 @@ void WebThrottleConnection::sendError(uint32_t throttleId, std::error_code ec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebThrottleConnection::sendWorld(const std::shared_ptr<World>& world)
|
||||||
|
{
|
||||||
|
assert(isEventLoopThread());
|
||||||
|
|
||||||
|
auto event = nlohmann::json::object();
|
||||||
|
event.emplace("event", "world");
|
||||||
|
if(world)
|
||||||
|
{
|
||||||
|
event.emplace("name", world->name.toJSON());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event.emplace("name", nullptr);
|
||||||
|
}
|
||||||
|
sendMessage(event);
|
||||||
|
}
|
||||||
|
|
||||||
const std::shared_ptr<WebThrottle>& WebThrottleConnection::getThrottle(uint32_t throttleId)
|
const std::shared_ptr<WebThrottle>& WebThrottleConnection::getThrottle(uint32_t throttleId)
|
||||||
{
|
{
|
||||||
assert(isEventLoopThread());
|
assert(isEventLoopThread());
|
||||||
@ -310,6 +355,13 @@ const std::shared_ptr<WebThrottle>& WebThrottleConnection::getThrottle(uint32_t
|
|||||||
auto [it, inserted] = m_throttles.emplace(throttleId, WebThrottle::create(*world));
|
auto [it, inserted] = m_throttles.emplace(throttleId, WebThrottle::create(*world));
|
||||||
if(inserted) /*[[likely]]*/
|
if(inserted) /*[[likely]]*/
|
||||||
{
|
{
|
||||||
|
m_throttleDestroying.emplace(throttleId, it->second->onDestroying.connect(
|
||||||
|
[this, throttleId](Object& /*object*/)
|
||||||
|
{
|
||||||
|
released(throttleId);
|
||||||
|
m_throttleDestroying.erase(throttleId);
|
||||||
|
m_throttles.erase(throttleId);
|
||||||
|
}));
|
||||||
m_throttleReleased.emplace(throttleId, it->second->released.connect(
|
m_throttleReleased.emplace(throttleId, it->second->released.connect(
|
||||||
[this, throttleId]()
|
[this, throttleId]()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -31,13 +31,16 @@
|
|||||||
#include "websocketconnection.hpp"
|
#include "websocketconnection.hpp"
|
||||||
|
|
||||||
class WebThrottle;
|
class WebThrottle;
|
||||||
|
class World;
|
||||||
|
|
||||||
class WebThrottleConnection : public WebSocketConnection
|
class WebThrottleConnection : public WebSocketConnection
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
boost::beast::flat_buffer m_readBuffer;
|
boost::beast::flat_buffer m_readBuffer;
|
||||||
std::queue<std::string> m_writeQueue;
|
std::queue<std::string> m_writeQueue;
|
||||||
|
boost::signals2::scoped_connection m_traintasticPropertyChanged;
|
||||||
std::map<uint32_t, std::shared_ptr<WebThrottle>> m_throttles;
|
std::map<uint32_t, std::shared_ptr<WebThrottle>> m_throttles;
|
||||||
|
std::map<uint32_t, boost::signals2::scoped_connection> m_throttleDestroying;
|
||||||
std::map<uint32_t, boost::signals2::scoped_connection> m_throttleReleased;
|
std::map<uint32_t, boost::signals2::scoped_connection> m_throttleReleased;
|
||||||
std::map<uint32_t, boost::signals2::scoped_connection> m_trainPropertyChanged;
|
std::map<uint32_t, boost::signals2::scoped_connection> m_trainPropertyChanged;
|
||||||
|
|
||||||
@ -48,6 +51,7 @@ protected:
|
|||||||
void sendMessage(const nlohmann::json& message);
|
void sendMessage(const nlohmann::json& message);
|
||||||
void sendError(uint32_t throttleId, std::string_view text, std::string_view tag = {});
|
void sendError(uint32_t throttleId, std::string_view text, std::string_view tag = {});
|
||||||
void sendError(uint32_t throttleId, std::error_code ec);
|
void sendError(uint32_t throttleId, std::error_code ec);
|
||||||
|
void sendWorld(const std::shared_ptr<World>& world);
|
||||||
|
|
||||||
const std::shared_ptr<WebThrottle>& getThrottle(uint32_t throttleId);
|
const std::shared_ptr<WebThrottle>& getThrottle(uint32_t throttleId);
|
||||||
|
|
||||||
@ -58,6 +62,8 @@ public:
|
|||||||
|
|
||||||
WebThrottleConnection(Server& server, std::shared_ptr<boost::beast::websocket::stream<boost::beast::tcp_stream>> ws);
|
WebThrottleConnection(Server& server, std::shared_ptr<boost::beast::websocket::stream<boost::beast::tcp_stream>> ws);
|
||||||
virtual ~WebThrottleConnection();
|
virtual ~WebThrottleConnection();
|
||||||
|
|
||||||
|
void start() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -92,7 +92,16 @@ inline static void deleteAll(T& objectList)
|
|||||||
objectList.front()->active = false;
|
objectList.front()->active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objectList.delete_(objectList.front());
|
if constexpr(std::is_same_v<T, ThrottleList>)
|
||||||
|
{
|
||||||
|
auto& throttle = objectList[0];
|
||||||
|
throttle->destroy();
|
||||||
|
objectList.removeObject(throttle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
objectList.delete_(objectList.front());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,6 +443,7 @@ World::~World()
|
|||||||
deleteAll(*inputs);
|
deleteAll(*inputs);
|
||||||
deleteAll(*identifications);
|
deleteAll(*identifications);
|
||||||
deleteAll(*boards);
|
deleteAll(*boards);
|
||||||
|
deleteAll(*throttles);
|
||||||
deleteAll(*trains);
|
deleteAll(*trains);
|
||||||
deleteAll(*railVehicles);
|
deleteAll(*railVehicles);
|
||||||
deleteAll(*luaScripts);
|
deleteAll(*luaScripts);
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
.hide
|
.hide
|
||||||
{
|
{
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stretch
|
.stretch
|
||||||
@ -76,6 +76,7 @@
|
|||||||
html
|
html
|
||||||
{
|
{
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
height: -webkit-fill-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
body
|
body
|
||||||
@ -109,11 +110,12 @@ header h1
|
|||||||
#body
|
#body
|
||||||
{
|
{
|
||||||
position: relative;
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#settings
|
#settings
|
||||||
{
|
{
|
||||||
z-index: 1;
|
z-index: 3;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: #121212;
|
background-color: #121212;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -126,6 +128,34 @@ header h1
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#not-connected
|
||||||
|
{
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#no-world
|
||||||
|
{
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#not-connected div,
|
||||||
|
#no-world div
|
||||||
|
{
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
button.control,
|
button.control,
|
||||||
select.control
|
select.control
|
||||||
{
|
{
|
||||||
|
|||||||
@ -301,16 +301,13 @@ var tm = new function ()
|
|||||||
localStorage.throttleName = document.getElementById('throttle_name').value;
|
localStorage.throttleName = document.getElementById('throttle_name').value;
|
||||||
localStorage.throttleStopOnRelease = document.getElementById('stop_train_on_release').value == 'on';
|
localStorage.throttleStopOnRelease = document.getElementById('stop_train_on_release').value == 'on';
|
||||||
document.getElementById('settings').classList.add('hide');
|
document.getElementById('settings').classList.add('hide');
|
||||||
if(tm.throttles.length == 0)
|
this.connect();
|
||||||
{
|
|
||||||
tm.add();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if(localStorage.throttleName && localStorage.throttleStopOnRelease)
|
if(localStorage.throttleName && localStorage.throttleStopOnRelease)
|
||||||
{
|
{
|
||||||
document.getElementById('settings').classList.add('hide');
|
document.getElementById('settings').classList.add('hide');
|
||||||
this.add();
|
this.connect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,31 +328,60 @@ var tm = new function ()
|
|||||||
this.ws = new WebSocket((window.location.protocol == 'https' ? 'wss' : 'ws') + '://' + window.location.host + window.location.pathname);
|
this.ws = new WebSocket((window.location.protocol == 'https' ? 'wss' : 'ws') + '://' + window.location.host + window.location.pathname);
|
||||||
this.ws.onopen = function (ev)
|
this.ws.onopen = function (ev)
|
||||||
{
|
{
|
||||||
tm.send({ 'action': 'get_train_list' });
|
console.log('onopen');
|
||||||
tm.throttles.forEach(function (throttle, _)
|
document.getElementById('not-connected').classList.add('hide');
|
||||||
{
|
|
||||||
tm.send({
|
|
||||||
'throttle_id': throttle.id,
|
|
||||||
'action': 'set_name',
|
|
||||||
'value': localStorage.throttleName + ' #' + throttle.id,
|
|
||||||
});
|
|
||||||
var trainId = localStorage['throttle' + throttle.id + 'TrainId'];
|
|
||||||
if(trainId)
|
|
||||||
{
|
|
||||||
tm.send({
|
|
||||||
'throttle_id': throttle.id,
|
|
||||||
'action': 'acquire',
|
|
||||||
'train_id': trainId,
|
|
||||||
'steal': false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
this.ws.onmessage = function (ev)
|
this.ws.onmessage = function (ev)
|
||||||
{
|
{
|
||||||
var msg = JSON.parse(ev.data);
|
var msg = JSON.parse(ev.data);
|
||||||
console.log('RX', msg);
|
console.log('RX', msg);
|
||||||
if(msg['event'] == 'train_list')
|
if(msg['event'] == 'world')
|
||||||
|
{
|
||||||
|
if(msg.name === null)
|
||||||
|
{
|
||||||
|
document.getElementById('throttles').replaceChildren();
|
||||||
|
document.getElementById('throttles').classList.add('hide');
|
||||||
|
document.getElementById('no-world').classList.remove('hide');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
document.getElementById('throttles').classList.remove('hide');
|
||||||
|
document.getElementById('no-world').classList.add('hide');
|
||||||
|
if(!document.getElementById('throttles').hasChildNodes())
|
||||||
|
{
|
||||||
|
tm.add();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tm.throttles.forEach(function (throttle, _)
|
||||||
|
{
|
||||||
|
throttle.setTrainList([]); // clear train list
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tm.send({ 'action': 'get_train_list' });
|
||||||
|
|
||||||
|
tm.throttles.forEach(function (throttle, _)
|
||||||
|
{
|
||||||
|
tm.send({
|
||||||
|
'throttle_id': throttle.id,
|
||||||
|
'action': 'set_name',
|
||||||
|
'value': localStorage.throttleName + ' #' + throttle.id,
|
||||||
|
});
|
||||||
|
const trainId = localStorage['throttle' + throttle.id + 'TrainId'];
|
||||||
|
if(trainId)
|
||||||
|
{
|
||||||
|
tm.send({
|
||||||
|
'throttle_id': throttle.id,
|
||||||
|
'action': 'acquire',
|
||||||
|
'train_id': trainId,
|
||||||
|
'steal': false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(msg['event'] == 'train_list')
|
||||||
{
|
{
|
||||||
tm.throttles.forEach(function (throttle, _)
|
tm.throttles.forEach(function (throttle, _)
|
||||||
{
|
{
|
||||||
@ -404,12 +430,17 @@ var tm = new function ()
|
|||||||
};
|
};
|
||||||
this.ws.onclose = function ()
|
this.ws.onclose = function ()
|
||||||
{
|
{
|
||||||
|
console.log('onclose');
|
||||||
|
document.getElementById('not-connected').classList.remove('hide');
|
||||||
|
document.getElementById('throttles').classList.add('hide');
|
||||||
|
document.getElementById('no-world').classList.add('hide');
|
||||||
|
tm.ws = null;
|
||||||
setTimeout(function () { tm.connect(); }, 1000);
|
setTimeout(function () { tm.connect(); }, 1000);
|
||||||
}
|
}
|
||||||
this.ws.onerror = function (err)
|
this.ws.onerror = function (err)
|
||||||
{
|
{
|
||||||
console.error('WebSocket error: ', err.message);
|
console.error('WebSocket error: ', err.message);
|
||||||
this.ws.close();
|
tm.ws.close();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,13 @@
|
|||||||
<button id="close_settings">Close</button>
|
<button id="close_settings">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="throttles" class="flex-row"></div>
|
<div id="not-connected">
|
||||||
|
<div>Not connected, trying to connect...</div>
|
||||||
|
</div>
|
||||||
|
<div id="no-world" class="hide">
|
||||||
|
<div>No world loaded.</div>
|
||||||
|
</div>
|
||||||
|
<div id="throttles" class="flex-row hide"></div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/js/throttle.js"></script>
|
<script src="/js/throttle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren