meta data for this page
  •  

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
doc_urecs:software_interface [2023/10/17 10:50] – [Components] vordoc_urecs:software_interface [2024/01/10 17:18] (current) – [LoRa Message] fun
Line 1: Line 1:
 ====== Software interface ====== ====== Software interface ======
  
-There are several software interfaces available to monitor the status of the RECS<sup>(r)</sup>%%|%%Box system. These are the Management Web****GUI and a REST API providing XML based monitoring and management functionality. +There are several software interfaces available to monitor the status of the u.RECS system. These are the Management Web****GUI and a REST API providing XML based monitoring and management functionality. 
  
 ===== Management WebGUI ===== ===== Management WebGUI =====
  
-The Management Web****GUI is established on every RECS<sup>(r)</sup>%%|%%Box unit. Accessible by any known browser on the assigned IP address and the default port 80. The following views are dependent on the device and assembly.+The Management Web****GUI is established on every u.RECS unit. Accessible by any known browser on the assigned IP address and the default port 443. The following views are dependent on the device and assembly.
  
 In general these symbols have the following meaning on every page: \\ In general these symbols have the following meaning on every page: \\
Line 12: Line 12:
 |{{ :documentation:statuswarning.png?nolink |}} |Warnung. Something is wrong, but the system is still fully functional. The system has to be checked so the problem doesn't get worse. Indicated by a yellow line in a graph.| |{{ :documentation:statuswarning.png?nolink |}} |Warnung. Something is wrong, but the system is still fully functional. The system has to be checked so the problem doesn't get worse. Indicated by a yellow line in a graph.|
 |{{ :documentation:statuscritical.png?nolink |}} |Critical Error. The system must be checked immediately and maybe has to be shut down to prevent hardware damage. indicated by a red line in a graph.| |{{ :documentation:statuscritical.png?nolink |}} |Critical Error. The system must be checked immediately and maybe has to be shut down to prevent hardware damage. indicated by a red line in a graph.|
