#include "dbcc/signal.h" #include #include #include #include /**< std::vector */ #include #include "dbcc/helper/signal_helper.h" // FIXME: We ignore the properties and comment of Signal namespace ad { namespace dbcc { static inline std::string &trim(std::string &str, const std::string &toTrim = " ") { std::string::size_type pos = str.find_last_not_of(toTrim); if (pos == std::string::npos) { str.clear(); } else { str.erase(pos + 1); str.erase(0, str.find_first_not_of(toTrim)); } return str; } static inline std::vector &split(const std::string &s, char delim, std::vector &elems) { std::stringstream ss(s); std::string item; while (std::getline(ss, item, delim)) { elems.push_back(item); } return elems; } static inline std::vector split(const std::string &s, char delim) { std::vector elems; split(s, delim, elems); return elems; } static inline bool consume_number(std::istream &in, double &num) { int64_t temp = 0; in >> temp; char c = in.peek(); if (c == '.') { double fraction = 0; in.ignore(1); // . in >> fraction; num = temp + fraction; return true; } num = temp; return false; } std::istream &operator>>(std::istream &in, Signal &sig) { std::string preamble; in >> preamble; // Check if we are actually reading a signal otherwise fail the stream if (preamble != "SG_") { in.setstate(std::ios_base::failbit); return in; } sig.m_isFloat = false; // Parse the signal name in >> sig.m_name; std::string multi; in >> multi; // This case happens if there is not Multiplexor present if (multi == ":") { sig.m_multiplexor = Multiplexor::None; } else { if (multi == "M") { sig.m_multiplexor = Multiplexor::Multiplexor; } else { // The multiplexor looks like that 'm12' so we ignore the m and parse it as integer std::istringstream multistream(multi); multistream.ignore(1); uint16_t multiNum; multistream >> multiNum; sig.m_multiplexor = Multiplexor::Multiplexor; sig.m_multiplexedNumber = multiNum; } // Ignore the next character which is a ':' in >> multi; } in >> sig.m_startBit; in.ignore(1); in >> sig.m_length; in.ignore(1); int order; in >> order; if (order == 0) { sig.m_byteOrder = ByteOrder::Motorola; } else { sig.m_byteOrder = ByteOrder::Intel; } char sign; in >> sign; if (sign == '+') { sig.m_sign = Sign::Unsigned; } else { sig.m_sign = Sign::Signed; } bool isfloat = false; // (factor,offset) [max|min] in.ignore(std::numeric_limits::max(), '('); isfloat = consume_number(in, sig.m_factor) || isfloat; in.ignore(1); // , isfloat = consume_number(in, sig.m_offset) || isfloat; in.ignore(1); // ) in.ignore(std::numeric_limits::max(), '['); isfloat = consume_number(in, sig.m_minimum) || isfloat; in.ignore(1); // | isfloat = consume_number(in, sig.m_maximum) || isfloat; in.ignore(1); // ] sig.m_isFloat = isfloat; // Unit string std::stringstream unit; in.ignore(std::numeric_limits::max(), '\"'); while (in.peek() == '\"') { unit.put(in.get()); } if (in.eof() || !in) { in.setstate(std::ios_base::failbit); return in; } std::string unitstr = unit.str(); sig.m_unit = trim(unitstr, "\""); std::string to; getline(in, to); if (!to.empty() && *to.rbegin() == '\r') { to.erase(to.length() - 1, 1); } if (!to.empty()) { std::vector toStrings = split(to, ','); std::move(toStrings.begin(), toStrings.end(), std::inserter(sig.m_to, sig.m_to.begin())); } return in; } Signal::~Signal() { } bool Signal::compile() { ad::dbcc::helper::SignalHelper sigHelper(*this); for (auto seg : sigHelper.segments(true)) { CompiledSignalParameter param; if (seg.direction == ad::dbcc::helper::SignalHelper::ShiftDirection::Left) { param.left = 1; } else { param.left = 0; } param.index = seg.index; param.shift = seg.shift; param.mask = seg.mask; m_params.emplace_back(param); } if (sign() == ad::dbcc::Sign::Signed) { uint32_t mask = ((1 << (sigHelper.typeLength() - length())) - 1); if (mask != 0) { mask <<= length(); m_signedLengthMask = mask; } } #if defined(DEBUG) && defined(VERBOSE) std::cout << *this << std::endl; #endif /* DEBUG && VERBOSE */ return true; } static uint8_t _packLeftShift(uint64_t value, uint32_t shift, uint32_t mask) { return (uint8_t)((uint8_t)(value << shift) & mask); } static uint8_t _packRightShift(uint64_t value, uint32_t shift, uint32_t mask) { return (uint8_t)((uint8_t)(value >> shift) & mask); } static int64_t _unpackLeftShift(uint8_t value, uint32_t shift, uint32_t mask) { return static_cast(static_cast(value & mask) << shift); } static int64_t _unpackRightShift(uint8_t value, uint32_t shift, uint32_t mask) { return static_cast(static_cast(value & mask) >> shift); } bool Signal::encode(const ParsedValue &pv, uint8_t *data, size_t /* length */) { if (m_params.size() == 0) { compile(); } if (pv.isInteger != isFloat()) { return false; } int64_t temp = 0; if (pv.isInteger) { temp = static_cast((pv.i - offset()) / factor()); } else { temp = static_cast((pv.f - offset()) / factor()); } for (auto &p : m_params) { if (p.left) { data[p.index] |= _packRightShift(temp, p.shift, p.mask); } else { data[p.index] |= _packLeftShift(temp, p.shift, p.mask); } } return true; } bool Signal::decode(const uint8_t *data, size_t /* length */, ParsedValue &pv) { if (m_params.size() == 0) { compile(); } int64_t result = 0; pv.isInteger = !isFloat(); for (auto &p : m_params) { if (p.left) { result |= _unpackLeftShift(data[p.index], p.shift, p.mask); } else { result |= _unpackRightShift(data[p.index], p.shift, p.mask); } } if (m_signedLengthMask != 0) { if ((result & (static_cast(1) << (length() - 1))) != 0) { result |= m_signedLengthMask; } } if (pv.isInteger) { pv.i = static_cast(result * factor() + offset()); } else { pv.f = static_cast(result * factor() + offset()); } return true; } bool Signal::encode(double value, uint8_t *data, size_t /* length */) { if (m_params.size() == 0) { compile(); } int64_t temp = 0; if (!isFloat()) { int32_t i = static_cast(value); temp = static_cast((i - offset()) / factor()); } else { temp = static_cast((value - offset()) / factor()); } for (auto &p : m_params) { if (p.left) { data[p.index] |= _packRightShift(temp, p.shift, p.mask); } else { data[p.index] |= _packLeftShift(temp, p.shift, p.mask); } } return true; } bool Signal::decode(const uint8_t *data, size_t /* length */, double &value) { if (m_params.size() == 0) { compile(); } int64_t result = 0; for (auto &p : m_params) { if (p.left) { result |= _unpackLeftShift(data[p.index], p.shift, p.mask); } else { result |= _unpackRightShift(data[p.index], p.shift, p.mask); } } if (m_signedLengthMask != 0) { if ((result & (static_cast(1) << (length() - 1))) != 0) { result |= m_signedLengthMask; } } if (!isFloat()) { value = static_cast(static_cast(result * factor() + offset())); } else { value = static_cast(result * factor() + offset()); } return true; } // Here I assume the layout: // // Bit // // 7 6 5 4 3 2 1 0 // +---+---+---+---+---+---+---+---+ // 0 |<-x|<---------------------x|<--| // +---+---+---+---+---+---+---+---+ // 1 |-------------------------------| // +---+---+---+---+---+---+---+---+ // 2 |----------x| | | | | | // B +---+---+---+---+---+---+---+---+ // y 3 | | | | | | | | | // t +---+---+---+---+---+---+---+---+ // e 4 | | | | | | | | | // +---+---+---+---+---+---+---+---+ // 5 | | | | | | | | | // +---+---+---+---+---+---+---+---+ // 6 | | | | | | | | | // +---+---+---+---+---+---+---+---+ // 7 | | | | | | | | | // +---+---+---+---+---+---+---+---+ // // Reference: https://github.com/cantools/cantools#the-dump-subcommand bool Signal::bitsLayout(LayoutInfo &info) { info.bits.clear(); info.byteLines.clear(); info.byteLineRange.clear(); if (m_byteOrder == ByteOrder::Intel) //< little-endian { int lineStart = m_startBit / 8; int remainder = 8 - m_startBit % 8; int lineNum = ((m_length - remainder + 7) / 8) + 1; int startBit = m_startBit; int endBit = startBit; if (remainder > m_length) { endBit += m_length; } else { endBit += remainder; } remainder = m_length; for (int line = lineStart; line < lineStart + lineNum; ++line) { for (int bitIdx = startBit; bitIdx < endBit; bitIdx++) { info.bits.push_back(bitIdx); } info.byteLines.push_back(line); info.byteLineRange.emplace_back(std::pair{(9 - startBit % 8) - 1, (8 - (endBit - 1) % 8) - 1}); remainder = remainder - (startBit - endBit); startBit = 8 * (line + 1); if (remainder >= 8) { endBit = startBit + 8; } else { endBit = startBit + remainder; } } } else //< big-endian { int lineStart = m_startBit / 8; int remainder = (8 - (8 - (m_startBit + 1) % 8) % 8); int lineNum = ((m_length - remainder + 7) / 8) + 1; int startBit = m_startBit; int lineRemainder = (startBit % 8) + 1; int endBit = startBit; if (lineRemainder > m_length) { endBit -= m_length; } else { endBit -= lineRemainder; } remainder = m_length; for (int line = lineStart; line < lineStart + lineNum; ++line) { for (int bitIdx = startBit; bitIdx > endBit; bitIdx--) { info.bits.push_back(bitIdx); } info.byteLines.push_back(line); info.byteLineRange.emplace_back(std::pair{(9 - (endBit + 1) % 8) - 1, (8 - startBit % 8) - 1}); remainder = remainder - (startBit - endBit); startBit = 8 * (line + 2) - 1; if (remainder >= 8) { endBit = startBit - 8; } else { endBit = startBit - remainder; } } } return true; } std::ostream &operator<<(std::ostream &out, Signal::CompiledSignalParameter &p) { out << "CompiledSignalParameter: { index: " << p.index << ", " << "shift: " << p.shift << ", " << "mask: 0x" << std::hex << p.mask << std::dec << ", " << "dir: " << (p.left == 1 ? "Left" : "Right") << " }"; return out; } std::ostream &operator<<(std::ostream &out, Signal::LayoutInfo &p) { out << "Bits: { "; for (auto bit : p.bits) { out << bit << ", "; } out << " }\n"; for (size_t idx = 0; idx < p.byteLines.size(); idx++) { out << "Line(" << p.byteLines[idx] << "): [" << p.byteLineRange[idx].first << ", " << p.byteLineRange[idx].second << "]\n"; } return out; } std::ostream &operator<<(std::ostream &out, Signal &cs) { int i = 0; out << "Message: " << cs.getMessageId() << ", Signal: " << cs.name() << (cs.isFloat() ? ", float" : "") << std::endl; for (auto &p : cs.m_params) { out << " param" << i << ": " << p << std::endl; i++; } if (cs.m_signedLengthMask != 0) { out << " mask: 0x" << std::hex << cs.m_signedLengthMask << std::dec << std::endl; } out << " factor: " << cs.factor() << ", offset: " << cs.offset(); return out; } } // namespace dbcc } // namespace ad