1
0
Fork 0
This commit is contained in:
Scott Giese 2021-02-26 21:40:11 -06:00
commit 7fe25aa97f
8 changed files with 482 additions and 377 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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
{

View file

@ -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.

View file

@ -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. */