PEL: Represent the SRC ASCII string field

In the SRC section of a PEL, there is a field called the 'ASCII string'.
This is the string of 32 characters that shows up on the panel when the
SRC function is chosen, and usually when people refer to an SRC, the
first 8 characters of this field is what they are referring to.

This new class handles that string.  It will belong to the SRC section
object.

For BMC error SRCs, it looks like: BDSSRRRR
Where:
    BD = "BD", indicating a BMC error SRC
    SS = subsystem value from PEL spec
    RRRR = reason code of the error

The remaining 24 characters are spaces (' ').

For example:
    "BD8D1234                        "

For BMC power* related errors, the value is:

    "11001234                        "

Where the difference is the "11" instead of "BD", and the following
2 bytes are always "00".

* 'power' means comes from the repository that monitors for power
  faults.  This is different purely to help keep field service
  documentation the same as in previous IBM server generations.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I6e7292e7f5b501428999781b1a5ee5c243a63ac6
diff --git a/extensions/openpower-pels/ascii_string.cpp b/extensions/openpower-pels/ascii_string.cpp
new file mode 100644
index 0000000..6d4711e
--- /dev/null
+++ b/extensions/openpower-pels/ascii_string.cpp
@@ -0,0 +1,83 @@
+#include "ascii_string.hpp"
+
+#include "pel_types.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+namespace openpower
+{
+namespace pels
+{
+namespace src
+{
+
+using namespace phosphor::logging;
+
+AsciiString::AsciiString(Stream& stream)
+{
+    unflatten(stream);
+}
+
+AsciiString::AsciiString(const message::Entry& entry)
+{
+    // Power Error:  1100RRRR
+    // BMC Error:    BDSSRRRR
+    // where:
+    //  RRRR = reason code
+    //  SS = subsystem ID
+
+    // First is type, like 'BD'
+    setByte(0, entry.src.type);
+
+    // Next is '00', or subsystem ID
+    if (entry.src.type == static_cast<uint8_t>(SRCType::powerError))
+    {
+        setByte(2, 0x00);
+    }
+    else // BMC Error
+    {
+        setByte(2, entry.subsystem);
+    }
+
+    // Then the reason code
+    setByte(4, entry.src.reasonCode >> 8);
+    setByte(6, entry.src.reasonCode & 0xFF);
+
+    // Padded with spaces
+    for (size_t offset = 8; offset < asciiStringSize; offset++)
+    {
+        _string[offset] = ' ';
+    }
+}
+
+void AsciiString::flatten(Stream& stream)
+{
+    stream.write(_string.data(), _string.size());
+}
+
+void AsciiString::unflatten(Stream& stream)
+{
+    stream.read(_string.data(), _string.size());
+}
+
+std::string AsciiString::get() const
+{
+    std::string string{_string.begin(), _string.begin() + _string.size()};
+    return string;
+}
+
+void AsciiString::setByte(size_t byteOffset, uint8_t value)
+{
+    assert(byteOffset < asciiStringSize);
+
+    char characters[3];
+    sprintf(characters, "%02X", value);
+
+    auto writeOffset = byteOffset;
+    _string[writeOffset++] = characters[0];
+    _string[writeOffset] = characters[1];
+}
+
+} // namespace src
+} // namespace pels
+} // namespace openpower