wxString CFileDataIO::ReadOnlyString(bool bOptUTF8, uint16 raw_len) const { // We only need to set the the NULL terminator, since we know that // reads will either succeed or throw an exception, in which case // we wont be returning anything std::vector<char> val_array(raw_len + 1); val_array[raw_len] = 0; char* val = &(val_array[0]); Read(val, raw_len); wxString str; if (CHECK_BOM(raw_len, val)) { // This is a UTF8 string with a BOM header, skip header. str = UTF82unicode(val + 3); } else if (bOptUTF8) { str = UTF82unicode(val); if (str.IsEmpty()) { // Fallback to Latin-1 str = wxString(val, wxConvISO8859_1, raw_len); } } else { // Raw strings are written as Latin-1 (see CFileDataIO::WriteStringCore) str = wxString(val, wxConvISO8859_1, raw_len); } return str; }
// Given a raw sample dbr_type/count/time/data, // map it onto xml_type/count and add to values. // Handles the special case data == 0, // which happens for undefined cells in a SpreadsheetReader. void encode_value(xmlrpc_env *env, DbrType dbr_type, DbrCount dbr_count, const epicsTime &time, const RawValue::Data *data, xmlrpc_int32 xml_type, xmlrpc_int32 xml_count, xmlrpc_value *values) { if (xml_count > dbr_count) xml_count = dbr_count; AutoXmlRpcValue val_array(xmlrpc_build_value(env, "()")); if (env->fault_occurred) return; int i; switch (xml_type) { case XML_STRING: { stdString txt; if (data) RawValue::getValueString(txt, dbr_type, dbr_count, data, 0); AutoXmlRpcValue element(xmlrpc_build_value(env, "s#", txt.c_str(), txt.length())); xmlrpc_array_append_item(env, val_array, element); } break; case XML_INT: case XML_ENUM: { long l; for (i=0; i < xml_count; ++i) { if (!data || !RawValue::getLong(dbr_type, dbr_count, data, l, i)) l = 0; AutoXmlRpcValue element(xmlrpc_build_value(env, "i", (xmlrpc_int32)l)); xmlrpc_array_append_item(env, val_array, element); } } break; case XML_DOUBLE: { double d; for (i=0; i < xml_count; ++i) { if (data && RawValue::getDouble(dbr_type, dbr_count, data, d, i)) { /* XML-RPC, being XML, sends all numbers as strings. * Unfortunately, XML-RPC also insists in the simple 'dot' * notation and prohibits exponential notation. * A number like 1e-300 would turn into * "0.0000000..." with 300 zeroes, * which is too long for the internal print buffer of * the xml-rpc C library. * Since such huge and tiny numbers can't be transferred, * they are replaced by 0 with stat/sevr UDF/INVALID. * * The cut-off point is somewhat arbitrary. * The XML-RPC library uses an internal print buffer * of about 120 characters. * Since PVs are usually scaled to be human-readable, * with only vacuum readings using exp. notation for * data like "1e-8 Torr", exponents of +-50 seemed * reasonable. */ if (d > 0.0) { if (d < 1e-50 || d > 1e50) { d = 0.0; data = 0; } } else if (d < 0.0) { if (d > -1e-50 || d < -1e50) { d = 0.0; data = 0; } } } else d = 0.0; AutoXmlRpcValue element(xmlrpc_build_value(env, "d", d)); xmlrpc_array_append_item(env, val_array, element); } } } xmlrpc_int32 secs, nano; epicsTime2pieces(time, secs, nano); AutoXmlRpcValue value; if (data) value.set(xmlrpc_build_value( env, "{s:i,s:i,s:i,s:i,s:V}", "stat", (xmlrpc_int32)RawValue::getStat(data), "sevr", (xmlrpc_int32)RawValue::getSevr(data), "secs", secs, "nano", nano, "value", (xmlrpc_value *)val_array)); else value.set(xmlrpc_build_value( env, "{s:i,s:i,s:i,s:i,s:V}", "stat", (xmlrpc_int32)UDF_ALARM, "sevr", (xmlrpc_int32)INVALID_ALARM, "secs", secs, "nano", nano, "value", (xmlrpc_value *)val_array)); xmlrpc_array_append_item(env, values, value); }
/** * Creates a time series property from the currently opened log entry. It is * assumed to * have been checked to have a time field and the value entry's name is given as * an argument * @param file :: A reference to the file handle * @param prop_name :: The name of the property * @returns A pointer to a new property containing the time series */ Kernel::Property * LoadNexusLogs::createTimeSeries(::NeXus::File &file, const std::string &prop_name) const { file.openData("time"); //----- Start time is an ISO8601 string date and time. ------ std::string start; try { file.getAttr("start", start); } catch (::NeXus::Exception &) { // Some logs have "offset" instead of start try { file.getAttr("offset", start); } catch (::NeXus::Exception &) { g_log.warning() << "Log entry has no start time indicated.\n"; file.closeData(); throw; } } if (start.compare("No Time") == 0) { start = freqStart; } // Convert to date and time Kernel::DateAndTime start_time = Kernel::DateAndTime(start); std::string time_units; file.getAttr("units", time_units); if (time_units.compare("second") < 0 && time_units != "s" && time_units != "minutes") // Can be s/second/seconds/minutes { file.closeData(); throw ::NeXus::Exception("Unsupported time unit '" + time_units + "'"); } //--- Load the seconds into a double array --- std::vector<double> time_double; try { file.getDataCoerce(time_double); } catch (::NeXus::Exception &e) { g_log.warning() << "Log entry's time field could not be loaded: '" << e.what() << "'.\n"; file.closeData(); throw; } file.closeData(); // Close time data g_log.debug() << " done reading \"time\" array\n"; // Convert to seconds if needed if (time_units == "minutes") { std::transform(time_double.begin(), time_double.end(), time_double.begin(), std::bind2nd(std::multiplies<double>(), 60.0)); } // Now the values: Could be a string, int or double file.openData("value"); // Get the units of the property std::string value_units(""); try { file.getAttr("units", value_units); } catch (::NeXus::Exception &) { // Ignore missing units field. value_units = ""; } // Now the actual data ::NeXus::Info info = file.getInfo(); // Check the size if (size_t(info.dims[0]) != time_double.size()) { file.closeData(); throw ::NeXus::Exception("Invalid value entry for time series"); } if (file.isDataInt()) // Int type { std::vector<int> values; try { file.getDataCoerce(values); file.closeData(); } catch (::NeXus::Exception &) { file.closeData(); throw; } // Make an int TSP auto tsp = new TimeSeriesProperty<int>(prop_name); tsp->create(start_time, time_double, values); tsp->setUnits(value_units); g_log.debug() << " done reading \"value\" array\n"; return tsp; } else if (info.type == ::NeXus::CHAR) { std::string values; const int64_t item_length = info.dims[1]; try { const int64_t nitems = info.dims[0]; const int64_t total_length = nitems * item_length; boost::scoped_array<char> val_array(new char[total_length]); file.getData(val_array.get()); file.closeData(); values = std::string(val_array.get(), total_length); } catch (::NeXus::Exception &) { file.closeData(); throw; } // The string may contain non-printable (i.e. control) characters, replace // these std::replace_if(values.begin(), values.end(), iscntrl, ' '); auto tsp = new TimeSeriesProperty<std::string>(prop_name); std::vector<DateAndTime> times; DateAndTime::createVector(start_time, time_double, times); const size_t ntimes = times.size(); for (size_t i = 0; i < ntimes; ++i) { std::string value_i = std::string(values.data() + i * item_length, item_length); tsp->addValue(times[i], value_i); } tsp->setUnits(value_units); g_log.debug() << " done reading \"value\" array\n"; return tsp; } else if (info.type == ::NeXus::FLOAT32 || info.type == ::NeXus::FLOAT64) { std::vector<double> values; try { file.getDataCoerce(values); file.closeData(); } catch (::NeXus::Exception &) { file.closeData(); throw; } auto tsp = new TimeSeriesProperty<double>(prop_name); tsp->create(start_time, time_double, values); tsp->setUnits(value_units); g_log.debug() << " done reading \"value\" array\n"; return tsp; } else { throw ::NeXus::Exception( "Invalid value type for time series. Only int, double or strings are " "supported"); } }