View Javadoc
1   package org.metricshub.ipmi.core.coding.payload.lan;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * IPMI Java Client
6    * ჻჻჻჻჻჻
7    * Copyright 2023 Verax Systems, MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import org.metricshub.ipmi.core.coding.payload.CompletionCode;
26  
27  public class IPMIException extends Exception {
28  
29  	private static final long serialVersionUID = 1L;
30  
31  	/** First command-specific code (IPMI 2.0 Table 5-2): 01h-7Eh are OEM, 80h-BEh command-specific. */
32  	private static final int COMMAND_SPECIFIC_CODES_START = 0x80;
33  
34  	private static final int GENERIC_CODES_START = 0xC0;
35  
36  	private final CompletionCode completionCode;
37  
38  	private final int rawCode;
39  
40  	/** The meaning of the code for the command that got it, or null. */
41  	private final String description;
42  
43  	public IPMIException(CompletionCode completionCode) {
44  		this(completionCode, completionCode.getCode());
45  	}
46  
47  	/**
48  	 * @param completionCode the decoded completion code, {@link CompletionCode#Unknown} for a code the library
49  	 *        does not list
50  	 * @param rawCode the completion code byte as the BMC sent it
51  	 */
52  	public IPMIException(CompletionCode completionCode, int rawCode) {
53  		this(completionCode, rawCode, null);
54  	}
55  
56  	/**
57  	 * Creates the exception for a code whose meaning depends on the command: an OEM or command-specific code that
58  	 * {@link CompletionCode} does not list.
59  	 *
60  	 * @param completionCode the decoded completion code, {@link CompletionCode#Unknown} for a code the library
61  	 *        does not list
62  	 * @param rawCode the completion code byte as the BMC sent it
63  	 * @param description the meaning of the code for the command that got it, returned by {@link #getMessage()}, or
64  	 *        null for the generic text
65  	 */
66  	public IPMIException(CompletionCode completionCode, int rawCode, String description) {
67  		this.completionCode = completionCode;
68  		this.rawCode = rawCode;
69  		this.description = description;
70  	}
71  
72  	/**
73  	 * @return whether the code is one Table 5-2 reserves: 7Fh, BFh, D7h-FEh
74  	 */
75  	private static boolean isReserved(int rawCode) {
76  		return rawCode == 0x7f || rawCode == 0xbf || (rawCode >= 0xd7 && rawCode <= 0xfe);
77  	}
78  
79  	public CompletionCode getCompletionCode() {
80  		return completionCode;
81  	}
82  
83  	/**
84  	 * @return the completion code byte as the BMC sent it, meaningful when {@link #getCompletionCode()} is
85  	 *         {@link CompletionCode#Unknown}
86  	 */
87  	public int getRawCode() {
88  		return rawCode;
89  	}
90  
91  	@Override
92  	public String getMessage() {
93  		if (description != null) {
94  			return description;
95  		}
96  		if (completionCode == CompletionCode.Unknown && rawCode >= 0) {
97  			String kind = isReserved(rawCode) ?
98  					"Reserved" :
99  					rawCode < COMMAND_SPECIFIC_CODES_START ?
100 							"OEM" :
101 							rawCode < GENERIC_CODES_START ? "Command-specific" : "Generic";
102 			return String.format("%s completion code %02Xh.", kind, rawCode);
103 		}
104 		return completionCode.getMessage();
105 	}
106 
107 	/**
108 	 * Describes the error reply as a sentence without its final period: the message, then the completion code in
109 	 * hexadecimal, named once.
110 	 *
111 	 * @return the description, {@code Requested Sensor, data, or record not present (completion code CBh)} or
112 	 *         {@code Command-specific completion code 81h} for instance
113 	 */
114 	public String describe() {
115 		if (rawCode < 0 || (description == null && completionCode == CompletionCode.Unknown)) {
116 			// No code to name, or the message already names it
117 			return withoutPeriod(getMessage());
118 		}
119 		return describe(getMessage(), rawCode);
120 	}
121 
122 	/**
123 	 * Describes a completion code as a sentence without its final period: its meaning, then the code in hexadecimal.
124 	 *
125 	 * @param meaning the meaning of the code, with or without a final period
126 	 * @param rawCode the completion code byte
127 	 * @return the description, {@code Invalid session handle (completion code 80h)} for instance
128 	 */
129 	public static String describe(String meaning, int rawCode) {
130 		return withoutPeriod(meaning) + String.format(" (completion code %02Xh)", rawCode);
131 	}
132 
133 	private static String withoutPeriod(String sentence) {
134 		return sentence.endsWith(".") ? sentence.substring(0, sentence.length() - 1) : sentence;
135 	}
136 }