- 
-Figure 1 shows the first call of the Management Web****GUI. It is organized into three columns. The first is on the left-hand side and contains the following: 
- 
-[[documentation:software_interface#Overview|Overview:]] General overview of all managed RCU<sup></sup>s, RPU<sup></sup>s, installed nodes and health status\\ 
-[[documentation:software_interface#Management|Management:]] Selection of every managed RCU and RPU in the rack with a sensor view button for the Arneb\\ 
- 
-The second colum contains the buttons and sliders to manipulate the system. While the third colum is mostly for history information like power usage and temperature graphs. 
- 
  
 ==== Management ==== ==== Management ====
Line 35: Line 27:
 ==== Access ==== ==== Access ====
  
-The RECS<sup>(r)</sup>%%|%%Box Management API is accessible via the IP-Address or the hostname of the TOR-Master of the cluster. The basic URL of the API has the format ''https://TOR-Master/REST/'' or ''http://TOR-Master/REST/''.+The u.RECS Management API is accessible via the IP-Address or the hostname of the u.RECS. The basic URL of the API has the format ''https://[ip-address]/REST/[system/baseboard/node]'' with ''http'' or ''https'', depending on your configurationTherefore, please check the following URL as an example:
  
-Accessing the REST API requires HTTP Basic authentication. The authenticated user has to be in the "Admin" or "User" group to be able to execute the POST/PUT management calls.+https://192.168.0.50/REST/system 
 + 
 +Accessing the REST API requires HTTP Basic authentication. The password of the admin account can be changed in the Settings page.
  
 ==== Components ==== ==== Components ====
  
-The RECS<sup>(r)</sup>%%|%%Box Management API makes all hardware components in the cluster available as XML trees in software. The following components are supported by the API: \\+The u.RECS Management API makes all hardware components available as XML trees. The following components are supported by the API: \\
  
 ^ Attribute ^ Description ^ ^ Attribute ^ Description ^
Line 49: Line 43:
 |''lorawan''   | Enables communication to LoRaWAN application servers | |''lorawan''   | Enables communication to LoRaWAN application servers |
  
-Many resources also return lists of components. These are named according to the scheme <component name>List (e.g. nodeList, rcuList) and contain the elements of the list.+Many resources also return lists of components. These are named according to the scheme <component name>List (e.g. nodeList) and contain the elements of the list.
  
 === Node === === Node ===
Line 60: Line 54:
     actualNodePowerUsage="2.8" actualPowerUsage="2.8" state="0" lastPowerState="0" defaultPowerState="0"      actualNodePowerUsage="2.8" actualPowerUsage="2.8" state="0" lastPowerState="0" defaultPowerState="0" 
     rcuId="RCU_0" health="OK" lastSensorUpdate="1369" id="RCU_0_BB_1_0" present="true" bootDevice="0"/>     rcuId="RCU_0" health="OK" lastSensorUpdate="1369" id="RCU_0_BB_1_0" present="true" bootDevice="0"/>
-  <node maxPowerUsage="27" baseboardPosition="0" architecture="ARM + iGPU" baseboardId="RCU_0_BB_1" voltage="5.64" +  <node maxPowerUsage="27" baseboardPosition="1" architecture="ARM + iGPU" baseboardId="RCU_0_BB_1" voltage="5.64" 
     actualNodePowerUsage="2.8" actualPowerUsage="2.8" state="0" lastPowerState="0" defaultPowerState="0"      actualNodePowerUsage="2.8" actualPowerUsage="2.8" state="0" lastPowerState="0" defaultPowerState="0" 
     rcuId="RCU_0" health="OK" lastSensorUpdate="1369" id="RCU_0_BB_1_1" present="false" mpciePresent="false" forceRecovery="false"/>     rcuId="RCU_0" health="OK" lastSensorUpdate="1369" id="RCU_0_BB_1_1" present="false" mpciePresent="false" forceRecovery="false"/>
Line 70: Line 64:
 ^ Attribute ^ Description ^ Unit ^ Data type ^ ^ Attribute ^ Description ^ Unit ^ Data type ^
 |''id''|Unique ID for referencing the component|-|String| |''id''|Unique ID for referencing the component|-|String|
 +|''rcuId''|Unique ID for referencing the underlying RCU (for backwards compatibility to the RECS<sup>(r)</sup>%%|%%Box series|-|String|
 |''actualPowerUsage'' |Actual power consumption of a node (Node + PEG)|W|Double| |''actualPowerUsage'' |Actual power consumption of a node (Node + PEG)|W|Double|
 |''actualNodePowerUsage'' |Actual power consumption of a node (Node only)|W|Double| |''actualNodePowerUsage'' |Actual power consumption of a node (Node only)|W|Double|
-|''actualPEGPowerUsage'' |Actual power consumption of a PEG card|W|Double| 
 |''maxPowerUsage'' |Maximum power the node can draw|W|Integer| |''maxPowerUsage'' |Maximum power the node can draw|W|Integer|
 |''baseboardId'' |ID of the baseboard which hosts the node|-|String| |''baseboardId'' |ID of the baseboard which hosts the node|-|String|
 |''baseboardPosition'' |Position of the node on the baseboard|-|Integer| |''baseboardPosition'' |Position of the node on the baseboard|-|Integer|
-|''state'' |Power state of the node (0=Off, 1=On, 2=Soft-off, 3=Standby, 4=Hibernate)|-|Integer|+|''state'' |Actual power state of the node (0=Off, 1=On, 2=Soft-off, 3=Standby, 4=Hibernate)|-|Integer| 
 +|''lastPowerState'' |Last power state of the node which can be used to recover after a power failure (0=Off, 1=On, 2=Soft-off, 3=Standby, 4=Hibernate)|-|Integer| 
 +|''defaultPowerState'' |Defines the power-on-state in which the node should be after a power failure (0=Off, 1=On, 2=lastPowerState)|-|Integer|
 |''architecture'' |Architecture (x86, arm, UNKNOWN)|-|String| |''architecture'' |Architecture (x86, arm, UNKNOWN)|-|String|
 |''health'' |Health status of the node (OK, Warning, Critical)|-|String| |''health'' |Health status of the node (OK, Warning, Critical)|-|String|
-|''inletTemperature'' |Temperature of the inlet air|°C|Double| 
-|''outletTemperature'' |Temperature of the outlet air|°C|Double| 
-|''highestTemperature'' |Highest temperature measured on the node's baseboard|°C|Double| 
 |''voltage'' |Supply voltage of the baseboard|V|Double| |''voltage'' |Supply voltage of the baseboard|V|Double|
-|''lastSensorUpdate'' |Timestamp of the last sensor update|ms|Long| +|''lastSensorUpdate'' |Unix-style epoche timestamp of the last sensor update|s|Long| 
-|''macAddressCompute'' |MAC address of the NIC connected to the compute network (optional)|-|String+|''present'' |Node is plugged in and detected|-|Boolean
-|''macAddressMgmt'' |MAC address of the NIC connected to the management network (optional)|-|String|+|''mpciePresent'' |mPCIe card is plugged in and detected|-|Boolean| 
 +|''forceRecovery'' |Force Recovery pin is pulled high (only for Nvidia Jetson)|-|Boolean|
  
 In accordance to the component node the API offers nodeList which returns multiple instances of node. In accordance to the component node the API offers nodeList which returns multiple instances of node.
Line 95: Line 89:
 <code xml> <code xml>
 <baseboard serialNumber="90380CA9D524" rcuPosition="0" baseboardType="u.RECS" id="RCU_0_BB_1" lastSensorUpdate="358"  <baseboard serialNumber="90380CA9D524" rcuPosition="0" baseboardType="u.RECS" id="RCU_0_BB_1" lastSensorUpdate="358" 
-           rcuId="RCU_0" inputVoltage="22.93" boardVoltage1V0="1.97" boardVoltage1V2="2.05" boardVoltage1V5="2.05"  +    rcuId="RCU_0" inputVoltage="22.93" boardVoltage1V0="1.97" boardVoltage1V2="2.05" boardVoltage1V5="2.05"  
-           boardVoltage1V8="2.05" boardVoltage2V5="2.82" boardVoltage5V0="5.64" totalPowerUsage="11.24" usbPowerUsage="2.8"  +    boardVoltage1V8="2.05" boardVoltage2V5="2.82" boardVoltage5V0="5.64" totalPowerUsage="11.24" usbPowerUsage="2.8"  
-           mPciePowerUsage="1.64" m2PowerUsage="1.64" ethSwitchPowerUsage="0" poePowerUsagePort1="0" poePowerUsagePort2="0" +    mPciePowerUsage="1.64" m2PowerUsage="1.64" ethSwitchPowerUsage="0" poePowerUsagePort1="0" poePowerUsagePort2="0" 
-           regulatorsTemperature="0" ambientTemperature="0" fanSpeed="100" systemFan1Rpm="0" systemFan2Rpm="0" loraJoined="false" +    regulatorsTemperature="0" ambientTemperature="0" fanSpeed="100" systemFan1Rpm="0" systemFan2Rpm="0" loraJoined="false" 
-           loraJoinEui="0000000000000000" loraDevEui="1234561234561234" loraAppKey="01234567890123456789012345678901" +    loraJoinEui="0000000000000000" loraDevEui="1234561234561234" loraAppKey="01234567890123456789012345678901" 
-           loraVendorID="FFFF" loraVendorProfileID="0001" poeDetectionStatusPort1="0" poeDetectionStatusPort2="0" +    loraVendorID="FFFF" loraVendorProfileID="0001" loraRssi="16" poeDetectionStatusPort1="0" poeDetectionStatusPort2="0" 
-           firmwareVersion="0837db2"> +    firmwareVersion="0837db2"> 
-<nodeId>RCU_0_BB_1_0</nodeId> +  <nodeId>RCU_0_BB_1_0</nodeId> 
-<nodeId>RCU_0_BB_1_1</nodeId>+  <nodeId>RCU_0_BB_1_1</nodeId>
 </baseboard></code> </baseboard></code>
  
Line 110: Line 104:
 ^ Attribute ^ Description ^ Unit ^ Data type ^ ^ Attribute ^ Description ^ Unit ^ Data type ^
 |''id'' |Unique ID for referencing the component|-|String| |''id'' |Unique ID for referencing the component|-|String|
-|''rcuId'' |Unique ID of the RECS<sup>(r)</sup>%%|%%Box Computing Unit hosting the baseboard|-|String+|''inputVoltage'' |Voltage of the input power |Volt|Double| 
-|''rcuPosition'' |Position of the baseboard inside the RECS<sup>(r)</sup>%%|%%Box Computing Unit|-|Integer+|''boardVoltage1V0'' |Voltage of the internal 1.0V power rail |V|Double
-|''infrastructurePower'' |Power usage of the infrastructure components on the baseboard|W|Double| +|''boardVoltage1V2'' |Voltage of the internal 1.2V power rail |V|Double| 
-|''lastSensorUpdate'' |Timestamp of the last sensor update|ms|Long+|''boardVoltage1V5'' |Voltage of the internal 1.5V power rail |V|Double| 
-|''baseboardType'' |Type of the baseboard (CXP, APLS)|-|String+|''boardVoltage1V8'' |Voltage of the internal 1.8V power rail |V|Double| 
-|''nodeId'' |List of ID****s of the nodes installed on the baseboard|-|String+|''boardVoltage2V5'' |Voltage of the internal 2.5V power rail |V|Double| 
-|''temperatures'' |List of temperatures measured on the backplane|°C|Double+|''boardVoltage5V0'' |Voltage of the internal 5.0V power rail |V|Double| 
- +|''totalPowerUsage'' |Total power usage of the whole u.RECS, measured at the power plug |W|Double| 
-In accordance to the component baseboard the API offers baseboardList which returns multiple instances of baseboard.+|''usbPowerUsage'' |Power usage of the whole USB hub and external USB ports |W|Double
 +|''mPciePowerUsage'' |Power usage of the mPCIe card |W|Double| 
 +|''m2PowerUsage'' |Power usage of the top M.2 card |W|Double| 
 +|''ethSwitchPowerUsage'' |Power usage of the Ethernet switch |W|Double| 
 +|''poePowerUsagePort1'' |PoE power output at Eth Port 1 |W|Double| 
 +|''poePowerUsagePort2'' |PoE power output at Eth Port 2 |W|Double
 +|''regulatorsTemperature'' |Temperature of the main power regulators |°C|Integer| 
 +|''ambientTemperature'' |Ambient temperature |°C|Integer| 
 +|''fanSpeed'' |Set fan speed (0-100) |-|%
 +|''systemFan1Rpm'' |Rotational speed of system fan 1 |RPM|Integer| 
 +|''systemFan2Rpm'' |Rotational speed of system fan 1 |RPM|Integer| 
 +|''loraJoined'' |u.RECS successfully joined/connected to the TTN, which needs to be done after every reboot |-|Boolean| 
 +|''loraJoinEui'' |u.RECS LoRa Join EUI, always 0 for now |-|Integer | 
 +|''loraDevEui'' |u.RECS specific LoRa Dev EUI |-|Integer | 
 +|''loraAppKey'' |u.RECS specific, random generated LoRa App Key |-|Integer | 
 +|''loraVendorID'' |christmann specific LoRa vendor ID, FFFF for now |-|Hex | 
 +|''loraVendorProfileID'' |christmann u.RECS LoRa vendor profile ID, always 0001 |-|Hex 
 +|''loraRssi'' |Measured signal strength of the last recevied message (incl. join responses) |dBm|Integer 
 +|''poeDetectionStatusPort1'' |Raw status of the PoE detection status of Eth Port 1, please see [[doc_urecs:software_interface#poe_status_decoder|PoE status decoder]] on how to decode the status|-|Integer | 
 +|''poeDetectionStatusPort2'' |Raw status of the PoE detection status of Eth Port 2, please see [[doc_urecs:software_interface#poe_status_decoder|PoE status decoder]] on how to decode the status|-|Integer | 
 +|''firmwareVersion'' |Actual running firmware version |-|Hex |
  
 ==== System ==== ==== System ====
 +
 +The output of the ''/REST/system'' API call returns a combined output of ''/REST/node'' and ''/REST/baseboard'', which eases the monitoring of the whole system with one call.
  
 Example XML: Example XML:
Line 141: Line 157:
       actualNodePowerUsage="2.8" actualPowerUsage="2.8" state="0" lastPowerState="0" defaultPowerState="0" rcuId="RCU_0"        actualNodePowerUsage="2.8" actualPowerUsage="2.8" state="0" lastPowerState="0" defaultPowerState="0" rcuId="RCU_0" 
       health="OK" lastSensorUpdate="1403" id="RCU_0_BB_1_0" present="true" bootDevice="0"/>       health="OK" lastSensorUpdate="1403" id="RCU_0_BB_1_0" present="true" bootDevice="0"/>
-    <node maxPowerUsage="27" baseboardPosition="0" architecture="ARM + iGPU" baseboardId="RCU_0_BB_1" voltage="5.64" +    <node maxPowerUsage="27" baseboardPosition="1" architecture="ARM + iGPU" baseboardId="RCU_0_BB_1" voltage="5.64" 
       actualNodePowerUsage="2.8" actualPowerUsage="2.8" state="0" lastPowerState="0" defaultPowerState="0" rcuId="RCU_0"        actualNodePowerUsage="2.8" actualPowerUsage="2.8" state="0" lastPowerState="0" defaultPowerState="0" rcuId="RCU_0" 
       health="OK" lastSensorUpdate="1403" id="RCU_0_BB_1_1" present="false" mpciePresent="false" forceRecovery="false"/>       health="OK" lastSensorUpdate="1403" id="RCU_0_BB_1_1" present="false" mpciePresent="false" forceRecovery="false"/>
Line 159: Line 175:
 |''/node'' |Returns a nodeList with all nodes of the system|GET| |''/node'' |Returns a nodeList with all nodes of the system|GET|
 |''/baseboard'' |Returns an overview of the baseboard including the installed nodes|GET| |''/baseboard'' |Returns an overview of the baseboard including the installed nodes|GET|
-|''/system'' |Returns an overview of the baseboard and all nodes|GET|+|''/system'' |Returns an overview of the baseboard and all nodes. It includes all information of /baseboard and /node which makes it easy to get the whole system status with one REST call.|GET|
  
 === Management === === Management ===
Line 172: Line 188:
 |''/rcu/{rcu_id}/manage/set_fans'' |Sets the overall fan speed of the RCU with the given ID and returns the curent status of the RCU|PUT|percent={value}| |''/rcu/{rcu_id}/manage/set_fans'' |Sets the overall fan speed of the RCU with the given ID and returns the curent status of the RCU|PUT|percent={value}|
  
-=== LoRaWAN ===+=== LoRaWAN API ===
  
 The LoRaWAN interface allows up and downlink connections to an application server. The LoRaWAN interface allows up and downlink connections to an application server.
-Packets can be scheduled and collected by interfacing the Management REST API+Payload can be scheduled and collected by interfacing the Management REST API.
  
 ^ Attribute ^ Description ^ HTTP method ^ ^ Attribute ^ Description ^ HTTP method ^
-|''/lorawan/queue'' |Responds with incoming LoRaWAN packets linked to the API key in the request body XML|POST| +|''/lorawan/uplink/{fport}'' |Schedules uplink packet to the application endpoint for the specified fport|POST
-|''/lorawan/queue'' |Schedules uplink packet to the application server defined in the management interface XML|GET+|''/lorawan/downlink/{fport}'' |Responds with incoming downlink LoRaWAN messages for the specified fport|GET|
-|''/lorawan/manage'' |Manages LoRa PHY settings XML|GET|+
  
-Example XML queue GET / POST:+Example HTTP Body on GET request:
  
-<code xml><lorawan apikey="..."+<code xml><lorawan> 
-<packetbody>lora packet content in base64</packetbody>+  <payload>{custom lorawan payload}</payload> 
 +  <time>{timestamp}</time>
 </lorawan></code> </lorawan></code>
  
-Example XML manage:+Example HTTP Body on POST request:
  
-<code xml><lorawan masterkey="..."+<code xml><lorawan> 
-<band>eu</band> +  <payload>{custom lorawan payload}</payload>
-<txpwr>14</txpwr> +
-<txsf>7</txsf> +
-<rx2wsf>9</rx2wsf>+
 </lorawan></code> </lorawan></code>
  
-In order to remotely manage the RECS power status via LoRaWAN, the Application Server must send the downlink command payload in following format: +==== FPort ====
- +
-<code xml> +
-<l masterkey=""> +
-  <power>1</power> +
-<l> +
-</code> +
- +
-The master and API keys are managed in the RECS web interface.+
  
 +The Frame Port (fport) separates different communication parties on the API, and functions as an identifyer for the message sender / reciever.
 +When using the REST API, you are free to choose a value between 2 - 223.
 +FPort 1 is reserved for the management controller.
  
 === Errors === === Errors ===
  
 Information about the success or failure of management requests are returned via HTTP status codes. Please have a look at [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html|RFC2616]] for an overview about the defined HTTP status codes. Information about the success or failure of management requests are returned via HTTP status codes. Please have a look at [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html|RFC2616]] for an overview about the defined HTTP status codes.
 +
 +==== LoRa Message ====
 +
 +The u.RECS supports upstream and downstream LoRa messages to [[https://www.thethingsnetwork.org/|The Things Network (TTN)]]. The following table gives the LoRa message meaning of version 0.
 +
 +All system related management communication (excluding the REST API) uses **FPort 1**. 
 +
 +Upstream message payload layout:
 +
 +^ Byte(s) ^ Description ^ Unit ^ Data type ^
 +|0   | u.RECS Lora Message-Version | - | Byte |
 +|1   | Node Info Smarc/Jetson, Bits: present_smarc / present_jetson / on_smarc  / on_jetson | - | 2 x 2 Bits |
 +|2   | Regulators temperature:      | °C | Byte (-127..+127) |
 +|3   | Ambient temperature:      | °C | Byte (-127..+127) |
 +|4-5 | System fan 1:        | RPM | Unsigned Short (0..65535) |
 +|6-7 | System fan 2: | RPM | Unsigned Short (0..65535) |
 +|8-9 | Smarc Power Usage:    | mW  | Unsigned Short (0..65535) |
 +|10-11 | Jetson Power Usage:    | mW  | Unsigned Short (0..65535) |
 +|12-13 | u.RECS Power Usage: | mW  | Unsigned Short (0..65535) |
 +|14-15 | USB Power Usage: | mW  | Unsigned Short (0..65535) |
 +|16-17 | mPCIe Power Usage: | mW  | Unsigned Short (0..65535) |
 +|18-19 | M.2 Power Usage: | mW  | Unsigned Short (0..65535) |
 +|20-21 | Ethernet Switch Power Usage: | mW  | Unsigned Short (0..65535) |
 +|22-23 | PoE Eth Port 1 Power Usage: | mW  | Unsigned Short (0..65535) |
 +|24-25 | PoE Eth Port 2 Power Usage: | mW  | Unsigned Short (0..65535) |
 +|26    | PoE Status Port 1 | - (see below) | Byte |
 +|27    | PoE Status Port 2 | - (see below) | Byte |
 +
 +The u.RECS supports basic control functions over LoRaWAN.
 +Downstream message payloads:
 +
 +**Change power state for node:**
 +^ Byte(s) ^ Description ^ Unit ^ Data type ^
 +|0   | Lora Message-Version | - | Byte |
 +|1   | Node ID | - | Byte |
 +|2   | LoRa Command (0x01 = ON, 0x02 = OFF, 0x03 = RESET) | - | Byte |
 +
 +
 +
 +
 +
 +==== PoE status decoder ====
 +
 +Here is some C-Code to decode the PoE Status:
 +
 +<code c>
 +PoE Status Bytes:
 +switch (status & 0xF) {
 +  case 0: ret = "Detection unknown"; break;
 +  case 1: ret = "Short circuit"; break;
 +  case 2: ret = "Capacitive"; break;
 +  case 3: ret = "RLOW"; break;
 +  case 4: ret = "RGOOD"; break;
 +  case 5: ret = "RHIGH"; break;
 +  case 6: ret = "Open circuit"; break;
 +  case 7: ret = "PSE to PSE"; break;
 +  case 15: ret = "MOSFET fault"; break;
 +}
 +
 +if ((status & 0xF) == 4) { // RGOOD
 +  switch ((status >> 4) & 0xF) {
 +    case 0: ret += ", class unknown"; break;
 +    case 1: ret += ", class 1"; break;
 +    case 2: ret += ", class 2"; break;
 +    case 3: ret += ", class 3"; break;
 +    case 4: ret += ", class 4"; break;
 +    case 6: ret += ", class 0"; break;
 +    case 7: ret += ", overcurrent"; break;
 +    case 8: ret += ", class 5 4P single signature"; break;
 +    case 12: ret += ", class 4 type 1 limited"; break;
 +    case 13: ret += ", class 5 legacy"; break;
 +    case 15: ret += ", class mismatch"; break;
 +}
 +</code>