Merge branch 'next' of https://git.code.sf.net/p/flightgear/flightgear into next
This commit is contained in:
commit
7fe25aa97f
8 changed files with 482 additions and 377 deletions
|
@ -222,10 +222,14 @@ static std::shared_ptr<RecordExtraProperties> s_record_extra_properties;
|
|||
|
||||
|
||||
FGFlightRecorder::FGFlightRecorder(const char* pConfigName) :
|
||||
m_RecorderNode(fgGetNode("/sim/flight-recorder", true)),
|
||||
m_ReplayMultiplayer(fgGetNode("/sim/replay/multiplayer", true)),
|
||||
m_RecordContinuous(fgGetNode("/sim/replay/record-continuous", true)),
|
||||
m_RecordExtraProperties(fgGetNode("/sim/replay/record-extra-properties", true)),
|
||||
m_RecorderNode (fgGetNode("/sim/flight-recorder", true)),
|
||||
m_ReplayMultiplayer (fgGetNode("/sim/replay/multiplayer", true)),
|
||||
m_ReplayExtraProperties (fgGetNode("/sim/replay/replay-extra-properties", true)),
|
||||
m_ReplayMainView (fgGetNode("/sim/replay/replay-main-view", true)),
|
||||
m_ReplayMainWindowPosition (fgGetNode("/sim/replay/replay-main-window-position", true)),
|
||||
m_ReplayMainWindowSize (fgGetNode("/sim/replay/replay-main-window-size", true)),
|
||||
m_RecordContinuous (fgGetNode("/sim/replay/record-continuous", true)),
|
||||
m_RecordExtraProperties (fgGetNode("/sim/replay/record-extra-properties", true)),
|
||||
m_TotalRecordSize(0),
|
||||
m_ConfigName(pConfigName),
|
||||
m_usingDefaultConfig(false),
|
||||
|
@ -624,12 +628,29 @@ FGFlightRecorder::capture(double SimTime, FGReplayData* ReplayData)
|
|||
ReplayData->multiplayer_messages.clear();
|
||||
bool replayMultiplayer = m_ReplayMultiplayer->getBoolValue();
|
||||
for(;;) {
|
||||
auto MultiplayerMessage = m_MultiplayMgr->popMessageHistory();
|
||||
if (!MultiplayerMessage) {
|
||||
auto multiplayerMessage = m_MultiplayMgr->popMessageHistory();
|
||||
if (!multiplayerMessage) {
|
||||
break;
|
||||
}
|
||||
if (replayMultiplayer) {
|
||||
ReplayData->multiplayer_messages.push_back( MultiplayerMessage);
|
||||
// Attempt to ignore chat messages. Unfortunately it seems
|
||||
// that CHAT_MSG_ID is unused, and instead chat messages are
|
||||
// included in POS_DATA_ID messages as sim/multiplay/chat in
|
||||
// src/MultiPlayer/multiplaymgr.cxx's sIdPropertyList.
|
||||
//
|
||||
auto message_header = reinterpret_cast<const T_MsgHdr*>(&multiplayerMessage->front());
|
||||
xdr_data_t message_id = message_header->MsgId;
|
||||
if (message_id == CHAT_MSG_ID) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Not recording chat message: "
|
||||
<< std::string(
|
||||
reinterpret_cast<const T_ChatMsg*>(message_header + 1)->Text,
|
||||
message_header->MsgLen - sizeof(*message_header)
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
ReplayData->multiplayer_messages.push_back( multiplayerMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,7 +658,7 @@ FGFlightRecorder::capture(double SimTime, FGReplayData* ReplayData)
|
|||
//
|
||||
if (m_RecordContinuous->getBoolValue()) {
|
||||
if (!m_RecordExtraPropertiesReference) {
|
||||
SG_LOG(SG_SYSTEMS, SG_ALERT, "m_RecordPropertiesReference is null");
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "m_RecordPropertiesReference is null");
|
||||
m_RecordExtraPropertiesReference = new SGPropertyNode;
|
||||
}
|
||||
s_record_extra_properties->capture(m_RecordExtraPropertiesReference, ReplayData);
|
||||
|
@ -698,7 +719,7 @@ weighting(TInterpolation interpolation, double ratio, double v1,double v2)
|
|||
void
|
||||
FGFlightRecorder::resetExtraProperties()
|
||||
{
|
||||
SG_LOG(SG_SYSTEMS, SG_ALERT, "Clearing m_RecordExtraPropertiesReference");
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "Clearing m_RecordExtraPropertiesReference");
|
||||
m_RecordExtraPropertiesReference = nullptr;
|
||||
}
|
||||
|
||||
|
@ -726,135 +747,128 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer,
|
|||
int* main_window_ysize
|
||||
)
|
||||
{
|
||||
const char* pLastBuffer = (_pLastBuffer) ? &_pLastBuffer->raw_data.front() : nullptr;
|
||||
const char* pBuffer = (_pNextBuffer) ? &_pNextBuffer->raw_data.front() : nullptr;
|
||||
if (!pBuffer)
|
||||
return;
|
||||
|
||||
int Offset = 0;
|
||||
double ratio = 1.0;
|
||||
if (pLastBuffer)
|
||||
{
|
||||
double NextSimTime = _pNextBuffer->sim_time;
|
||||
double LastSimTime = _pLastBuffer->sim_time;
|
||||
double Numerator = SimTime - LastSimTime;
|
||||
double dt = NextSimTime - LastSimTime;
|
||||
// avoid divide by zero and other quirks
|
||||
if ((Numerator > 0.0)&&(dt != 0.0))
|
||||
const char* pLastBuffer = (_pLastBuffer && !_pLastBuffer->raw_data.empty()) ? &_pLastBuffer->raw_data.front() : nullptr;
|
||||
const char* pBuffer = (_pNextBuffer && !_pNextBuffer->raw_data.empty()) ? &_pNextBuffer->raw_data.front() : nullptr;
|
||||
|
||||
if (pBuffer) {
|
||||
/* Replay signals. */
|
||||
int Offset = 0;
|
||||
double ratio = 1.0;
|
||||
if (pLastBuffer)
|
||||
{
|
||||
ratio = Numerator / dt;
|
||||
if (ratio > 1.0)
|
||||
ratio = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// 64bit aligned data first!
|
||||
{
|
||||
// restore doubles
|
||||
const double* pDoubles = (const double*) &pBuffer[Offset];
|
||||
const double* pLastDoubles = (const double*) &pLastBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureDouble.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
{
|
||||
double v = pDoubles[i];
|
||||
if (pLastBuffer)
|
||||
double NextSimTime = _pNextBuffer->sim_time;
|
||||
double LastSimTime = _pLastBuffer->sim_time;
|
||||
double Numerator = SimTime - LastSimTime;
|
||||
double dt = NextSimTime - LastSimTime;
|
||||
// avoid divide by zero and other quirks
|
||||
if ((Numerator > 0.0)&&(dt != 0.0))
|
||||
{
|
||||
v = weighting(m_CaptureDouble[i].Interpolation, ratio,
|
||||
pLastDoubles[i], v);
|
||||
ratio = Numerator / dt;
|
||||
if (ratio > 1.0)
|
||||
ratio = 1.0;
|
||||
}
|
||||
m_CaptureDouble[i].Signal->setDoubleValue(v);
|
||||
}
|
||||
Offset += SignalCount * sizeof(double);
|
||||
}
|
||||
|
||||
// 32bit aligned data comes second...
|
||||
{
|
||||
// restore floats
|
||||
const float* pFloats = (const float*) &pBuffer[Offset];
|
||||
const float* pLastFloats = (const float*) &pLastBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureFloat.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
// 64bit aligned data first!
|
||||
{
|
||||
float v = pFloats[i];
|
||||
if (pLastBuffer)
|
||||
// restore doubles
|
||||
const double* pDoubles = (const double*) &pBuffer[Offset];
|
||||
const double* pLastDoubles = (const double*) &pLastBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureDouble.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
{
|
||||
v = weighting(m_CaptureFloat[i].Interpolation, ratio,
|
||||
pLastFloats[i], v);
|
||||
double v = pDoubles[i];
|
||||
if (pLastBuffer)
|
||||
{
|
||||
v = weighting(m_CaptureDouble[i].Interpolation, ratio,
|
||||
pLastDoubles[i], v);
|
||||
}
|
||||
m_CaptureDouble[i].Signal->setDoubleValue(v);
|
||||
}
|
||||
m_CaptureFloat[i].Signal->setDoubleValue(v);//setFloatValue
|
||||
Offset += SignalCount * sizeof(double);
|
||||
}
|
||||
Offset += SignalCount * sizeof(float);
|
||||
}
|
||||
|
||||
{
|
||||
// restore integers (32bit aligned)
|
||||
const int* pInt = (const int*) &pBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureInteger.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
// 32bit aligned data comes second...
|
||||
{
|
||||
m_CaptureInteger[i].Signal->setIntValue(pInt[i]);
|
||||
// restore floats
|
||||
const float* pFloats = (const float*) &pBuffer[Offset];
|
||||
const float* pLastFloats = (const float*) &pLastBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureFloat.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
{
|
||||
float v = pFloats[i];
|
||||
if (pLastBuffer)
|
||||
{
|
||||
v = weighting(m_CaptureFloat[i].Interpolation, ratio,
|
||||
pLastFloats[i], v);
|
||||
}
|
||||
m_CaptureFloat[i].Signal->setDoubleValue(v);//setFloatValue
|
||||
}
|
||||
Offset += SignalCount * sizeof(float);
|
||||
}
|
||||
Offset += SignalCount * sizeof(int);
|
||||
}
|
||||
|
||||
// 16bit aligned data is next...
|
||||
{
|
||||
// restore 16bit short integers
|
||||
const short int* pShortInt = (const short int*) &pBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureInt16.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
{
|
||||
m_CaptureInt16[i].Signal->setIntValue(pShortInt[i]);
|
||||
// restore integers (32bit aligned)
|
||||
const int* pInt = (const int*) &pBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureInteger.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
{
|
||||
m_CaptureInteger[i].Signal->setIntValue(pInt[i]);
|
||||
}
|
||||
Offset += SignalCount * sizeof(int);
|
||||
}
|
||||
Offset += SignalCount * sizeof(short int);
|
||||
}
|
||||
|
||||
// finally: byte aligned data is last...
|
||||
{
|
||||
// restore 8bit chars
|
||||
const signed char* pChar = (const signed char*) &pBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureInt8.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
// 16bit aligned data is next...
|
||||
{
|
||||
m_CaptureInt8[i].Signal->setIntValue(pChar[i]);
|
||||
// restore 16bit short integers
|
||||
const short int* pShortInt = (const short int*) &pBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureInt16.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
{
|
||||
m_CaptureInt16[i].Signal->setIntValue(pShortInt[i]);
|
||||
}
|
||||
Offset += SignalCount * sizeof(short int);
|
||||
}
|
||||
Offset += SignalCount * sizeof(signed char);
|
||||
}
|
||||
|
||||
{
|
||||
// restore 1bit booleans (8bit aligned)
|
||||
const unsigned char* pFlags = (const unsigned char*) &pBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureBool.size();
|
||||
int Size = (SignalCount+7)/8;
|
||||
Offset += Size;
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
// finally: byte aligned data is last...
|
||||
{
|
||||
m_CaptureBool[i].Signal->setBoolValue(0 != (pFlags[i>>3] & (1 << (i&7))));
|
||||
// restore 8bit chars
|
||||
const signed char* pChar = (const signed char*) &pBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureInt8.size();
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
{
|
||||
m_CaptureInt8[i].Signal->setIntValue(pChar[i]);
|
||||
}
|
||||
Offset += SignalCount * sizeof(signed char);
|
||||
}
|
||||
|
||||
{
|
||||
// restore 1bit booleans (8bit aligned)
|
||||
const unsigned char* pFlags = (const unsigned char*) &pBuffer[Offset];
|
||||
unsigned int SignalCount = m_CaptureBool.size();
|
||||
int Size = (SignalCount+7)/8;
|
||||
Offset += Size;
|
||||
for (unsigned int i=0; i<SignalCount; i++)
|
||||
{
|
||||
m_CaptureBool[i].Signal->setBoolValue(0 != (pFlags[i>>3] & (1 << (i&7))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replay any multiplayer messages. But don't send the same multiplayer
|
||||
// messages repeatedly when we are called with a timestamp that ends up
|
||||
// picking the same _pNextBuffer as last time.
|
||||
// Replay any multiplayer messages.
|
||||
for (auto multiplayer_message: _pNextBuffer->multiplayer_messages) {
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "Pushing multiplayer message to multiplay manager");
|
||||
m_MultiplayMgr->pushMessageHistory(multiplayer_message);
|
||||
}
|
||||
|
||||
// Replay extra property changes.
|
||||
//
|
||||
static const FGReplayData* _pNextBuffer_prev = nullptr;
|
||||
if ( _pNextBuffer != _pNextBuffer_prev) {
|
||||
_pNextBuffer_prev = _pNextBuffer;
|
||||
for (auto multiplayer_message: _pNextBuffer->multiplayer_messages) {
|
||||
m_MultiplayMgr->pushMessageHistory(multiplayer_message);
|
||||
}
|
||||
}
|
||||
bool replay_extra_properties = m_ReplayExtraProperties->getBoolValue();
|
||||
bool replay_main_view = m_ReplayMainView->getBoolValue();
|
||||
bool replay_main_window_position = m_ReplayMainWindowPosition->getBoolValue();
|
||||
bool replay_main_window_size = m_ReplayMainWindowSize->getBoolValue();
|
||||
|
||||
// Replay property changes.
|
||||
//
|
||||
|
||||
bool replay_extra_property_removal = globals->get_props()->getBoolValue("sim/replay/replay-extra-property-removal");
|
||||
bool replay_extra_property_changes = globals->get_props()->getBoolValue("sim/replay/replay-extra-property-changes");
|
||||
bool replay_main_view = globals->get_props()->getBoolValue("sim/replay/replay-main-view");
|
||||
bool replay_main_window_position = globals->get_props()->getBoolValue("sim/replay/replay-main-window-position");
|
||||
bool replay_main_window_size = globals->get_props()->getBoolValue("sim/replay/replay-main-window-size");
|
||||
|
||||
if (replay_extra_property_removal) {
|
||||
if (replay_extra_properties) {
|
||||
for (auto extra_property_removed_path: _pNextBuffer->replay_extra_property_removals) {
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "replaying extra property removal: " << extra_property_removed_path);
|
||||
globals->get_props()->removeChild(extra_property_removed_path);
|
||||
|
@ -875,7 +889,7 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer,
|
|||
const std::string& path = prop_change.first;
|
||||
const std::string& value = prop_change.second;
|
||||
if (simgear::strutils::starts_with(path, "/sim/current-view/view-number")) {
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "*** SimTime=" << SimTime << " replaying view " << path << "=" << value);
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "SimTime=" << SimTime << " replaying view " << path << "=" << value);
|
||||
globals->get_props()->setStringValue(path, value);
|
||||
}
|
||||
}
|
||||
|
@ -905,7 +919,7 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer,
|
|||
globals->get_props()->setStringValue(path, value);
|
||||
}
|
||||
}
|
||||
else if (replay_extra_property_changes) {
|
||||
else if (replay_extra_properties) {
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "SimTime=" << SimTime
|
||||
<< " replaying extra_property change: " << path << "=" << value);
|
||||
globals->get_props()->setStringValue(path, value);
|
||||
|
|
|
@ -85,7 +85,13 @@ private:
|
|||
|
||||
SGPropertyNode_ptr m_RecorderNode;
|
||||
SGPropertyNode_ptr m_ConfigNode;
|
||||
|
||||
SGPropertyNode_ptr m_ReplayMultiplayer;
|
||||
SGPropertyNode_ptr m_ReplayExtraProperties;
|
||||
SGPropertyNode_ptr m_ReplayMainView;
|
||||
SGPropertyNode_ptr m_ReplayMainWindowPosition;
|
||||
SGPropertyNode_ptr m_ReplayMainWindowSize;
|
||||
|
||||
SGPropertyNode_ptr m_RecordContinuous;
|
||||
SGPropertyNode_ptr m_RecordExtraProperties;
|
||||
|
||||
|
|
|
@ -739,7 +739,7 @@ static SGPropertyNode_ptr saveSetup(
|
|||
|| fgGetBool("/sim/replay/record-main-window", false)
|
||||
|| fgGetBool("/sim/replay/record-main-view", false)
|
||||
) {
|
||||
SG_LOG(SG_SYSTEMS, SG_ALERT, "Adding data[]=extra-properties."
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "Adding data[]=extra-properties."
|
||||
<< " record-extra-properties=" << fgGetBool("/sim/replay/record-extra-properties", false)
|
||||
<< " record-main-window=" << fgGetBool("/sim/replay/record-main-window", false)
|
||||
<< " record-main-view=" << fgGetBool("/sim/replay/record-main-view", false)
|
||||
|
@ -852,6 +852,15 @@ struct FGFrameInfo
|
|||
bool has_extra_properties = false;
|
||||
};
|
||||
|
||||
std::ostream& operator << (std::ostream& out, const FGFrameInfo& frame_info)
|
||||
{
|
||||
return out << "{"
|
||||
<< " offset=" << frame_info.offset
|
||||
<< " has_multiplayer=" << frame_info.has_multiplayer
|
||||
<< " has_extra_properties=" << frame_info.has_extra_properties
|
||||
<< "}";
|
||||
}
|
||||
|
||||
void
|
||||
FGReplay::update( double dt )
|
||||
{
|
||||
|
@ -1093,8 +1102,10 @@ FGReplay::update( double dt )
|
|||
rename(path_temp.c_str(), path.c_str());
|
||||
}
|
||||
else {
|
||||
std::string message = "Failed to update recovery file: " + path.str();
|
||||
popupTip(message.c_str(), 3 /*delay*/);
|
||||
std::string message = "Failed to update recovery snapshot file '" + path.str() + "';"
|
||||
+ " See File / Flight Recorder Control / 'Maintain recovery snapshot'."
|
||||
;
|
||||
popupTip(message.c_str(), 10 /*delay*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1257,69 +1268,44 @@ FGReplay::replay( double time ) {
|
|||
int xsize = xsize0;
|
||||
int ysize = ysize0;
|
||||
|
||||
double t_begin = m_continuous_in_time_last;
|
||||
if (m_continuous_in_extra_properties) {
|
||||
// Continuous recording has property changes.
|
||||
double multiplayer_recent = 3;
|
||||
|
||||
// We replay all frames from just after the previously-replayed frame,
|
||||
// in order to replay extra properties and multiplayer aircraft
|
||||
// correctly.
|
||||
//
|
||||
double t_begin = m_continuous_in_frame_time_last;
|
||||
|
||||
if (time < m_continuous_in_time_last) {
|
||||
// We have gone backwards, e.g. user has clicked on the back
|
||||
// buttons in the Replay dialogue.
|
||||
//
|
||||
if (time < m_continuous_in_time_last) {
|
||||
// We have gone backwards; need to replay all property changes
|
||||
// from t=0.
|
||||
|
||||
if (m_continuous_in_multiplayer) {
|
||||
// Continuous recording has multiplayer data, so replay recent
|
||||
// ones.
|
||||
//
|
||||
t_begin = time - multiplayer_recent;
|
||||
}
|
||||
|
||||
if (m_continuous_in_extra_properties) {
|
||||
// Continuous recording has property changes. we need to replay
|
||||
// all property changes from the beginning.
|
||||
//
|
||||
t_begin = -1;
|
||||
}
|
||||
}
|
||||
|
||||
double multiplayer_recent = 3;
|
||||
if (m_continuous_in_multiplayer) {
|
||||
double t = std::max(0.0, time - multiplayer_recent);
|
||||
t_begin = std::min(t_begin, t);
|
||||
}
|
||||
|
||||
// Replay property changes for all t in t_prop_begin < t < time, and multiplayer
|
||||
// changes for most recent multiplayer_recent seconds.
|
||||
//
|
||||
for (auto p = m_continuous_in_time_to_frameinfo.upper_bound(t_begin);
|
||||
p != m_continuous_in_time_to_frameinfo.end();
|
||||
++p)
|
||||
{
|
||||
if (p->first >= time) break;
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "Replaying extra property changes."
|
||||
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "Have gone backwards."
|
||||
<< " m_continuous_in_time_last=" << m_continuous_in_time_last
|
||||
<< " time=" << time
|
||||
<< " t_begin=" << t_begin
|
||||
<< " p->first=" << p->first
|
||||
<< " p->second.offset=" << p->second.offset
|
||||
<< " m_continuous_in_extra_properties=" << m_continuous_in_extra_properties
|
||||
);
|
||||
|
||||
// Replaying a frame is expensive because we read frame data
|
||||
// from disc each time. So we only replay this frame if it has
|
||||
// extra_properties, or if it has multiplayer packets and we are
|
||||
// within <multiplayer_recent> seconds of current time.
|
||||
//
|
||||
bool replay_this_frame = p->second.has_extra_properties;
|
||||
if (!replay_this_frame) {
|
||||
if (p->first > time - multiplayer_recent) {
|
||||
if (p->second.has_multiplayer) {
|
||||
replay_this_frame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (replay_this_frame) {
|
||||
replay(
|
||||
p->first,
|
||||
p->second.offset,
|
||||
0 /*offset_old*/,
|
||||
false /*replay_signals*/,
|
||||
p->first > time - multiplayer_recent /*replay_multiplayer*/,
|
||||
true /*replay_extra_properties*/,
|
||||
&xpos,
|
||||
&ypos,
|
||||
&xsize,
|
||||
&ysize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Replay from uncompressed recording file.
|
||||
// Prepare to replay signals from uncompressed recording file. We want
|
||||
// to find a pair of frames that straddle the requested <time> so that
|
||||
// we can interpolate.
|
||||
//
|
||||
auto p = m_continuous_in_time_to_frameinfo.lower_bound(time);
|
||||
bool ret = false;
|
||||
|
@ -1340,7 +1326,7 @@ FGReplay::replay( double time ) {
|
|||
offset = p->second.offset;
|
||||
}
|
||||
else {
|
||||
// Interpolate between items that straddle <time>.
|
||||
// Interpolate between pair of items that straddle <time>.
|
||||
auto prev = p;
|
||||
--prev;
|
||||
offset_prev = prev->second.offset;
|
||||
|
@ -1351,6 +1337,56 @@ FGReplay::replay( double time ) {
|
|||
// Exact match.
|
||||
offset = p->second.offset;
|
||||
}
|
||||
|
||||
// Before interpolating signals, we replay all property changes from
|
||||
// all frame times t satisfying t_prop_begin < t < time. We also replay
|
||||
// all recent multiplayer packets in this range, i.e. for which t >
|
||||
// time - multiplayer_recent.
|
||||
//
|
||||
for (auto p_before = m_continuous_in_time_to_frameinfo.upper_bound(t_begin);
|
||||
p_before != m_continuous_in_time_to_frameinfo.end();
|
||||
++p_before) {
|
||||
if (p_before->first >= p->first) {
|
||||
break;
|
||||
}
|
||||
// Replaying a frame is expensive because we read frame data
|
||||
// from disc each time. So we only replay this frame if it has
|
||||
// extra_properties, or if it has multiplayer packets and we are
|
||||
// within <multiplayer_recent> seconds of current time.
|
||||
//
|
||||
bool replay_this_frame = p_before->second.has_extra_properties;
|
||||
if (p_before->second.has_multiplayer && p_before->first > time - multiplayer_recent) {
|
||||
replay_this_frame = true;
|
||||
}
|
||||
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "Looking at extra property changes."
|
||||
<< " replay_this_frame=" << replay_this_frame
|
||||
<< " m_continuous_in_time_last=" << m_continuous_in_time_last
|
||||
<< " m_continuous_in_frame_time_last=" << m_continuous_in_frame_time_last
|
||||
<< " time=" << time
|
||||
<< " t_begin=" << t_begin
|
||||
<< " p_before->first=" << p_before->first
|
||||
<< " p_before->second=" << p_before->second
|
||||
);
|
||||
|
||||
if (replay_this_frame) {
|
||||
replay(
|
||||
p_before->first,
|
||||
p_before->second.offset,
|
||||
0 /*offset_old*/,
|
||||
false /*replay_signals*/,
|
||||
p_before->first > time - multiplayer_recent /*replay_multiplayer*/,
|
||||
true /*replay_extra_properties*/,
|
||||
&xpos,
|
||||
&ypos,
|
||||
&xsize,
|
||||
&ysize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now replay signals, interpolating between frames atoffset_prev and
|
||||
offset. */
|
||||
replay(
|
||||
time,
|
||||
offset,
|
||||
|
@ -1392,6 +1428,7 @@ FGReplay::replay( double time ) {
|
|||
}
|
||||
|
||||
m_continuous_in_time_last = time;
|
||||
m_continuous_in_frame_time_last = p->first;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1543,7 +1580,6 @@ static std::unique_ptr<FGReplayData> ReadFGReplayData(
|
|||
ret->multiplayer_messages.clear();
|
||||
uint32_t pos = 0;
|
||||
for(;;) {
|
||||
SG_LOG(SG_SYSTEMS, SG_DEBUG, "length=" << length << " pos=" << pos);
|
||||
assert(pos <= length);
|
||||
if (pos == length) {
|
||||
break;
|
||||
|
@ -1551,6 +1587,12 @@ static std::unique_ptr<FGReplayData> ReadFGReplayData(
|
|||
std::shared_ptr<std::vector<char>> v(new std::vector<char>);
|
||||
ret->multiplayer_messages.push_back(v);
|
||||
pos += VectorRead<uint16_t>(in, *ret->multiplayer_messages.back(), length - pos);
|
||||
SG_LOG(SG_SYSTEMS, SG_BULK, "replaying multiplayer data"
|
||||
<< " ret->sim_time=" << ret->sim_time
|
||||
<< " length=" << length
|
||||
<< " pos=" << pos
|
||||
<< " callsign=" << ((T_MsgHdr*) &v->front())->Callsign
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (load_extra_properties && !strcmp(data_type, "extra-properties")) {
|
||||
|
@ -1586,12 +1628,6 @@ void FGReplay::replay(
|
|||
int* ysize
|
||||
)
|
||||
{
|
||||
SG_LOG(SG_SYSTEMS, SG_BULK,
|
||||
"FGReplay::replay():"
|
||||
<< " time=" << time
|
||||
<< " offset=" << offset
|
||||
<< " offset_old=" << offset_old
|
||||
);
|
||||
std::unique_ptr<FGReplayData> replay_data = ReadFGReplayData(
|
||||
m_continuous_in,
|
||||
offset,
|
||||
|
@ -1600,6 +1636,7 @@ void FGReplay::replay(
|
|||
replay_multiplayer,
|
||||
replay_extra_properties
|
||||
);
|
||||
assert(replay_data.get());
|
||||
std::unique_ptr<FGReplayData> replay_data_old;
|
||||
if (offset_old) {
|
||||
replay_data_old = ReadFGReplayData(
|
||||
|
@ -1611,6 +1648,16 @@ void FGReplay::replay(
|
|||
replay_extra_properties
|
||||
);
|
||||
}
|
||||
if (replay_extra_properties) SG_LOG(SG_SYSTEMS, SG_BULK,
|
||||
"FGReplay::replay():"
|
||||
<< " time=" << time
|
||||
<< " offset=" << offset
|
||||
<< " offset_old=" << offset_old
|
||||
<< " replay_data->raw_data.size()=" << replay_data->raw_data.size()
|
||||
<< " replay_data->multiplayer_messages.size()=" << replay_data->multiplayer_messages.size()
|
||||
<< " replay_data->extra_properties.size()=" << replay_data->extra_properties.size()
|
||||
<< " replay_data->replay_extra_property_changes.size()=" << replay_data->replay_extra_property_changes.size()
|
||||
);
|
||||
m_pRecorder->replay(time, replay_data.get(), replay_data_old.get(), xpos, ypos, xsize, ysize);
|
||||
}
|
||||
|
||||
|
@ -1918,10 +1965,12 @@ void FGReplay::indexContinuousRecording(const void* data, size_t numbytes)
|
|||
else if (!strcmp(data->getStringValue(), "multiplayer")) {
|
||||
frameinfo.has_multiplayer = true;
|
||||
++m_num_frames_multiplayer;
|
||||
m_continuous_in_multiplayer = true;
|
||||
}
|
||||
else if (!strcmp(data->getStringValue(), "extra-properties")) {
|
||||
frameinfo.has_extra_properties = true;
|
||||
++m_num_frames_extra_properties;
|
||||
m_continuous_in_extra_properties = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2021,6 +2070,7 @@ FGReplay::loadTape(const SGPath& Filename, bool Preview, SGPropertyNode& MetaMet
|
|||
clear();
|
||||
fillRecycler();
|
||||
m_continuous_in_time_last = -1;
|
||||
m_continuous_in_frame_time_last = -1;
|
||||
m_continuous_in_time_to_frameinfo.clear();
|
||||
m_num_frames_extra_properties = 0;
|
||||
m_num_frames_multiplayer = 0;
|
||||
|
@ -2241,6 +2291,17 @@ FGReplay::listTapes(bool SameAircraftFilter, const SGPath& tapeDirectory)
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string FGReplay::makeTapePath(const std::string& tape_name)
|
||||
{
|
||||
std::string path = tape_name;
|
||||
if (simgear::strutils::ends_with(path, ".fgtape")) {
|
||||
return path;
|
||||
}
|
||||
SGPath path2(fgGetString("/sim/replay/tape-directory", ""));
|
||||
path2.append(path + ".fgtape");
|
||||
return path2.str();
|
||||
}
|
||||
|
||||
/** Load a flight recorder tape from disk. User/script command. */
|
||||
bool
|
||||
FGReplay::loadTape(const SGPropertyNode* ConfigData)
|
||||
|
@ -2262,19 +2323,9 @@ FGReplay::loadTape(const SGPropertyNode* ConfigData)
|
|||
else
|
||||
{
|
||||
SGPropertyNode* MetaMeta = fgGetNode("/sim/gui/dialogs/flightrecorder/preview", true);
|
||||
SGPath tapePath;
|
||||
if (simgear::strutils::ends_with(tape, ".fgtape"))
|
||||
{
|
||||
tapePath = tape;
|
||||
}
|
||||
else
|
||||
{
|
||||
tapePath = tapeDirectory;
|
||||
tapePath.append(tape);
|
||||
tapePath.concat(".fgtape");
|
||||
}
|
||||
SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Checking flight recorder file " << tapePath << ", preview: " << Preview);
|
||||
return loadTape(tapePath, Preview, *MetaMeta);
|
||||
std::string path = makeTapePath(tape);
|
||||
SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Checking flight recorder file " << path << ", preview: " << Preview);
|
||||
return loadTape(path, Preview, *MetaMeta);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -145,6 +145,10 @@ public:
|
|||
//
|
||||
static int loadContinuousHeader(const std::string& path, std::istream* in, SGPropertyNode* properties);
|
||||
|
||||
// Prepends /sim/replay/tape-directory and/or appends .fgtape etc.
|
||||
//
|
||||
static std::string makeTapePath(const std::string& tape_name);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
FGReplayData* record(double time);
|
||||
|
@ -255,6 +259,7 @@ private:
|
|||
std::map<double, FGFrameInfo> m_continuous_in_time_to_frameinfo;
|
||||
SGPropertyNode_ptr m_continuous_in_config;
|
||||
double m_continuous_in_time_last;
|
||||
double m_continuous_in_frame_time_last;
|
||||
|
||||
std::ifstream m_continuous_indexing_in;
|
||||
std::streampos m_continuous_indexing_pos;
|
||||
|
|
|
@ -1575,18 +1575,37 @@ static std::string urlToLocalPath(const char* url)
|
|||
const char* s2 = (http) ? url+7 : url+8; // fg.com/foo/bar/wibble.fgtape
|
||||
const char* s3 = strchr(s2, '/'); // /foo/bar/wibble.fgtape
|
||||
const char* s4 = (s3) ? strrchr(s3, '/') : NULL; // /wibble.fgtape
|
||||
std::string path;
|
||||
if (s3) path = std::string(s2, s3-s2); // fg.com
|
||||
path += '_'; // fg.com_
|
||||
std::string path = "url_";
|
||||
if (s3) path += std::string(s2, s3-s2); // url_fg.com
|
||||
path += '_'; // url_fg.com_
|
||||
if (s3 && s4 > s3) {
|
||||
path += simgear::strutils::md5(s3, s4-s3).substr(0, 8);
|
||||
path += '_'; // fg.com_12345678_
|
||||
path += '_'; // url_fg.com_12345678_
|
||||
}
|
||||
if (s4) path += (s4+1); // fg.com_12345678_wibble.fgtape
|
||||
if (s4) path += (s4+1); // url_fg.com_12345678_wibble.fgtape
|
||||
if (!simgear::strutils::ends_with(path, ".fgtape")) path += ".fgtape";
|
||||
std::string dir = fgGetString("/sim/replay/tape-directory");
|
||||
if (dir != "") {
|
||||
SGPath path2 = dir;
|
||||
path2.append(path);
|
||||
path = path2.str();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// When loading a Continuous recording at startup, we need to override the
|
||||
// aircraft and airport. Unfortunately we can't simply set /sim/aircraft
|
||||
// because there may be --aircraft options later on in the command line. Also
|
||||
// fgMainInit() ends up calling Options::initAircraft() after we have processed
|
||||
// all options, and Options::initAircraft() seems to look directly at the
|
||||
// options again, instead of using /sim/aircraft.
|
||||
//
|
||||
// So we store any aircraft/airport override here, so that
|
||||
// Options::initAircraft() can use them if they are set, instead of going back
|
||||
// to any user-specified aircraft.
|
||||
//
|
||||
static std::string g_load_tape_aircraft;
|
||||
static std::string g_load_tape_airport;
|
||||
|
||||
static int
|
||||
fgOptLoadTape(const char* arg)
|
||||
|
@ -1639,7 +1658,8 @@ fgOptLoadTape(const char* arg)
|
|||
//
|
||||
// Load the recording's header if it is a Continuous recording.
|
||||
//
|
||||
(void) FGReplay::loadContinuousHeader(arg, nullptr /*in*/, properties);
|
||||
path = FGReplay::makeTapePath(arg);
|
||||
(void) FGReplay::loadContinuousHeader(path.c_str(), nullptr /*in*/, properties);
|
||||
}
|
||||
else {
|
||||
// <arg> is a URL. Start download.
|
||||
|
@ -1653,6 +1673,7 @@ fgOptLoadTape(const char* arg)
|
|||
const char* url = arg;
|
||||
FGHTTPClient* http = globals->add_new_subsystem<FGHTTPClient>();
|
||||
http->init();
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Replaying url " << url << " using local path: " << path);
|
||||
filerequest.reset(new simgear::HTTP::FileRequest(url, path, true /*append*/));
|
||||
long max_download_speed = fgGetLong("/sim/replay/download-max-bytes-per-sec");
|
||||
if (max_download_speed != 0) {
|
||||
|
@ -1712,19 +1733,11 @@ fgOptLoadTape(const char* arg)
|
|||
std::string aircraft = properties->getStringValue("meta/aircraft-type");
|
||||
std::string airport = properties->getStringValue("meta/closest-airport-id");
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "From recording header: aircraft=" << aircraft << " airport=" << airport);
|
||||
if (aircraft != "") {
|
||||
// Force --aircraft and --airport options to use values from the
|
||||
// recording.
|
||||
//
|
||||
Options::sharedInstance()->setOption("aircraft", aircraft);
|
||||
}
|
||||
if (airport != "") {
|
||||
// Looks like setting --airport option doesn't work - we need to call
|
||||
// fgOptAirport() directly.
|
||||
//
|
||||
Options::sharedInstance()->setOption("airport", airport);
|
||||
fgOptAirport(airport.c_str());
|
||||
}
|
||||
// Override aircraft and airport settings to match what is in the
|
||||
// recording.
|
||||
//
|
||||
g_load_tape_aircraft = aircraft;
|
||||
g_load_tape_airport = airport;
|
||||
|
||||
// Arrange to load the recording after FDM has initialised.
|
||||
new DelayedTapeLoader(path.c_str(), filerequest);
|
||||
|
@ -2371,7 +2384,11 @@ void Options::initPaths()
|
|||
OptionResult Options::initAircraft()
|
||||
{
|
||||
string aircraft;
|
||||
if (isOptionSet("aircraft")) {
|
||||
if (g_load_tape_aircraft != "") {
|
||||
// Use Continuous recording's aircraft if we are replaying on startup.
|
||||
aircraft = g_load_tape_aircraft;
|
||||
}
|
||||
else if (isOptionSet("aircraft")) {
|
||||
aircraft = valueForOption("aircraft");
|
||||
} else if (isOptionSet("vehicle")) {
|
||||
aircraft = valueForOption("vehicle");
|
||||
|
@ -2827,6 +2844,19 @@ OptionResult Options::processOptions()
|
|||
globals->append_fg_scenery(root);
|
||||
}
|
||||
|
||||
if (g_load_tape_aircraft != "") {
|
||||
// This might not be necessary, because we always end up calling
|
||||
// Options::initAircraft() later on, which also knows to use
|
||||
// g_load_tape_aircraft if it is not "".
|
||||
//
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "overriding aircraft from " << fgGetString("/sim/aircraft") << " to " << g_load_tape_aircraft);
|
||||
fgSetString("/sim/aircraft", g_load_tape_aircraft);
|
||||
}
|
||||
if (g_load_tape_airport != "") {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "overriding airport from " << fgGetString("/sim/presets/airport-id") << " to " << g_load_tape_airport);
|
||||
fgOptAirport(g_load_tape_airport.c_str());
|
||||
}
|
||||
|
||||
if (isOptionSet("json-report")) {
|
||||
printJSONReport();
|
||||
return FG_OPTIONS_EXIT;
|
||||
|
|
|
@ -2299,169 +2299,169 @@ FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
|
|||
// Check the ID actually exists and get the type
|
||||
const IdPropertyList* plist = findProperty(id);
|
||||
|
||||
if (plist)
|
||||
{
|
||||
FGPropertyData* pData = new FGPropertyData;
|
||||
if (plist->decode_received)
|
||||
if (plist)
|
||||
{
|
||||
//
|
||||
// this needs the pointer prior to the extraction of the property id and possible shortint decode
|
||||
// too allow the method to redecode as it wishes
|
||||
xdr = (*plist->decode_received)(plist, xdr, pData);
|
||||
}
|
||||
else
|
||||
{
|
||||
pData->id = id;
|
||||
pData->type = plist->type;
|
||||
xdr++;
|
||||
// How we decode the remainder of the property depends on the type
|
||||
switch (pData->type) {
|
||||
case simgear::props::BOOL:
|
||||
/*
|
||||
* For 2017.2 we support boolean arrays transmitted as a single int for 30 bools.
|
||||
* this section handles the unpacking into the arrays.
|
||||
*/
|
||||
if (pData->id >= BOOLARRAY_START_ID && pData->id <= BOOLARRAY_END_ID)
|
||||
{
|
||||
unsigned int val = XDR_decode_uint32(*xdr);
|
||||
bool first_bool = true;
|
||||
xdr++;
|
||||
for (int bitidx = 0; bitidx <= 30; bitidx++)
|
||||
{
|
||||
// ensure that this property is in the master list.
|
||||
const IdPropertyList* plistBool = findProperty(id + bitidx);
|
||||
|
||||
if (plistBool)
|
||||
{
|
||||
if (first_bool)
|
||||
first_bool = false;
|
||||
else
|
||||
pData = new FGPropertyData;
|
||||
|
||||
pData->id = id + bitidx;
|
||||
pData->int_value = (val & (1 << bitidx)) != 0;
|
||||
pData->type = simgear::props::BOOL;
|
||||
motionInfo.properties.push_back(pData);
|
||||
|
||||
// ensure that this is null because this section of code manages the property data and list directly
|
||||
// it has to be this way because one MP value results in multiple properties being set.
|
||||
pData = nullptr;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case simgear::props::INT:
|
||||
case simgear::props::LONG:
|
||||
if (short_int_encoded)
|
||||
{
|
||||
pData->int_value = int_value;
|
||||
pData->type = simgear::props::INT;
|
||||
}
|
||||
else
|
||||
{
|
||||
pData->int_value = XDR_decode_uint32(*xdr);
|
||||
xdr++;
|
||||
}
|
||||
//cout << pData->int_value << "\n";
|
||||
break;
|
||||
case simgear::props::FLOAT:
|
||||
case simgear::props::DOUBLE:
|
||||
if (short_int_encoded)
|
||||
{
|
||||
switch (plist->TransmitAs)
|
||||
{
|
||||
case TT_SHORT_FLOAT_1:
|
||||
pData->float_value = (double)int_value / 10.0;
|
||||
break;
|
||||
case TT_SHORT_FLOAT_2:
|
||||
pData->float_value = (double)int_value / 100.0;
|
||||
break;
|
||||
case TT_SHORT_FLOAT_3:
|
||||
pData->float_value = (double)int_value / 1000.0;
|
||||
break;
|
||||
case TT_SHORT_FLOAT_4:
|
||||
pData->float_value = (double)int_value / 10000.0;
|
||||
break;
|
||||
case TT_SHORT_FLOAT_NORM:
|
||||
pData->float_value = (double)int_value / 32767.0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pData->float_value = XDR_decode_float(*xdr);
|
||||
xdr++;
|
||||
}
|
||||
break;
|
||||
case simgear::props::STRING:
|
||||
case simgear::props::UNSPECIFIED:
|
||||
FGPropertyData* pData = new FGPropertyData;
|
||||
if (plist->decode_received)
|
||||
{
|
||||
// if the string is using short int encoding then it is in the new format.
|
||||
if (short_int_encoded)
|
||||
{
|
||||
uint32_t length = int_value;
|
||||
pData->string_value = new char[length + 1];
|
||||
|
||||
char *cptr = (char*)xdr;
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
{
|
||||
pData->string_value[i] = *cptr++;
|
||||
}
|
||||
pData->string_value[length] = '\0';
|
||||
xdr = (xdr_data_t*)cptr;
|
||||
}
|
||||
else {
|
||||
// String is complicated. It consists of
|
||||
// The length of the string
|
||||
// The string itself
|
||||
// Padding to the nearest 4-bytes.
|
||||
uint32_t length = XDR_decode_uint32(*xdr);
|
||||
xdr++;
|
||||
//cout << length << " ";
|
||||
// Old versions truncated the string but left the length unadjusted.
|
||||
if (length > MAX_TEXT_SIZE)
|
||||
length = MAX_TEXT_SIZE;
|
||||
pData->string_value = new char[length + 1];
|
||||
//cout << " String: ";
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
{
|
||||
pData->string_value[i] = (char)XDR_decode_int8(*xdr);
|
||||
xdr++;
|
||||
}
|
||||
|
||||
pData->string_value[length] = '\0';
|
||||
|
||||
// Now handle the padding
|
||||
while ((length % 4) != 0)
|
||||
{
|
||||
xdr++;
|
||||
length++;
|
||||
//cout << "0";
|
||||
}
|
||||
//cout << "\n";
|
||||
}
|
||||
//
|
||||
// this needs the pointer prior to the extraction of the property id and possible shortint decode
|
||||
// too allow the method to redecode as it wishes
|
||||
xdr = (*plist->decode_received)(plist, xdr, pData);
|
||||
}
|
||||
break;
|
||||
else
|
||||
{
|
||||
pData->id = id;
|
||||
pData->type = plist->type;
|
||||
xdr++;
|
||||
// How we decode the remainder of the property depends on the type
|
||||
switch (pData->type) {
|
||||
case simgear::props::BOOL:
|
||||
/*
|
||||
* For 2017.2 we support boolean arrays transmitted as a single int for 30 bools.
|
||||
* this section handles the unpacking into the arrays.
|
||||
*/
|
||||
if (pData->id >= BOOLARRAY_START_ID && pData->id <= BOOLARRAY_END_ID)
|
||||
{
|
||||
unsigned int val = XDR_decode_uint32(*xdr);
|
||||
bool first_bool = true;
|
||||
xdr++;
|
||||
for (int bitidx = 0; bitidx <= 30; bitidx++)
|
||||
{
|
||||
// ensure that this property is in the master list.
|
||||
const IdPropertyList* plistBool = findProperty(id + bitidx);
|
||||
|
||||
default:
|
||||
pData->float_value = XDR_decode_float(*xdr);
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type);
|
||||
xdr++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pData) {
|
||||
motionInfo.properties.push_back(pData);
|
||||
if (plistBool)
|
||||
{
|
||||
if (first_bool)
|
||||
first_bool = false;
|
||||
else
|
||||
pData = new FGPropertyData;
|
||||
|
||||
// Special case - we need the /sim/model/fallback-model-index to create
|
||||
// the MP model
|
||||
if (pData->id == FALLBACK_MODEL_ID) {
|
||||
fallback_model_index = pData->int_value;
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "Found Fallback model index in message " << fallback_model_index);
|
||||
pData->id = id + bitidx;
|
||||
pData->int_value = (val & (1 << bitidx)) != 0;
|
||||
pData->type = simgear::props::BOOL;
|
||||
motionInfo.properties.push_back(pData);
|
||||
|
||||
// ensure that this is null because this section of code manages the property data and list directly
|
||||
// it has to be this way because one MP value results in multiple properties being set.
|
||||
pData = nullptr;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case simgear::props::INT:
|
||||
case simgear::props::LONG:
|
||||
if (short_int_encoded)
|
||||
{
|
||||
pData->int_value = int_value;
|
||||
pData->type = simgear::props::INT;
|
||||
}
|
||||
else
|
||||
{
|
||||
pData->int_value = XDR_decode_uint32(*xdr);
|
||||
xdr++;
|
||||
}
|
||||
//cout << pData->int_value << "\n";
|
||||
break;
|
||||
case simgear::props::FLOAT:
|
||||
case simgear::props::DOUBLE:
|
||||
if (short_int_encoded)
|
||||
{
|
||||
switch (plist->TransmitAs)
|
||||
{
|
||||
case TT_SHORT_FLOAT_1:
|
||||
pData->float_value = (double)int_value / 10.0;
|
||||
break;
|
||||
case TT_SHORT_FLOAT_2:
|
||||
pData->float_value = (double)int_value / 100.0;
|
||||
break;
|
||||
case TT_SHORT_FLOAT_3:
|
||||
pData->float_value = (double)int_value / 1000.0;
|
||||
break;
|
||||
case TT_SHORT_FLOAT_4:
|
||||
pData->float_value = (double)int_value / 10000.0;
|
||||
break;
|
||||
case TT_SHORT_FLOAT_NORM:
|
||||
pData->float_value = (double)int_value / 32767.0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pData->float_value = XDR_decode_float(*xdr);
|
||||
xdr++;
|
||||
}
|
||||
break;
|
||||
case simgear::props::STRING:
|
||||
case simgear::props::UNSPECIFIED:
|
||||
{
|
||||
// if the string is using short int encoding then it is in the new format.
|
||||
if (short_int_encoded)
|
||||
{
|
||||
uint32_t length = int_value;
|
||||
pData->string_value = new char[length + 1];
|
||||
|
||||
char *cptr = (char*)xdr;
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
{
|
||||
pData->string_value[i] = *cptr++;
|
||||
}
|
||||
pData->string_value[length] = '\0';
|
||||
xdr = (xdr_data_t*)cptr;
|
||||
}
|
||||
else {
|
||||
// String is complicated. It consists of
|
||||
// The length of the string
|
||||
// The string itself
|
||||
// Padding to the nearest 4-bytes.
|
||||
uint32_t length = XDR_decode_uint32(*xdr);
|
||||
xdr++;
|
||||
//cout << length << " ";
|
||||
// Old versions truncated the string but left the length unadjusted.
|
||||
if (length > MAX_TEXT_SIZE)
|
||||
length = MAX_TEXT_SIZE;
|
||||
pData->string_value = new char[length + 1];
|
||||
//cout << " String: ";
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
{
|
||||
pData->string_value[i] = (char)XDR_decode_int8(*xdr);
|
||||
xdr++;
|
||||
}
|
||||
|
||||
pData->string_value[length] = '\0';
|
||||
|
||||
// Now handle the padding
|
||||
while ((length % 4) != 0)
|
||||
{
|
||||
xdr++;
|
||||
length++;
|
||||
//cout << "0";
|
||||
}
|
||||
//cout << "\n";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
pData->float_value = XDR_decode_float(*xdr);
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type);
|
||||
xdr++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pData) {
|
||||
motionInfo.properties.push_back(pData);
|
||||
|
||||
// Special case - we need the /sim/model/fallback-model-index to create
|
||||
// the MP model
|
||||
if (pData->id == FALLBACK_MODEL_ID) {
|
||||
fallback_model_index = pData->int_value;
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "Found Fallback model index in message " << fallback_model_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// \file props.cxx
|
||||
// Property server class.
|
||||
// Property server class. Used for telnet server.
|
||||
//
|
||||
// Written by Curtis Olson, started September 2000.
|
||||
// Modified by Bernie Bright, May 2002.
|
||||
|
|
|
@ -829,8 +829,8 @@ struct SviewStepDouble : SviewStep
|
|||
|
||||
We require that:
|
||||
|
||||
EL is local aircraft's chase-distance (though at the moment we use
|
||||
a fixed value) so that local aircraft is in perfect view.
|
||||
EL is local aircraft's chase-distance so that local aircraft is in
|
||||
perfect view.
|
||||
|
||||
Angle LER is fixed to give good view of both aircraft in
|
||||
window. (Should be related to the vertical angular size of the
|
||||
|
@ -1647,6 +1647,8 @@ struct EventHandler : osgGA::EventHandler
|
|||
|
||||
std::shared_ptr<SviewView> SviewCreate(SGPropertyNode* config)
|
||||
{
|
||||
assert(config);
|
||||
|
||||
FGRenderer* renderer = globals->get_renderer();
|
||||
osgViewer::ViewerBase* viewer_base = renderer->getViewerBase();
|
||||
osgViewer::CompositeViewer* composite_viewer = dynamic_cast<osgViewer::CompositeViewer*>(viewer_base);
|
||||
|
@ -1655,19 +1657,15 @@ std::shared_ptr<SviewView> SviewCreate(SGPropertyNode* config)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
osgViewer::View* main_view = renderer->getView();
|
||||
osg::Node* scene_data = main_view->getSceneData();
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "main_view->getNumSlaves()=" << main_view->getNumSlaves());
|
||||
|
||||
osgViewer::View* view = new osgViewer::View();
|
||||
//EventHandler* event_handler = new EventHandler;
|
||||
flightgear::FGEventHandler* event_handler = globals->get_renderer()->getEventHandler();
|
||||
view->addEventHandler(event_handler);
|
||||
|
||||
assert(config);
|
||||
|
||||
std::shared_ptr<SviewView> sview_view;
|
||||
|
||||
const char* type = config->getStringValue("type");
|
||||
|
@ -1708,6 +1706,7 @@ std::shared_ptr<SviewView> SviewCreate(SGPropertyNode* config)
|
|||
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
|
||||
osg::ref_ptr<osg::GraphicsContext> gc;
|
||||
|
||||
/* When we implement canvas views, we won't create a new window here. */
|
||||
if (1) {
|
||||
/* Create a new window. */
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue