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);
|
||||
virtual ~WebSocketConnection();
|
||||
|
||||
void start();
|
||||
virtual void start();
|
||||
|
||||
virtual void disconnect();
|
||||
};
|
||||
|
||||
@ -44,6 +44,13 @@ WebThrottleConnection::~WebThrottleConnection()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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));
|
||||
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(
|
||||
[this, throttleId]()
|
||||
{
|
||||
|
||||
@ -31,13 +31,16 @@
|
||||
#include "websocketconnection.hpp"
|
||||
|
||||
class WebThrottle;
|
||||
class World;
|
||||
|
||||
class WebThrottleConnection : public WebSocketConnection
|
||||
{
|
||||
protected:
|
||||
boost::beast::flat_buffer m_readBuffer;
|
||||
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, 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_trainPropertyChanged;
|
||||
|
||||
@ -48,6 +51,7 @@ protected:
|
||||
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::error_code ec);
|
||||
void sendWorld(const std::shared_ptr<World>& world);
|
||||
|
||||
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);
|
||||
virtual ~WebThrottleConnection();
|
||||
|
||||
void start() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -92,7 +92,16 @@ inline static void deleteAll(T& objectList)
|
||||
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(*identifications);
|
||||
deleteAll(*boards);
|
||||
deleteAll(*throttles);
|
||||
deleteAll(*trains);
|
||||
deleteAll(*railVehicles);
|
||||
deleteAll(*luaScripts);
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
|
||||
.hide
|
||||
{
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.stretch
|
||||
@ -76,6 +76,7 @@
|
||||
html
|
||||
{
|
||||
font-family: sans-serif;
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
body
|
||||
@ -109,11 +110,12 @@ header h1
|
||||
#body
|
||||
{
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#settings
|
||||
{
|
||||
z-index: 1;
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
background-color: #121212;
|
||||
width: 100%;
|
||||
@ -126,6 +128,34 @@ header h1
|
||||
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,
|
||||
select.control
|
||||
{
|
||||
|
||||
@ -301,16 +301,13 @@ var tm = new function ()
|
||||
localStorage.throttleName = document.getElementById('throttle_name').value;
|
||||
localStorage.throttleStopOnRelease = document.getElementById('stop_train_on_release').value == 'on';
|
||||
document.getElementById('settings').classList.add('hide');
|
||||
if(tm.throttles.length == 0)
|
||||
{
|
||||
tm.add();
|
||||
}
|
||||
this.connect();
|
||||
};
|
||||
|
||||
if(localStorage.throttleName && localStorage.throttleStopOnRelease)
|
||||
{
|
||||
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.onopen = function (ev)
|
||||
{
|
||||
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,
|
||||
});
|
||||
var trainId = localStorage['throttle' + throttle.id + 'TrainId'];
|
||||
if(trainId)
|
||||
{
|
||||
tm.send({
|
||||
'throttle_id': throttle.id,
|
||||
'action': 'acquire',
|
||||
'train_id': trainId,
|
||||
'steal': false,
|
||||
});
|
||||
}
|
||||
});
|
||||
console.log('onopen');
|
||||
document.getElementById('not-connected').classList.add('hide');
|
||||
};
|
||||
this.ws.onmessage = function (ev)
|
||||
{
|
||||
var msg = JSON.parse(ev.data);
|
||||
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, _)
|
||||
{
|
||||
@ -404,12 +430,17 @@ var tm = new 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);
|
||||
}
|
||||
this.ws.onerror = function (err)
|
||||
{
|
||||
console.error('WebSocket error: ', err.message);
|
||||
this.ws.close();
|
||||
tm.ws.close();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,13 @@
|
||||
<button id="close_settings">Close</button>
|
||||
</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>
|
||||
<script src="/js/throttle.js"></script>
|
||||
</body>
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren