Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#13 - Added BatterySystem service + a bunch of new Bat Tags. #16

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import java.time.format.DateTimeFormatter
* @param value The actual value
*
* @since 2.0
* @since 2.1 new function:
* - [valueAsErrorCode]
*/
data class Data(
val tag: ByteArray,
Expand Down Expand Up @@ -272,15 +274,25 @@ data class Data(
value

/**
* Liest den Int wert des Datenblocks und wandelt ihn in einen [ResultCode]
* Reads the Int value of the data block and converts it into a [ResultCode].
*
* @return [ResultCode] Wert
* @return [ResultCode] value
*
* @since 2.0
*/
fun valueAsResultCode() =
if (typeObject() == DataType.ERROR) ResultCode.UNKNOWN else ResultCode.byRscpCode(valueAsInt()?:ResultCode.UNKNOWN.rscpCode)

/**
* Reads the Int value of the data block and converts it into a [ErrorCode].
*
* @return [ErrorCode] Value
*
* @since 2.1
*/
fun valueAsErrorCode() =
if (typeObject() == DataType.ERROR) ResultCode.UNKNOWN else ErrorCode.byRscpCode(valueAsInt() ?: ErrorCode.UNKNOWN.rscpCode.toInt())

/**
* Checks if the response type is of type error.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package de.jnkconsulting.e3dc.easyrscp.api.frame

/**
* Possible error codes that can be in data blocks of type [DataType.ERROR]
*
* @param rscpCode Code in the data block that identifies the error type
*
* @since 2.1
*/
enum class ErrorCode(val rscpCode: Byte) {
/**
* Empty data block. Usually used for request frames; code = 0x00
*/
NOT_HANDLED(rscpCode = 0x00.toByte()),
/**
* Boolean typ. 1 for true, 0 for false; code = 0x01
*/
ACCESS_DENIED(rscpCode = 0x01.toByte()),

/**
* Contains a 1byte number; code = 0x02
*/
FORMAT(rscpCode = 0x02.toByte()),
/**
* Contains a 1byte number (unsigned); code = 0x03
*/
AGAIN(rscpCode = 0x03.toByte()),

UNKNOWN(rscpCode = 0xFF.toByte());


companion object {

/**
* Returns the [ErrorCode] object for the given code.
*
* If the code is not known, [UNKNOWN] is supplied.
*
* @return [ErrorCode] object to the given code
*
* @since 2.1
*/
fun byRscpCode(code: Int) =
entries
.find { it.rscpCode.toInt() == code }
?: UNKNOWN

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ import java.time.Instant
* Data, the RSCP data itself.
*
* @param timestamp Creation time of the frame
* @param controlBytes Control functions. Currently the protocol version as well as a control bit whether checksum are used or not. See [FIXED_VALUES].
* @param controlBytes Control functions. Currently, the protocol version as well as a control bit whether checksum are used or not. See [FIXED_VALUES].
* @param data The data itself. See [Data]
* @param parser The parser to use for parsing container data
*
* @since 2.0
* @since 2.1 new functions:
* - [isDataBlockInError]
* - [errorCodeByTag]
*/
data class Frame(
val timestamp: Instant,
Expand Down Expand Up @@ -280,6 +283,36 @@ data class Frame(
find(tag, data, *containerPath)
?.valueAsResultCode() ?: ResultCode.UNKNOWN

/**
* Checks whether the data block in the frame is of data type [DataType.ERROR].
*
* @param tag The [Tag] to search for
* @param containerPath Optional path through datablocks of type [DataType.CONTAINER].
*
* @return true if the data block is of type [DataType.ERROR], otherwise false
*
* @since 2.1
*/
fun isDataBlockInError(tag: Tag, vararg containerPath: Tag) =
find(tag, data, *containerPath)
?.isErrorResponse() ?: false

/**
* Searches for a data block of type [tag] and returns the value as [ErrorCode].
*
* If the block is not found [ErrorCode.UNKNOWN] is returned.
*
* @param tag The [Tag] to search for
* @param containerPath Optional path through datablocks of type [DataType.CONTAINER].
*
* @return Value of the data block as [ErrorCode] or [ErrorCode.UNKNOWN] if the data block does not exist.
*
* @since 2.1
*/
fun errorCodeByTag(tag: Tag, vararg containerPath: Tag) =
find(tag, data, *containerPath)
?.valueAsErrorCode() ?: ErrorCode.UNKNOWN

/**
* Query whether the frame calculates the checksum or not.
*
Expand Down
1,580 changes: 1,435 additions & 145 deletions api/src/main/kotlin/de/jnkconsulting/e3dc/easyrscp/api/frame/tags/BatTag.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.jnkconsulting.e3dc.easyrscp.api.service

import de.jnkconsulting.e3dc.easyrscp.api.service.model.BatterySpec
import de.jnkconsulting.e3dc.easyrscp.api.service.model.BatteryStatus
import de.jnkconsulting.e3dc.easyrscp.api.service.model.PowerState

/**
* Service to query the battery specification and status data
*
* @since 2.1
*/
interface BatteryService {

/**
* Liest die Batteriespezifikation aus dem E3DC Hauskraftwerk
*
* @return Specification of the battery. As a rule, the list contains only one element. Theoretically, however, a home power station can have several batteries
*
* @since 2.1
*/
fun readSpecification(): List<BatterySpec>

/**
* Reads the current status data of the battery
*
* @return Current monitoring data. As a rule, the list contains only one element. Theoretically, however, a home power station can have several batteries
*
* @since 2.1
*/
fun readMonitoringData(): List<BatteryStatus>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package de.jnkconsulting.e3dc.easyrscp.api.service.model

/**
* Contains the specification data of the battery
*
* @param index ID of the battery in the E3DC system
* @param name Name of the battery stored in the E3DC system
* @param maxChargingTempCelsius Maximum possible charging temperature in Celsius. fyi: I don't know why the temperature is an Int value for the battery and a float for the DCB module ... BUT, it is like this in the protocol
* @param minChargingTempCelsius Maximum possible discharging temperature in Celsius. fyi: I don't know why the temperature is an Int value for the battery and a float for the DCB module ... BUT, it is like this in the protocol
* @param voltage Voltage for which the battery was specified in volts
* @param capacityAh Capacity in Ah. Supplied by the system and NOT calculated. May therefore deviate slightly from the theoretically calculated value
* @param capacityWh Kapazität in Wh. Supplied by the system and NOT calculated. May therefore deviate slightly from the theoretically calculated value
* @param maxChargeCurrentA Maximum charging current in A
* @param maxDischargeCurrentA Maximum discharging current in A
* @param dcbSpecs List of specifications for the individual modules
*
* @since 2.1
*/
data class BatterySpec(
val index: Short,
val name: String,
val maxChargingTempCelsius: Int,
val minChargingTempCelsius: Int,
val voltage: Float,
val capacityAh: Float,
val capacityWh: Percentage,
val maxChargeCurrentA: Float,
val maxDischargeCurrentA: Float,
val dcbSpecs: List<DCBSpec>
)

/**
* Contains the specification of a battery module
*
* @param index ID of the module in the battery
* @param capacityAh Capacity of the module in Ah. Supplied by the system and NOT calculated. May therefore deviate slightly from the theoretically calculated value
* @param maxChargeCurrentA Maximum charging current in A
* @param maxDischargeCurrentA Maximum discharging current in A
* @param fullChargeCapacityAh Capacity of the module in Ah, from when the system displays 100% charge status
* @param voltage Voltage for which the module was specified in volts
* @param maxChargingTempCelsius Maximum possible charging temperature in Celsius. . fyi: I don't know why the temperature is an Int value for the battery and a float for the DCB module ... BUT, it is like this in the protocol
* @param minChargingTempCelsius Maximum possible discharging temperature in Celsius. . fyi: I don't know why the temperature is an Int value for the battery and a float for the DCB module ... BUT, it is like this in the protocol
* @param serialCells Series-connected cells of the module
* @param parallelCells Cells of the module connected in parallel
*
* @since 2.1
*/
data class DCBSpec(
val index: Int,
val capacityAh: Float,
val maxChargeCurrentA: Float,
val maxDischargeCurrentA: Float,
val fullChargeCapacityAh: Float,
val voltage: Float,
val maxChargingTempCelsius: Float,
val minChargingTempCelsius: Float,
val serialCells: Int,
val parallelCells: Int,
)

/**
* Contains information on the current status of the battery
*
* @param index ID of the battery in the E3DC system
* @param trainingModeActive Information on whether the battery is in training mode
* @param connected Information on whether the battery is connected to the system (battery disconnect switch on the home power station)
* @param working Information on whether the battery is working correctly
* @param inService Information on whether the battery is in maintenance mode
* @param asoc Percentage value that includes the condition of the battery taking ageing into account. A new battery theoretically has a value of 1.0 (100%)
* @param realRsoc Current charge level as a percentage of the battery without taking into account the reserve for absolute deep discharge and theoretically possible full charge. The value should therefore never reach completely 0 and never completely 1
* @param voltage Current battery voltage
* @param dcbStatus Status of the individual battery modules
*
* @since 2.1
*/
data class BatteryStatus(
val index: Short,
val trainingModeActive: Boolean,
val connected: Boolean,
val working: Boolean,
val inService: Boolean,
val asoc: Float,
val realRsoc: Float,
val voltage: Float,
val dcbStatus: List<DCBStatus>
)

/**
* Contains current status information on a battery module
*
* @param index ID of the module in the battery
* @param voltage Current voltage
* @param voltageAVG30s Average voltage of the last 30 seconds
* @param currentA Current amperage in A
* @param currentAVG30s Average current in the last 30 seconds
* @param temperaturesCelsius Current temperature values of the respective sensors in Celsius
*
* @since 2.1
*/
data class DCBStatus(
val index: Int,
val voltage: Float,
val voltageAVG30s: Float,
val currentA: Float,
val currentAVG30s: Float,
val temperaturesCelsius: List<Float>
)
Loading