commit
f40450548f
5
.editorconfig
Normal file
5
.editorconfig
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
|
||||||
|
[*{kt,kts}]
|
||||||
|
disabled_rules=no-wildcard-imports
|
|
@ -22,6 +22,7 @@
|
||||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="6" />
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="6" />
|
||||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="6" />
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="6" />
|
||||||
<option name="BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES" value="1" />
|
<option name="BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES" value="1" />
|
||||||
|
<option name="WRAP_EXPRESSION_BODY_FUNCTIONS" value="1" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<codeStyleSettings language="JAVA">
|
<codeStyleSettings language="JAVA">
|
||||||
<option name="METHOD_ANNOTATION_WRAP" value="0" />
|
<option name="METHOD_ANNOTATION_WRAP" value="0" />
|
||||||
|
@ -140,14 +141,28 @@
|
||||||
</arrangement>
|
</arrangement>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="kotlin">
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="RIGHT_MARGIN" value="120" />
|
||||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||||
<option name="LINE_COMMENT_ADD_SPACE" value="true" />
|
<option name="LINE_COMMENT_ADD_SPACE" value="true" />
|
||||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
||||||
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
|
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
|
||||||
|
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||||
|
<option name="CALL_PARAMETERS_WRAP" value="5" />
|
||||||
|
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||||
|
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||||
|
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
|
||||||
|
<option name="ASSIGNMENT_WRAP" value="5" />
|
||||||
<option name="METHOD_ANNOTATION_WRAP" value="5" />
|
<option name="METHOD_ANNOTATION_WRAP" value="5" />
|
||||||
<option name="FIELD_ANNOTATION_WRAP" value="0" />
|
<option name="CLASS_ANNOTATION_WRAP" value="1" />
|
||||||
|
<option name="FIELD_ANNOTATION_WRAP" value="1" />
|
||||||
|
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
|
||||||
|
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
|
||||||
|
<option name="ENUM_CONSTANTS_WRAP" value="5" />
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
|
|
10
build.gradle
10
build.gradle
|
@ -44,9 +44,10 @@ buildscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2
|
maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2
|
||||||
|
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||||
classpath 'com.google.gms:google-services:4.3.4'
|
classpath 'com.google.gms:google-services:4.3.4'
|
||||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
|
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
|
||||||
|
|
||||||
|
@ -58,6 +59,11 @@ buildscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "io.gitlab.arturbosch.detekt" version "1.16.0-RC2"
|
||||||
|
id "org.jlleitschuh.gradle.ktlint" version "9.4.1"
|
||||||
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
@ -78,6 +84,8 @@ allprojects {
|
||||||
includeModule("org.jetbrains.trove4j", "trove4j")
|
includeModule("org.jetbrains.trove4j", "trove4j")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
|
||||||
|
|
||||||
}
|
}
|
||||||
//Support @JvmDefault
|
//Support @JvmDefault
|
||||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
||||||
|
|
|
@ -2,6 +2,8 @@ apply plugin: 'com.android.library'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'com.hiya.jacoco-android'
|
apply plugin: 'com.hiya.jacoco-android'
|
||||||
|
apply plugin: "io.gitlab.arturbosch.detekt" // TODO move to `subprojects` section in global build.gradle
|
||||||
|
apply plugin: "org.jlleitschuh.gradle.ktlint" // TODO move to `subprojects` section in global build.gradle
|
||||||
|
|
||||||
apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
|
apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
|
||||||
apply from: "${project.rootDir}/gradle/android_module_dependencies.gradle"
|
apply from: "${project.rootDir}/gradle/android_module_dependencies.gradle"
|
||||||
|
@ -14,6 +16,11 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detekt { // TODO move to `subprojects` section in global build.gradle
|
||||||
|
toolVersion = "1.15.0-RC2"
|
||||||
|
config = files("./detekt-config.yml") // TODO move to global space and use "../detekt-config.yml"
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':core')
|
implementation project(':core')
|
||||||
implementation project(':omnipod-common')
|
implementation project(':omnipod-common')
|
||||||
|
|
690
omnipod-dash/detekt-config.yml
Normal file
690
omnipod-dash/detekt-config.yml
Normal file
|
@ -0,0 +1,690 @@
|
||||||
|
build:
|
||||||
|
maxIssues: 0
|
||||||
|
excludeCorrectable: false
|
||||||
|
weights:
|
||||||
|
# complexity: 2
|
||||||
|
# LongParameterList: 1
|
||||||
|
# style: 1
|
||||||
|
# comments: 1
|
||||||
|
|
||||||
|
config:
|
||||||
|
validation: true
|
||||||
|
warningsAsErrors: false
|
||||||
|
# when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
|
||||||
|
excludes: ''
|
||||||
|
|
||||||
|
processors:
|
||||||
|
active: true
|
||||||
|
exclude:
|
||||||
|
- 'DetektProgressListener'
|
||||||
|
# - 'FunctionCountProcessor'
|
||||||
|
# - 'PropertyCountProcessor'
|
||||||
|
# - 'ClassCountProcessor'
|
||||||
|
# - 'PackageCountProcessor'
|
||||||
|
# - 'KtFileCountProcessor'
|
||||||
|
|
||||||
|
console-reports:
|
||||||
|
active: true
|
||||||
|
exclude:
|
||||||
|
- 'ProjectStatisticsReport'
|
||||||
|
- 'ComplexityReport'
|
||||||
|
- 'NotificationReport'
|
||||||
|
# - 'FindingsReport'
|
||||||
|
- 'FileBasedFindingsReport'
|
||||||
|
|
||||||
|
output-reports:
|
||||||
|
active: true
|
||||||
|
exclude:
|
||||||
|
# - 'TxtOutputReport'
|
||||||
|
# - 'XmlOutputReport'
|
||||||
|
# - 'HtmlOutputReport'
|
||||||
|
|
||||||
|
comments:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
AbsentOrWrongFileLicense:
|
||||||
|
active: false
|
||||||
|
licenseTemplateFile: 'license.template'
|
||||||
|
CommentOverPrivateFunction:
|
||||||
|
active: false
|
||||||
|
CommentOverPrivateProperty:
|
||||||
|
active: false
|
||||||
|
EndOfSentenceFormat:
|
||||||
|
active: false
|
||||||
|
endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
|
||||||
|
UndocumentedPublicClass:
|
||||||
|
active: false
|
||||||
|
searchInNestedClass: true
|
||||||
|
searchInInnerClass: true
|
||||||
|
searchInInnerObject: true
|
||||||
|
searchInInnerInterface: true
|
||||||
|
UndocumentedPublicFunction:
|
||||||
|
active: false
|
||||||
|
UndocumentedPublicProperty:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
complexity:
|
||||||
|
active: true
|
||||||
|
ComplexCondition:
|
||||||
|
active: true
|
||||||
|
threshold: 4
|
||||||
|
ComplexInterface:
|
||||||
|
active: false
|
||||||
|
threshold: 10
|
||||||
|
includeStaticDeclarations: false
|
||||||
|
includePrivateDeclarations: false
|
||||||
|
ComplexMethod:
|
||||||
|
active: true
|
||||||
|
threshold: 15
|
||||||
|
ignoreSingleWhenExpression: false
|
||||||
|
ignoreSimpleWhenEntries: false
|
||||||
|
ignoreNestingFunctions: false
|
||||||
|
nestingFunctions: [run, let, apply, with, also, use, forEach, isNotNull, ifNull]
|
||||||
|
LabeledExpression:
|
||||||
|
active: false
|
||||||
|
ignoredLabels: []
|
||||||
|
LargeClass:
|
||||||
|
active: true
|
||||||
|
threshold: 600
|
||||||
|
LongMethod:
|
||||||
|
active: true
|
||||||
|
threshold: 60
|
||||||
|
LongParameterList:
|
||||||
|
active: true
|
||||||
|
functionThreshold: 6
|
||||||
|
constructorThreshold: 7
|
||||||
|
ignoreDefaultParameters: false
|
||||||
|
ignoreDataClasses: true
|
||||||
|
ignoreAnnotated: []
|
||||||
|
MethodOverloading:
|
||||||
|
active: false
|
||||||
|
threshold: 6
|
||||||
|
NamedArguments:
|
||||||
|
active: false
|
||||||
|
threshold: 3
|
||||||
|
NestedBlockDepth:
|
||||||
|
active: true
|
||||||
|
threshold: 4
|
||||||
|
ReplaceSafeCallChainWithRun:
|
||||||
|
active: false
|
||||||
|
StringLiteralDuplication:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
threshold: 3
|
||||||
|
ignoreAnnotation: true
|
||||||
|
excludeStringsWithLessThan5Characters: true
|
||||||
|
ignoreStringsRegex: '$^'
|
||||||
|
TooManyFunctions:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
thresholdInFiles: 11
|
||||||
|
thresholdInClasses: 11
|
||||||
|
thresholdInInterfaces: 11
|
||||||
|
thresholdInObjects: 11
|
||||||
|
thresholdInEnums: 11
|
||||||
|
ignoreDeprecated: false
|
||||||
|
ignorePrivate: false
|
||||||
|
ignoreOverridden: false
|
||||||
|
|
||||||
|
coroutines:
|
||||||
|
active: true
|
||||||
|
GlobalCoroutineUsage:
|
||||||
|
active: false
|
||||||
|
RedundantSuspendModifier:
|
||||||
|
active: false
|
||||||
|
SuspendFunWithFlowReturnType:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
empty-blocks:
|
||||||
|
active: true
|
||||||
|
EmptyCatchBlock:
|
||||||
|
active: true
|
||||||
|
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||||
|
EmptyClassBlock:
|
||||||
|
active: true
|
||||||
|
EmptyDefaultConstructor:
|
||||||
|
active: true
|
||||||
|
EmptyDoWhileBlock:
|
||||||
|
active: true
|
||||||
|
EmptyElseBlock:
|
||||||
|
active: true
|
||||||
|
EmptyFinallyBlock:
|
||||||
|
active: true
|
||||||
|
EmptyForBlock:
|
||||||
|
active: true
|
||||||
|
EmptyFunctionBlock:
|
||||||
|
active: true
|
||||||
|
ignoreOverridden: false
|
||||||
|
EmptyIfBlock:
|
||||||
|
active: true
|
||||||
|
EmptyInitBlock:
|
||||||
|
active: true
|
||||||
|
EmptyKtFile:
|
||||||
|
active: true
|
||||||
|
EmptySecondaryConstructor:
|
||||||
|
active: true
|
||||||
|
EmptyTryBlock:
|
||||||
|
active: true
|
||||||
|
EmptyWhenBlock:
|
||||||
|
active: true
|
||||||
|
EmptyWhileBlock:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
exceptions:
|
||||||
|
active: true
|
||||||
|
ExceptionRaisedInUnexpectedLocation:
|
||||||
|
active: false
|
||||||
|
methodNames: [toString, hashCode, equals, finalize]
|
||||||
|
InstanceOfCheckForException:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
NotImplementedDeclaration:
|
||||||
|
active: false
|
||||||
|
PrintStackTrace:
|
||||||
|
active: false
|
||||||
|
RethrowCaughtException:
|
||||||
|
active: false
|
||||||
|
ReturnFromFinally:
|
||||||
|
active: false
|
||||||
|
ignoreLabeled: false
|
||||||
|
SwallowedException:
|
||||||
|
active: false
|
||||||
|
ignoredExceptionTypes:
|
||||||
|
- InterruptedException
|
||||||
|
- NumberFormatException
|
||||||
|
- ParseException
|
||||||
|
- MalformedURLException
|
||||||
|
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||||
|
ThrowingExceptionFromFinally:
|
||||||
|
active: false
|
||||||
|
ThrowingExceptionInMain:
|
||||||
|
active: false
|
||||||
|
ThrowingExceptionsWithoutMessageOrCause:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
exceptions:
|
||||||
|
- IllegalArgumentException
|
||||||
|
- IllegalStateException
|
||||||
|
- IOException
|
||||||
|
ThrowingNewInstanceOfSameException:
|
||||||
|
active: false
|
||||||
|
TooGenericExceptionCaught:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
exceptionNames:
|
||||||
|
- ArrayIndexOutOfBoundsException
|
||||||
|
- Error
|
||||||
|
- Exception
|
||||||
|
- IllegalMonitorStateException
|
||||||
|
- NullPointerException
|
||||||
|
- IndexOutOfBoundsException
|
||||||
|
- RuntimeException
|
||||||
|
- Throwable
|
||||||
|
allowedExceptionNameRegex: '_|(ignore|expected).*'
|
||||||
|
TooGenericExceptionThrown:
|
||||||
|
active: true
|
||||||
|
exceptionNames:
|
||||||
|
- Error
|
||||||
|
- Exception
|
||||||
|
- Throwable
|
||||||
|
- RuntimeException
|
||||||
|
|
||||||
|
formatting:
|
||||||
|
active: true
|
||||||
|
android: false
|
||||||
|
autoCorrect: true
|
||||||
|
AnnotationOnSeparateLine:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
AnnotationSpacing:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
ArgumentListWrapping:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
ChainWrapping:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
CommentSpacing:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
EnumEntryNameCase:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
Filename:
|
||||||
|
active: true
|
||||||
|
FinalNewline:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
insertFinalNewLine: true
|
||||||
|
ImportOrdering:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
layout: 'idea'
|
||||||
|
Indentation:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
indentSize: 4
|
||||||
|
continuationIndentSize: 4
|
||||||
|
MaximumLineLength:
|
||||||
|
active: true
|
||||||
|
maxLineLength: 150
|
||||||
|
ModifierOrdering:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
MultiLineIfElse:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoBlankLineBeforeRbrace:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoConsecutiveBlankLines:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoEmptyClassBody:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoEmptyFirstLineInMethodBlock:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
NoLineBreakAfterElse:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoLineBreakBeforeAssignment:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoMultipleSpaces:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoSemicolons:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoTrailingSpaces:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoUnitReturn:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoUnusedImports:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
NoWildcardImports:
|
||||||
|
active: true
|
||||||
|
PackageName:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
ParameterListWrapping:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
indentSize: 4
|
||||||
|
SpacingAroundColon:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingAroundComma:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingAroundCurly:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingAroundDot:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingAroundDoubleColon:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingAroundKeyword:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingAroundOperators:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingAroundParens:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingAroundRangeOperator:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingBetweenDeclarationsWithAnnotations:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
SpacingBetweenDeclarationsWithComments:
|
||||||
|
active: false
|
||||||
|
autoCorrect: true
|
||||||
|
StringTemplate:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
|
||||||
|
naming:
|
||||||
|
active: true
|
||||||
|
ClassNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
classPattern: '[A-Z][a-zA-Z0-9]*'
|
||||||
|
ConstructorParameterNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
parameterPattern: '[a-z][A-Za-z0-9]*'
|
||||||
|
privateParameterPattern: '[a-z][A-Za-z0-9]*'
|
||||||
|
excludeClassPattern: '$^'
|
||||||
|
ignoreOverridden: true
|
||||||
|
EnumNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
|
||||||
|
ForbiddenClassName:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
forbiddenName: []
|
||||||
|
FunctionMaxLength:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
maximumFunctionNameLength: 30
|
||||||
|
FunctionMinLength:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
minimumFunctionNameLength: 3
|
||||||
|
FunctionNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)'
|
||||||
|
excludeClassPattern: '$^'
|
||||||
|
ignoreOverridden: true
|
||||||
|
ignoreAnnotated: ['Composable']
|
||||||
|
FunctionParameterNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
parameterPattern: '[a-z][A-Za-z0-9]*'
|
||||||
|
excludeClassPattern: '$^'
|
||||||
|
ignoreOverridden: true
|
||||||
|
InvalidPackageDeclaration:
|
||||||
|
active: false
|
||||||
|
rootPackage: ''
|
||||||
|
MatchingDeclarationName:
|
||||||
|
active: true
|
||||||
|
mustBeFirst: true
|
||||||
|
MemberNameEqualsClassName:
|
||||||
|
active: true
|
||||||
|
ignoreOverridden: true
|
||||||
|
NonBooleanPropertyPrefixedWithIs:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
ObjectPropertyNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
constantPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||||
|
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||||
|
privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
|
||||||
|
PackageNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
|
||||||
|
TopLevelPropertyNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
constantPattern: '[A-Z][_A-Z0-9]*'
|
||||||
|
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
|
||||||
|
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
|
||||||
|
VariableMaxLength:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
maximumVariableNameLength: 64
|
||||||
|
VariableMinLength:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
minimumVariableNameLength: 1
|
||||||
|
VariableNaming:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
variablePattern: '[a-z][A-Za-z0-9]*'
|
||||||
|
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
|
||||||
|
excludeClassPattern: '$^'
|
||||||
|
ignoreOverridden: true
|
||||||
|
|
||||||
|
performance:
|
||||||
|
active: true
|
||||||
|
ArrayPrimitive:
|
||||||
|
active: true
|
||||||
|
ForEachOnRange:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
SpreadOperator:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
UnnecessaryTemporaryInstantiation:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
potential-bugs:
|
||||||
|
active: true
|
||||||
|
Deprecation:
|
||||||
|
active: false
|
||||||
|
DuplicateCaseInWhenExpression:
|
||||||
|
active: true
|
||||||
|
EqualsAlwaysReturnsTrueOrFalse:
|
||||||
|
active: true
|
||||||
|
EqualsWithHashCodeExist:
|
||||||
|
active: true
|
||||||
|
ExplicitGarbageCollectionCall:
|
||||||
|
active: true
|
||||||
|
HasPlatformType:
|
||||||
|
active: false
|
||||||
|
IgnoredReturnValue:
|
||||||
|
active: false
|
||||||
|
restrictToAnnotatedMethods: true
|
||||||
|
returnValueAnnotations: ['*.CheckReturnValue', '*.CheckResult']
|
||||||
|
ImplicitDefaultLocale:
|
||||||
|
active: false
|
||||||
|
ImplicitUnitReturnType:
|
||||||
|
active: false
|
||||||
|
allowExplicitReturnType: true
|
||||||
|
InvalidRange:
|
||||||
|
active: true
|
||||||
|
IteratorHasNextCallsNextMethod:
|
||||||
|
active: true
|
||||||
|
IteratorNotThrowingNoSuchElementException:
|
||||||
|
active: true
|
||||||
|
LateinitUsage:
|
||||||
|
active: false
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
excludeAnnotatedProperties: []
|
||||||
|
ignoreOnClassesPattern: ''
|
||||||
|
MapGetWithNotNullAssertionOperator:
|
||||||
|
active: false
|
||||||
|
MissingWhenCase:
|
||||||
|
active: true
|
||||||
|
allowElseExpression: true
|
||||||
|
NullableToStringCall:
|
||||||
|
active: false
|
||||||
|
RedundantElseInWhen:
|
||||||
|
active: true
|
||||||
|
UnconditionalJumpStatementInLoop:
|
||||||
|
active: false
|
||||||
|
UnnecessaryNotNullOperator:
|
||||||
|
active: false
|
||||||
|
UnnecessarySafeCall:
|
||||||
|
active: false
|
||||||
|
UnreachableCode:
|
||||||
|
active: true
|
||||||
|
UnsafeCallOnNullableType:
|
||||||
|
active: true
|
||||||
|
UnsafeCast:
|
||||||
|
active: false
|
||||||
|
UselessPostfixExpression:
|
||||||
|
active: false
|
||||||
|
WrongEqualsTypeParameter:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
style:
|
||||||
|
active: true
|
||||||
|
ClassOrdering:
|
||||||
|
active: false
|
||||||
|
CollapsibleIfStatements:
|
||||||
|
active: false
|
||||||
|
DataClassContainsFunctions:
|
||||||
|
active: false
|
||||||
|
conversionFunctionPrefix: 'to'
|
||||||
|
DataClassShouldBeImmutable:
|
||||||
|
active: false
|
||||||
|
EqualsNullCall:
|
||||||
|
active: true
|
||||||
|
EqualsOnSignatureLine:
|
||||||
|
active: false
|
||||||
|
ExplicitCollectionElementAccessMethod:
|
||||||
|
active: false
|
||||||
|
ExplicitItLambdaParameter:
|
||||||
|
active: false
|
||||||
|
ExpressionBodySyntax:
|
||||||
|
active: false
|
||||||
|
includeLineWrapping: false
|
||||||
|
ForbiddenComment:
|
||||||
|
active: true
|
||||||
|
values: ['TODO:', 'FIXME:', 'STOPSHIP:']
|
||||||
|
allowedPatterns: ''
|
||||||
|
ForbiddenImport:
|
||||||
|
active: false
|
||||||
|
imports: []
|
||||||
|
forbiddenPatterns: ''
|
||||||
|
ForbiddenMethodCall:
|
||||||
|
active: false
|
||||||
|
methods: ['kotlin.io.println', 'kotlin.io.print']
|
||||||
|
ForbiddenPublicDataClass:
|
||||||
|
active: false
|
||||||
|
ignorePackages: ['*.internal', '*.internal.*']
|
||||||
|
ForbiddenVoid:
|
||||||
|
active: false
|
||||||
|
ignoreOverridden: false
|
||||||
|
ignoreUsageInGenerics: false
|
||||||
|
FunctionOnlyReturningConstant:
|
||||||
|
active: true
|
||||||
|
ignoreOverridableFunction: true
|
||||||
|
excludedFunctions: 'describeContents'
|
||||||
|
excludeAnnotatedFunction: ['dagger.Provides']
|
||||||
|
LibraryCodeMustSpecifyReturnType:
|
||||||
|
active: true
|
||||||
|
LibraryEntitiesShouldNotBePublic:
|
||||||
|
active: false
|
||||||
|
LoopWithTooManyJumpStatements:
|
||||||
|
active: true
|
||||||
|
maxJumpCount: 1
|
||||||
|
MagicNumber:
|
||||||
|
active: true
|
||||||
|
# TODO: re-enable omnipod-dash
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/omnipod-dash/**']
|
||||||
|
ignoreNumbers: ['-1', '0', '1', '2']
|
||||||
|
ignoreHashCodeFunction: true
|
||||||
|
ignorePropertyDeclaration: false
|
||||||
|
ignoreLocalVariableDeclaration: false
|
||||||
|
ignoreConstantDeclaration: true
|
||||||
|
ignoreCompanionObjectPropertyDeclaration: true
|
||||||
|
ignoreAnnotation: false
|
||||||
|
ignoreNamedArgument: true
|
||||||
|
ignoreEnums: false
|
||||||
|
ignoreRanges: false
|
||||||
|
MandatoryBracesIfStatements:
|
||||||
|
active: false
|
||||||
|
MandatoryBracesLoops:
|
||||||
|
active: false
|
||||||
|
MaxLineLength:
|
||||||
|
active: true
|
||||||
|
maxLineLength: 120
|
||||||
|
excludePackageStatements: true
|
||||||
|
excludeImportStatements: true
|
||||||
|
excludeCommentStatements: false
|
||||||
|
MayBeConst:
|
||||||
|
active: true
|
||||||
|
ModifierOrder:
|
||||||
|
active: true
|
||||||
|
NestedClassesVisibility:
|
||||||
|
active: false
|
||||||
|
NewLineAtEndOfFile:
|
||||||
|
active: false
|
||||||
|
NoTabs:
|
||||||
|
active: false
|
||||||
|
OptionalAbstractKeyword:
|
||||||
|
active: true
|
||||||
|
OptionalUnit:
|
||||||
|
active: false
|
||||||
|
OptionalWhenBraces:
|
||||||
|
active: false
|
||||||
|
PreferToOverPairSyntax:
|
||||||
|
active: false
|
||||||
|
ProtectedMemberInFinalClass:
|
||||||
|
active: true
|
||||||
|
RedundantExplicitType:
|
||||||
|
active: false
|
||||||
|
RedundantHigherOrderMapUsage:
|
||||||
|
active: false
|
||||||
|
RedundantVisibilityModifierRule:
|
||||||
|
active: false
|
||||||
|
ReturnCount:
|
||||||
|
active: true
|
||||||
|
max: 2
|
||||||
|
excludedFunctions: 'equals'
|
||||||
|
excludeLabeled: false
|
||||||
|
excludeReturnFromLambda: true
|
||||||
|
excludeGuardClauses: false
|
||||||
|
SafeCast:
|
||||||
|
active: true
|
||||||
|
SerialVersionUIDInSerializableClass:
|
||||||
|
active: false
|
||||||
|
SpacingBetweenPackageAndImports:
|
||||||
|
active: false
|
||||||
|
ThrowsCount:
|
||||||
|
active: true
|
||||||
|
max: 2
|
||||||
|
TrailingWhitespace:
|
||||||
|
active: false
|
||||||
|
UnderscoresInNumericLiterals:
|
||||||
|
active: false
|
||||||
|
acceptableDecimalLength: 5
|
||||||
|
UnnecessaryAbstractClass:
|
||||||
|
active: true
|
||||||
|
excludeAnnotatedClasses: ['dagger.Module']
|
||||||
|
UnnecessaryAnnotationUseSiteTarget:
|
||||||
|
active: false
|
||||||
|
UnnecessaryApply:
|
||||||
|
active: false
|
||||||
|
UnnecessaryInheritance:
|
||||||
|
active: true
|
||||||
|
UnnecessaryLet:
|
||||||
|
active: false
|
||||||
|
UnnecessaryParentheses:
|
||||||
|
active: false
|
||||||
|
UntilInsteadOfRangeTo:
|
||||||
|
active: false
|
||||||
|
UnusedImports:
|
||||||
|
active: false
|
||||||
|
UnusedPrivateClass:
|
||||||
|
active: true
|
||||||
|
UnusedPrivateMember:
|
||||||
|
active: false
|
||||||
|
allowedNames: '(_|ignored|expected|serialVersionUID)'
|
||||||
|
UseArrayLiteralsInAnnotations:
|
||||||
|
active: false
|
||||||
|
UseCheckNotNull:
|
||||||
|
active: false
|
||||||
|
UseCheckOrError:
|
||||||
|
active: false
|
||||||
|
UseDataClass:
|
||||||
|
active: false
|
||||||
|
excludeAnnotatedClasses: []
|
||||||
|
allowVars: false
|
||||||
|
UseEmptyCounterpart:
|
||||||
|
active: false
|
||||||
|
UseIfEmptyOrIfBlank:
|
||||||
|
active: false
|
||||||
|
UseIfInsteadOfWhen:
|
||||||
|
active: false
|
||||||
|
UseRequire:
|
||||||
|
active: false
|
||||||
|
UseRequireNotNull:
|
||||||
|
active: false
|
||||||
|
UselessCallOnNotNull:
|
||||||
|
active: true
|
||||||
|
UtilityClassWithPublicConstructor:
|
||||||
|
active: true
|
||||||
|
VarCouldBeVal:
|
||||||
|
active: false
|
||||||
|
WildcardImport:
|
||||||
|
active: true
|
||||||
|
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
|
||||||
|
excludeImports: ['java.util.*', 'kotlinx.android.synthetic.*']
|
|
@ -30,7 +30,9 @@ class DashHistoryTest {
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
database = Room.inMemoryDatabaseBuilder(
|
database = Room.inMemoryDatabaseBuilder(
|
||||||
context, DashHistoryDatabase::class.java).build()
|
context,
|
||||||
|
DashHistoryDatabase::class.java
|
||||||
|
).build()
|
||||||
dao = database.historyRecordDao()
|
dao = database.historyRecordDao()
|
||||||
dashHistory = DashHistory(dao, HistoryMapper())
|
dashHistory = DashHistory(dao, HistoryMapper())
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ class RxSchedulerRule(val scheduler: Scheduler) : TestRule {
|
||||||
RxJavaPlugins.reset()
|
RxJavaPlugins.reset()
|
||||||
RxAndroidPlugins.reset()
|
RxAndroidPlugins.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -129,19 +129,31 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult {
|
override fun setTempBasalAbsolute(
|
||||||
|
absoluteRate: Double,
|
||||||
|
durationInMinutes: Int,
|
||||||
|
profile: Profile,
|
||||||
|
enforceNew: Boolean
|
||||||
|
): PumpEnactResult {
|
||||||
// TODO
|
// TODO
|
||||||
return PumpEnactResult(injector).success(false).enacted(false).comment("TODO")
|
return PumpEnactResult(injector).success(false).enacted(false).comment("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult {
|
override fun setTempBasalPercent(
|
||||||
|
percent: Int,
|
||||||
|
durationInMinutes: Int,
|
||||||
|
profile: Profile,
|
||||||
|
enforceNew: Boolean
|
||||||
|
): PumpEnactResult {
|
||||||
// TODO i18n
|
// TODO i18n
|
||||||
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support percentage temp basals")
|
return PumpEnactResult(injector).success(false).enacted(false)
|
||||||
|
.comment("Omnipod Dash driver does not support percentage temp basals")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult {
|
override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult {
|
||||||
// TODO i18n
|
// TODO i18n
|
||||||
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support extended boluses")
|
return PumpEnactResult(injector).success(false).enacted(false)
|
||||||
|
.comment("Omnipod Dash driver does not support extended boluses")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
|
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
|
||||||
|
@ -151,7 +163,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
|
|
||||||
override fun cancelExtendedBolus(): PumpEnactResult {
|
override fun cancelExtendedBolus(): PumpEnactResult {
|
||||||
// TODO i18n
|
// TODO i18n
|
||||||
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support extended boluses")
|
return PumpEnactResult(injector).success(false).enacted(false)
|
||||||
|
.comment("Omnipod Dash driver does not support extended boluses")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
|
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
|
||||||
|
@ -184,7 +197,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
|
|
||||||
override fun loadTDDs(): PumpEnactResult {
|
override fun loadTDDs(): PumpEnactResult {
|
||||||
// TODO i18n
|
// TODO i18n
|
||||||
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support TDD")
|
return PumpEnactResult(injector).success(false).enacted(false)
|
||||||
|
.comment("Omnipod Dash driver does not support TDD")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canHandleDST(): Boolean {
|
override fun canHandleDST(): Boolean {
|
||||||
|
|
|
@ -19,7 +19,8 @@ class OmnipodDashHistoryModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
internal fun provideHistoryRecordDao(dashHistoryDatabase: DashHistoryDatabase): HistoryRecordDao = dashHistoryDatabase.historyRecordDao()
|
internal fun provideHistoryRecordDao(dashHistoryDatabase: DashHistoryDatabase): HistoryRecordDao =
|
||||||
|
dashHistoryDatabase.historyRecordDao()
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Reusable // no state, let system decide when to reuse or create new.
|
@Reusable // no state, let system decide when to reuse or create new.
|
||||||
|
@ -29,5 +30,4 @@ class OmnipodDashHistoryModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
internal fun provideDashHistory(dao: HistoryRecordDao, historyMapper: HistoryMapper) =
|
internal fun provideDashHistory(dao: HistoryRecordDao, historyMapper: HistoryMapper) =
|
||||||
DashHistory(dao, historyMapper)
|
DashHistory(dao, historyMapper)
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver
|
||||||
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertConfiguration
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertConfiguration
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertTrigger
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
|
@ -9,9 +10,9 @@ import java.util.*
|
||||||
|
|
||||||
interface OmnipodDashManager {
|
interface OmnipodDashManager {
|
||||||
|
|
||||||
fun activatePodPart1(): Observable<PodEvent>
|
fun activatePodPart1(lowReservoirAlertTrigger: AlertTrigger.ReservoirVolumeTrigger?): Observable<PodEvent>
|
||||||
|
|
||||||
fun activatePodPart2(): Observable<PodEvent>
|
fun activatePodPart2(basalProgram: BasalProgram): Observable<PodEvent>
|
||||||
|
|
||||||
fun getStatus(): Observable<PodEvent>
|
fun getStatus(): Observable<PodEvent>
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,15 @@ import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.GetVersionCommand
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.*
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.GetVersionCommand.Companion.DEFAULT_UNIQUE_ID
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.GetVersionCommand.Companion.DEFAULT_UNIQUE_ID
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertConfiguration
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.*
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||||
import info.nightscout.androidaps.utils.rx.retryWithBackoff
|
import info.nightscout.androidaps.utils.rx.retryWithBackoff
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.functions.Action
|
||||||
import io.reactivex.functions.Consumer
|
import io.reactivex.functions.Consumer
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
@ -42,33 +36,307 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val observePodReadyForActivationPart2: Observable<PodEvent>
|
||||||
|
get() = Observable.defer {
|
||||||
|
if (podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED) && podStateManager.activationProgress.isBefore(
|
||||||
|
ActivationProgress.COMPLETED
|
||||||
|
)) {
|
||||||
|
Observable.empty()
|
||||||
|
} else {
|
||||||
|
Observable.error(IllegalStateException("Pod is in an incorrect state"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val observeConnectToPod: Observable<PodEvent>
|
private val observeConnectToPod: Observable<PodEvent>
|
||||||
get() = Observable.defer { bleManager.connect().retryWithBackoff(retries = 2, delay = 3, timeUnit = TimeUnit.SECONDS) } // TODO are these reasonable values?
|
get() = Observable.defer {
|
||||||
|
bleManager.connect().retryWithBackoff(retries = 2, delay = 3, timeUnit = TimeUnit.SECONDS)
|
||||||
|
} // TODO are these reasonable values?
|
||||||
|
|
||||||
|
private fun observeSendProgramBolusCommand(
|
||||||
|
units: Double,
|
||||||
|
rateInEighthPulsesPerSeconds: Byte,
|
||||||
|
confirmationBeeps: Boolean,
|
||||||
|
completionBeeps: Boolean
|
||||||
|
): Observable<PodEvent> {
|
||||||
|
return Observable.defer {
|
||||||
|
bleManager.sendCommand(
|
||||||
|
ProgramBolusCommand.Builder()
|
||||||
|
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||||
|
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||||
|
.setNonce(1229869870) // TODO
|
||||||
|
.setNumberOfUnits(units)
|
||||||
|
.setDelayBetweenPulsesInEighthSeconds(rateInEighthPulsesPerSeconds)
|
||||||
|
.setProgramReminder(ProgramReminder(confirmationBeeps, completionBeeps, 0))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeSendGetPodStatusCommand(type: ResponseType.StatusResponseType = ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE): Observable<PodEvent> {
|
||||||
|
return Observable.defer {
|
||||||
|
bleManager.sendCommand(
|
||||||
|
GetStatusCommand.Builder()
|
||||||
|
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||||
|
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||||
|
.setStatusResponseType(type)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val observeVerifyCannulaInsertion: Observable<PodEvent>
|
||||||
|
get() = Observable.defer {
|
||||||
|
observeSendGetPodStatusCommand()
|
||||||
|
.ignoreElements() //
|
||||||
|
.andThen(
|
||||||
|
Observable.defer {
|
||||||
|
if (podStateManager.podStatus == PodStatus.RUNNING_ABOVE_MIN_VOLUME) {
|
||||||
|
Observable.empty()
|
||||||
|
} else {
|
||||||
|
Observable.error(IllegalStateException("Unexpected Pod status"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeSendProgramAlertsCommand(
|
||||||
|
alertConfigurations: List<AlertConfiguration>,
|
||||||
|
multiCommandFlag: Boolean = false
|
||||||
|
): Observable<PodEvent> {
|
||||||
|
return Observable.defer {
|
||||||
|
bleManager.sendCommand(
|
||||||
|
ProgramAlertsCommand.Builder()
|
||||||
|
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||||
|
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||||
|
.setNonce(1229869870) // TODO
|
||||||
|
.setAlertConfigurations(alertConfigurations)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeProgramBasalCommand(basalProgram: BasalProgram): Observable<PodEvent> {
|
||||||
|
return Observable.defer {
|
||||||
|
bleManager.sendCommand(
|
||||||
|
ProgramBasalCommand.Builder()
|
||||||
|
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||||
|
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||||
|
.setNonce(1229869870) // TODO
|
||||||
|
.setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0))
|
||||||
|
.setBasalProgram(basalProgram)
|
||||||
|
.setCurrentTime(Date())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val observeVerifyPrime: Observable<PodEvent>
|
||||||
|
get() = Observable.defer {
|
||||||
|
observeSendGetPodStatusCommand()
|
||||||
|
.ignoreElements() //
|
||||||
|
.andThen(
|
||||||
|
Observable.defer {
|
||||||
|
if (podStateManager.podStatus == PodStatus.CLUTCH_DRIVE_ENGAGED) {
|
||||||
|
Observable.empty()
|
||||||
|
} else {
|
||||||
|
Observable.error(IllegalStateException("Unexpected Pod status"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val observeSendSetUniqueIdCommand: Observable<PodEvent>
|
||||||
|
get() = Observable.defer {
|
||||||
|
bleManager.sendCommand(
|
||||||
|
SetUniqueIdCommand.Builder() //
|
||||||
|
.setSequenceNumber(podStateManager.messageSequenceNumber) //
|
||||||
|
.setUniqueId(podStateManager.uniqueId!!.toInt()) //
|
||||||
|
.setLotNumber(podStateManager.lotNumber!!.toInt()) //
|
||||||
|
.setPodSequenceNumber(podStateManager.podSequenceNumber!!.toInt())
|
||||||
|
.setInitializationTime(Date())
|
||||||
|
.build()
|
||||||
|
) //
|
||||||
|
}
|
||||||
|
|
||||||
private val observeSendGetVersionCommand: Observable<PodEvent>
|
private val observeSendGetVersionCommand: Observable<PodEvent>
|
||||||
get() = Observable.defer {
|
get() = Observable.defer {
|
||||||
bleManager.sendCommand(GetVersionCommand.Builder() //
|
bleManager.sendCommand(
|
||||||
.setSequenceNumber(podStateManager.messageSequenceNumber) //
|
GetVersionCommand.Builder() //
|
||||||
.setUniqueId(DEFAULT_UNIQUE_ID) //
|
.setSequenceNumber(podStateManager.messageSequenceNumber) //
|
||||||
.build()) //
|
.setUniqueId(DEFAULT_UNIQUE_ID) //
|
||||||
|
.build()
|
||||||
|
) //
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun activatePodPart1(): Observable<PodEvent> {
|
override fun activatePodPart1(lowReservoirAlertTrigger: AlertTrigger.ReservoirVolumeTrigger?): Observable<PodEvent> {
|
||||||
return Observable.concat(
|
return Observable.concat(
|
||||||
observePodReadyForActivationPart1,
|
observePodReadyForActivationPart1,
|
||||||
observeConnectToPod,
|
observeConnectToPod,
|
||||||
observeSendGetVersionCommand
|
observeActivationPart1Commands(lowReservoirAlertTrigger)
|
||||||
// ... Send more commands
|
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PHASE_1_COMPLETED))
|
||||||
) //
|
|
||||||
// TODO these would be common for any observable returned in a public function in this class
|
// TODO these would be common for any observable returned in a public function in this class
|
||||||
.doOnNext(PodEventInterceptor()) //
|
.doOnNext(PodEventInterceptor()) //
|
||||||
.doOnError(ErrorInterceptor())
|
.doOnError(ErrorInterceptor())
|
||||||
.subscribeOn(aapsSchedulers.io)
|
.subscribeOn(aapsSchedulers.io)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun activatePodPart2(): Observable<PodEvent> {
|
private fun observeActivationPart1Commands(lowReservoirAlertTrigger: AlertTrigger.ReservoirVolumeTrigger?): Observable<PodEvent> {
|
||||||
// TODO
|
val observables = createActivationPart1Observables(lowReservoirAlertTrigger)
|
||||||
return Observable.empty()
|
|
||||||
|
return if (observables.isEmpty()) {
|
||||||
|
Observable.empty()
|
||||||
|
} else {
|
||||||
|
Observable.concat(observables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createActivationPart1Observables(lowReservoirAlertTrigger: AlertTrigger.ReservoirVolumeTrigger?): List<Observable<PodEvent>> {
|
||||||
|
val observables = ArrayList<Observable<PodEvent>>()
|
||||||
|
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIME_COMPLETED)) {
|
||||||
|
observables.add(
|
||||||
|
observeVerifyPrime.doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIME_COMPLETED))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) {
|
||||||
|
observables.add(
|
||||||
|
observeSendProgramBolusCommand(
|
||||||
|
podStateManager.firstPrimeBolusVolume!! * 0.05,
|
||||||
|
podStateManager.primePulseRate!!.toByte(),
|
||||||
|
confirmationBeeps = false,
|
||||||
|
completionBeeps = false
|
||||||
|
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIMING))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.REPROGRAMMED_LUMP_OF_COAL_ALERT)) {
|
||||||
|
observables.add(
|
||||||
|
observeSendProgramAlertsCommand(
|
||||||
|
listOf(
|
||||||
|
AlertConfiguration(
|
||||||
|
AlertType.EXPIRATION,
|
||||||
|
enabled = true,
|
||||||
|
durationInMinutes = 55,
|
||||||
|
autoOff = false,
|
||||||
|
AlertTrigger.TimerTrigger(5),
|
||||||
|
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||||
|
BeepRepetitionType.XXX5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).doOnComplete(ActivationProgressUpdater(ActivationProgress.REPROGRAMMED_LUMP_OF_COAL_ALERT))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (lowReservoirAlertTrigger != null && podStateManager.activationProgress.isBefore(ActivationProgress.PROGRAMMED_LOW_RESERVOIR_ALERTS)) {
|
||||||
|
observables.add(
|
||||||
|
observeSendProgramAlertsCommand(
|
||||||
|
listOf(
|
||||||
|
AlertConfiguration(
|
||||||
|
AlertType.LOW_RESERVOIR,
|
||||||
|
enabled = true,
|
||||||
|
durationInMinutes = 0,
|
||||||
|
autoOff = false,
|
||||||
|
lowReservoirAlertTrigger,
|
||||||
|
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||||
|
BeepRepetitionType.XXX
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PROGRAMMED_LOW_RESERVOIR_ALERTS))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.SET_UNIQUE_ID)) {
|
||||||
|
observables.add(
|
||||||
|
observeSendSetUniqueIdCommand.doOnComplete(ActivationProgressUpdater(ActivationProgress.SET_UNIQUE_ID))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.GOT_POD_VERSION)) {
|
||||||
|
observables.add(
|
||||||
|
observeSendGetVersionCommand.doOnComplete(ActivationProgressUpdater(ActivationProgress.GOT_POD_VERSION))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return observables.reversed()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun activatePodPart2(basalProgram: BasalProgram): Observable<PodEvent> {
|
||||||
|
return Observable.concat(
|
||||||
|
observePodReadyForActivationPart2,
|
||||||
|
observeConnectToPod,
|
||||||
|
observeActivationPart2Commands(basalProgram)
|
||||||
|
).doOnComplete(ActivationProgressUpdater(ActivationProgress.COMPLETED))
|
||||||
|
// TODO these would be common for any observable returned in a public function in this class
|
||||||
|
.doOnNext(PodEventInterceptor()) //
|
||||||
|
.doOnError(ErrorInterceptor())
|
||||||
|
.subscribeOn(aapsSchedulers.io)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeActivationPart2Commands(basalProgram: BasalProgram): Observable<PodEvent> {
|
||||||
|
val observables = createActivationPart2Observables(basalProgram)
|
||||||
|
|
||||||
|
return if (observables.isEmpty()) {
|
||||||
|
Observable.empty()
|
||||||
|
} else {
|
||||||
|
Observable.concat(observables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createActivationPart2Observables(basalProgram: BasalProgram): List<Observable<PodEvent>> {
|
||||||
|
val observables = ArrayList<Observable<PodEvent>>()
|
||||||
|
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.CANNULA_INSERTED)) {
|
||||||
|
observables.add(
|
||||||
|
observeVerifyCannulaInsertion
|
||||||
|
.doOnComplete(ActivationProgressUpdater(ActivationProgress.CANNULA_INSERTED))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.INSERTING_CANNULA)) {
|
||||||
|
observables.add(
|
||||||
|
observeSendProgramBolusCommand(
|
||||||
|
podStateManager.secondPrimeBolusVolume!! * 0.05,
|
||||||
|
podStateManager.primePulseRate!!.toByte(),
|
||||||
|
confirmationBeeps = false,
|
||||||
|
completionBeeps = false
|
||||||
|
).doOnComplete(ActivationProgressUpdater(ActivationProgress.INSERTING_CANNULA))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.UPDATED_EXPIRATION_ALERTS)) {
|
||||||
|
observables.add(
|
||||||
|
observeSendProgramAlertsCommand(
|
||||||
|
listOf(
|
||||||
|
// FIXME use user configured expiration alert
|
||||||
|
AlertConfiguration(
|
||||||
|
AlertType.EXPIRATION,
|
||||||
|
enabled = true,
|
||||||
|
durationInMinutes = TimeUnit.HOURS.toMinutes(7).toShort(),
|
||||||
|
autoOff = false,
|
||||||
|
AlertTrigger.TimerTrigger(
|
||||||
|
TimeUnit.HOURS.toMinutes(73).toShort()
|
||||||
|
), // FIXME use activation time
|
||||||
|
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||||
|
BeepRepetitionType.XXX3
|
||||||
|
),
|
||||||
|
AlertConfiguration(
|
||||||
|
AlertType.EXPIRATION_IMMINENT,
|
||||||
|
enabled = true,
|
||||||
|
durationInMinutes = TimeUnit.HOURS.toMinutes(1).toShort(),
|
||||||
|
autoOff = false,
|
||||||
|
AlertTrigger.TimerTrigger(
|
||||||
|
TimeUnit.HOURS.toMinutes(79).toShort()
|
||||||
|
), // FIXME use activation time
|
||||||
|
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||||
|
BeepRepetitionType.XXX4
|
||||||
|
)
|
||||||
|
),
|
||||||
|
multiCommandFlag = true
|
||||||
|
).doOnComplete(ActivationProgressUpdater(ActivationProgress.UPDATED_EXPIRATION_ALERTS))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.PROGRAMMED_BASAL)) {
|
||||||
|
observables.add(
|
||||||
|
observeProgramBasalCommand(basalProgram)
|
||||||
|
.doOnComplete(ActivationProgressUpdater(ActivationProgress.PROGRAMMED_BASAL))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return observables.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getStatus(): Observable<PodEvent> {
|
override fun getStatus(): Observable<PodEvent> {
|
||||||
|
@ -149,6 +417,10 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
podStateManager.uniqueId = event.uniqueId
|
podStateManager.uniqueId = event.uniqueId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is PodEvent.CommandSent -> {
|
||||||
|
podStateManager.increaseMessageSequenceNumber()
|
||||||
|
}
|
||||||
|
|
||||||
is PodEvent.ResponseReceived -> {
|
is PodEvent.ResponseReceived -> {
|
||||||
podStateManager.increaseMessageSequenceNumber()
|
podStateManager.increaseMessageSequenceNumber()
|
||||||
handleResponse(event.response)
|
handleResponse(event.response)
|
||||||
|
@ -179,7 +451,6 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ErrorInterceptor : Consumer<Throwable> {
|
inner class ErrorInterceptor : Consumer<Throwable> {
|
||||||
|
@ -187,6 +458,12 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
override fun accept(throwable: Throwable) {
|
override fun accept(throwable: Throwable) {
|
||||||
logger.debug(LTag.PUMP, "Intercepted error in OmnipodDashManagerImpl: ${throwable.javaClass.simpleName}")
|
logger.debug(LTag.PUMP, "Intercepted error in OmnipodDashManagerImpl: ${throwable.javaClass.simpleName}")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ActivationProgressUpdater(private val value: ActivationProgress) : Action {
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
podStateManager.activationProgress = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,7 +22,7 @@ data class Id(val address: ByteArray) {
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
val asInt = ByteBuffer.wrap(address).int
|
val asInt = ByteBuffer.wrap(address).int
|
||||||
return "${asInt}/${address.toHex()}"
|
return "$asInt/${address.toHex()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -33,5 +33,4 @@ data class Id(val address: ByteArray) {
|
||||||
return Id(ByteBuffer.allocate(4).putInt(v).array())
|
return Id(ByteBuffer.allocate(4).putInt(v).array())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,18 +29,34 @@ import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class OmnipodDashBleManagerImpl @Inject constructor(private val context: Context, private val aapsLogger: AAPSLogger) : OmnipodDashBleManager {
|
class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
|
private val context: Context,
|
||||||
|
private val aapsLogger: AAPSLogger
|
||||||
|
) : OmnipodDashBleManager {
|
||||||
|
|
||||||
private val bluetoothManager: BluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
private val bluetoothManager: BluetoothManager =
|
||||||
|
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||||
private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
|
private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
|
||||||
|
|
||||||
@Throws(FailedToConnectException::class, CouldNotSendBleException::class, InterruptedException::class, BleIOBusyException::class, TimeoutException::class, CouldNotConfirmWriteException::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, CouldNotConfirmDescriptorWriteException::class)
|
@Throws(
|
||||||
|
FailedToConnectException::class,
|
||||||
|
CouldNotSendBleException::class,
|
||||||
|
InterruptedException::class,
|
||||||
|
BleIOBusyException::class,
|
||||||
|
TimeoutException::class,
|
||||||
|
CouldNotConfirmWriteException::class,
|
||||||
|
CouldNotEnableNotifications::class,
|
||||||
|
DescriptorNotFoundException::class,
|
||||||
|
CouldNotConfirmDescriptorWriteException::class
|
||||||
|
)
|
||||||
private fun connect(podAddress: String): BleIO {
|
private fun connect(podAddress: String): BleIO {
|
||||||
// TODO: locking?
|
// TODO: locking?
|
||||||
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
|
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
|
||||||
val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> =
|
val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> =
|
||||||
mapOf(CharacteristicType.CMD to LinkedBlockingDeque(),
|
mapOf(
|
||||||
CharacteristicType.DATA to LinkedBlockingDeque())
|
CharacteristicType.CMD to LinkedBlockingDeque(),
|
||||||
|
CharacteristicType.DATA to LinkedBlockingDeque()
|
||||||
|
)
|
||||||
val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
|
val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to $podAddress")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to $podAddress")
|
||||||
var autoConnect = true
|
var autoConnect = true
|
||||||
|
@ -74,7 +90,18 @@ class OmnipodDashBleManagerImpl @Inject constructor(private val context: Context
|
||||||
TODO("not implemented")
|
TODO("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(InterruptedException::class, ScanFailException::class, FailedToConnectException::class, CouldNotSendBleException::class, BleIOBusyException::class, TimeoutException::class, CouldNotConfirmWriteException::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, CouldNotConfirmDescriptorWriteException::class)
|
@Throws(
|
||||||
|
InterruptedException::class,
|
||||||
|
ScanFailException::class,
|
||||||
|
FailedToConnectException::class,
|
||||||
|
CouldNotSendBleException::class,
|
||||||
|
BleIOBusyException::class,
|
||||||
|
TimeoutException::class,
|
||||||
|
CouldNotConfirmWriteException::class,
|
||||||
|
CouldNotEnableNotifications::class,
|
||||||
|
DescriptorNotFoundException::class,
|
||||||
|
CouldNotConfirmDescriptorWriteException::class
|
||||||
|
)
|
||||||
override fun connect(): Observable<PodEvent> = Observable.create { emitter ->
|
override fun connect(): Observable<PodEvent> = Observable.create { emitter ->
|
||||||
// TODO: when we are already connected,
|
// TODO: when we are already connected,
|
||||||
// emit PodEvent.AlreadyConnected, complete the observable and return from this method
|
// emit PodEvent.AlreadyConnected, complete the observable and return from this method
|
||||||
|
@ -86,7 +113,10 @@ class OmnipodDashBleManagerImpl @Inject constructor(private val context: Context
|
||||||
val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
|
val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
|
||||||
emitter.onNext(PodEvent.Scanning)
|
emitter.onNext(PodEvent.Scanning)
|
||||||
|
|
||||||
val podAddress = podScanner.scanForPod(PodScanner.SCAN_FOR_SERVICE_UUID, PodScanner.POD_ID_NOT_ACTIVATED).scanResult.device.address
|
val podAddress = podScanner.scanForPod(
|
||||||
|
PodScanner.SCAN_FOR_SERVICE_UUID,
|
||||||
|
PodScanner.POD_ID_NOT_ACTIVATED
|
||||||
|
).scanResult.device.address
|
||||||
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
|
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
|
||||||
emitter.onNext(PodEvent.BluetoothConnecting)
|
emitter.onNext(PodEvent.BluetoothConnecting)
|
||||||
|
|
||||||
|
@ -118,5 +148,4 @@ class OmnipodDashBleManagerImpl @Inject constructor(private val context: Context
|
||||||
private const val CONNECT_TIMEOUT_MS = 7000
|
private const val CONNECT_TIMEOUT_MS = 7000
|
||||||
const val CONTROLLER_ID = 4242 // TODO read from preferences or somewhere else.
|
const val CONTROLLER_ID = 4242 // TODO read from preferences or somewhere else.
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,7 +4,6 @@ import android.bluetooth.BluetoothGatt
|
||||||
import android.bluetooth.BluetoothGattCharacteristic
|
import android.bluetooth.BluetoothGattCharacteristic
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CharacteristicNotFoundException
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CharacteristicNotFoundException
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ServiceNotFoundException
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ServiceNotFoundException
|
||||||
|
@ -12,7 +11,11 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.Chara
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ServiceDiscoverer(private val logger: AAPSLogger, private val gatt: BluetoothGatt, private val bleCallbacks: BleCommCallbacks) {
|
class ServiceDiscoverer(
|
||||||
|
private val logger: AAPSLogger,
|
||||||
|
private val gatt: BluetoothGatt,
|
||||||
|
private val bleCallbacks: BleCommCallbacks
|
||||||
|
) {
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* This is first step after connection establishment
|
* This is first step after connection establishment
|
||||||
|
@ -29,8 +32,10 @@ class ServiceDiscoverer(private val logger: AAPSLogger, private val gatt: Blueto
|
||||||
?: throw CharacteristicNotFoundException(CharacteristicType.CMD.value)
|
?: throw CharacteristicNotFoundException(CharacteristicType.CMD.value)
|
||||||
val dataChar = service.getCharacteristic(CharacteristicType.DATA.uuid) // TODO: this is never used
|
val dataChar = service.getCharacteristic(CharacteristicType.DATA.uuid) // TODO: this is never used
|
||||||
?: throw CharacteristicNotFoundException(CharacteristicType.DATA.value)
|
?: throw CharacteristicNotFoundException(CharacteristicType.DATA.value)
|
||||||
var chars = mapOf(CharacteristicType.CMD to cmdChar,
|
var chars = mapOf(
|
||||||
CharacteristicType.DATA to dataChar)
|
CharacteristicType.CMD to cmdChar,
|
||||||
|
CharacteristicType.DATA to dataChar
|
||||||
|
)
|
||||||
return chars
|
return chars
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,10 @@ import java.util.concurrent.LinkedBlockingQueue
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>) : BluetoothGattCallback() {
|
class BleCommCallbacks(
|
||||||
|
private val aapsLogger: AAPSLogger,
|
||||||
|
private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>
|
||||||
|
) : BluetoothGattCallback() {
|
||||||
|
|
||||||
private val serviceDiscoveryComplete: CountDownLatch = CountDownLatch(1)
|
private val serviceDiscoveryComplete: CountDownLatch = CountDownLatch(1)
|
||||||
private val connected: CountDownLatch = CountDownLatch(1)
|
private val connected: CountDownLatch = CountDownLatch(1)
|
||||||
|
@ -64,7 +67,10 @@ class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingP
|
||||||
|
|
||||||
private fun confirmWritePayload(expectedPayload: ByteArray, received: CharacteristicWriteConfirmationPayload) {
|
private fun confirmWritePayload(expectedPayload: ByteArray, received: CharacteristicWriteConfirmationPayload) {
|
||||||
if (!expectedPayload.contentEquals(received.payload)) {
|
if (!expectedPayload.contentEquals(received.payload)) {
|
||||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Could not confirm write. Got " + received.payload.toHex() + ".Excepted: " + expectedPayload.toHex())
|
aapsLogger.warn(
|
||||||
|
LTag.PUMPBTCOMM,
|
||||||
|
"Could not confirm write. Got " + received.payload.toHex() + ".Excepted: " + expectedPayload.toHex()
|
||||||
|
)
|
||||||
throw CouldNotConfirmWriteException(expectedPayload, received.payload)
|
throw CouldNotConfirmWriteException(expectedPayload, received.payload)
|
||||||
}
|
}
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Confirmed write with value: " + received.payload.toHex())
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Confirmed write with value: " + received.payload.toHex())
|
||||||
|
@ -77,8 +83,11 @@ class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingP
|
||||||
} else {
|
} else {
|
||||||
CharacteristicWriteConfirmationError(status)
|
CharacteristicWriteConfirmationError(status)
|
||||||
}
|
}
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicWrite with status/char/value " +
|
aapsLogger.debug(
|
||||||
status + "/" + byValue(characteristic.uuid.toString()) + "/" + characteristic.value.toHex())
|
LTag.PUMPBTCOMM,
|
||||||
|
"OnCharacteristicWrite with status/char/value " +
|
||||||
|
status + "/" + byValue(characteristic.uuid.toString()) + "/" + characteristic.value.toHex()
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
if (writeQueue.size > 0) {
|
if (writeQueue.size > 0) {
|
||||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Write confirm queue should be empty. found: " + writeQueue.size)
|
aapsLogger.warn(LTag.PUMPBTCOMM, "Write confirm queue should be empty. found: " + writeQueue.size)
|
||||||
|
@ -97,24 +106,34 @@ class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingP
|
||||||
super.onCharacteristicChanged(gatt, characteristic)
|
super.onCharacteristicChanged(gatt, characteristic)
|
||||||
val payload = characteristic.value
|
val payload = characteristic.value
|
||||||
val characteristicType = byValue(characteristic.uuid.toString())
|
val characteristicType = byValue(characteristic.uuid.toString())
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicChanged with char/value " +
|
aapsLogger.debug(
|
||||||
characteristicType + "/" +
|
LTag.PUMPBTCOMM,
|
||||||
payload.toHex())
|
"OnCharacteristicChanged with char/value " +
|
||||||
|
characteristicType + "/" +
|
||||||
|
payload.toHex()
|
||||||
|
)
|
||||||
incomingPackets[characteristicType]!!.add(payload)
|
incomingPackets[characteristicType]!!.add(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(InterruptedException::class, CouldNotConfirmDescriptorWriteException::class)
|
@Throws(InterruptedException::class, CouldNotConfirmDescriptorWriteException::class)
|
||||||
fun confirmWriteDescriptor(descriptorUUID: String, timeout_ms: Int) {
|
fun confirmWriteDescriptor(descriptorUUID: String, timeout_ms: Int) {
|
||||||
val confirmed: DescriptorWriteConfirmation = descriptorWriteQueue.poll(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
|
val confirmed: DescriptorWriteConfirmation = descriptorWriteQueue.poll(
|
||||||
|
timeout_ms.toLong(),
|
||||||
|
TimeUnit.MILLISECONDS
|
||||||
|
)
|
||||||
?: throw TimeoutException()
|
?: throw TimeoutException()
|
||||||
when (confirmed) {
|
when (confirmed) {
|
||||||
is DescriptorWriteConfirmationError -> throw CouldNotConfirmWriteException(confirmed.status)
|
is DescriptorWriteConfirmationError -> throw CouldNotConfirmWriteException(confirmed.status)
|
||||||
is DescriptorWriteConfirmationUUID -> if (confirmed.uuid != descriptorUUID) {
|
is DescriptorWriteConfirmationUUID ->
|
||||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Could not confirm descriptor write. Got ${confirmed.uuid}. Expected: $descriptorUUID")
|
if (confirmed.uuid != descriptorUUID) {
|
||||||
throw CouldNotConfirmDescriptorWriteException(descriptorUUID, confirmed.uuid)
|
aapsLogger.warn(
|
||||||
} else {
|
LTag.PUMPBTCOMM,
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Confirmed descriptor write : " + confirmed.uuid)
|
"Could not confirm descriptor write. Got ${confirmed.uuid}. Expected: $descriptorUUID"
|
||||||
}
|
)
|
||||||
|
throw CouldNotConfirmDescriptorWriteException(descriptorUUID, confirmed.uuid)
|
||||||
|
} else {
|
||||||
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Confirmed descriptor write : " + confirmed.uuid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,10 +147,17 @@ class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingP
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (descriptorWriteQueue.size > 0) {
|
if (descriptorWriteQueue.size > 0) {
|
||||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Descriptor write queue should be empty, found: ${descriptorWriteQueue.size}")
|
aapsLogger.warn(
|
||||||
|
LTag.PUMPBTCOMM,
|
||||||
|
"Descriptor write queue should be empty, found: ${descriptorWriteQueue.size}"
|
||||||
|
)
|
||||||
descriptorWriteQueue.clear()
|
descriptorWriteQueue.clear()
|
||||||
}
|
}
|
||||||
val offered = descriptorWriteQueue.offer(writeConfirmation, WRITE_CONFIRM_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
val offered = descriptorWriteQueue.offer(
|
||||||
|
writeConfirmation,
|
||||||
|
WRITE_CONFIRM_TIMEOUT_MS.toLong(),
|
||||||
|
TimeUnit.MILLISECONDS
|
||||||
|
)
|
||||||
if (!offered) {
|
if (!offered) {
|
||||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Received delayed descriptor write confirmation")
|
aapsLogger.warn(LTag.PUMPBTCOMM, "Received delayed descriptor write confirmation")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
||||||
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
class BleCommandHello(controllerId: Int) : BleCommand(BleCommandType.HELLO,
|
class BleCommandHello(controllerId: Int) : BleCommand(
|
||||||
|
BleCommandType.HELLO,
|
||||||
ByteBuffer.allocate(6)
|
ByteBuffer.allocate(6)
|
||||||
.put(1.toByte()) // TODO find the meaning of this constant
|
.put(1.toByte()) // TODO find the meaning of this constant
|
||||||
.put(4.toByte()) // TODO find the meaning of this constant
|
.put(4.toByte()) // TODO find the meaning of this constant
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
||||||
|
|
||||||
enum class BleCommandType(val value: Byte) {
|
enum class BleCommandType(val value: Byte) {
|
||||||
RTS(0x00.toByte()), CTS(0x01.toByte()), NACK(0x02.toByte()), ABORT(0x03.toByte()), SUCCESS(0x04.toByte()), FAIL(0x05.toByte()), HELLO(0x06.toByte());
|
RTS(0x00.toByte()),
|
||||||
|
CTS(0x01.toByte()),
|
||||||
|
NACK(0x02.toByte()),
|
||||||
|
ABORT(0x03.toByte()),
|
||||||
|
SUCCESS(0x04.toByte()),
|
||||||
|
FAIL(0x05.toByte()),
|
||||||
|
HELLO(
|
||||||
|
0x06.toByte()
|
||||||
|
);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||||
|
|
||||||
class CouldNotConfirmDescriptorWriteException(override val message: String?) : Exception(message) {
|
class CouldNotConfirmDescriptorWriteException(override val message: String?) : Exception(message) {
|
||||||
constructor(sent: String, confirmed: String) : this("Could not confirm write. Sent: {$sent} .Received: ${confirmed}")
|
constructor(sent: String, confirmed: String) : this("Could not confirm write. Sent: {$sent} .Received: $confirmed")
|
||||||
constructor(status: Int) : this("Could not confirm write. Write status: ${status}")
|
constructor(status: Int) : this("Could not confirm write. Write status: $status")
|
||||||
}
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||||
|
|
||||||
class CouldNotConfirmWriteException(override val message: String?) : Exception(message) {
|
class CouldNotConfirmWriteException(override val message: String?) : Exception(message) {
|
||||||
constructor(sent: ByteArray, confirmed: ByteArray) : this("Could not confirm write. Sent: {$sent} .Received: ${confirmed}")
|
constructor(
|
||||||
constructor(status: Int) : this("Could not confirm write. Write status: ${status}")
|
sent: ByteArray,
|
||||||
|
confirmed: ByteArray
|
||||||
|
) : this("Could not confirm write. Sent: {$sent} .Received: $confirmed")
|
||||||
|
|
||||||
|
constructor(status: Int) : this("Could not confirm write. Write status: $status")
|
||||||
}
|
}
|
|
@ -2,4 +2,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.excepti
|
||||||
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command.BleCommand
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command.BleCommand
|
||||||
|
|
||||||
class UnexpectedCommandException(val cmd: BleCommand) : Exception("Unexpected command: ${cmd}")
|
class UnexpectedCommandException(val cmd: BleCommand) : Exception("Unexpected command: $cmd")
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("WildcardImport")
|
||||||
|
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io
|
||||||
|
|
||||||
import android.bluetooth.BluetoothGatt
|
import android.bluetooth.BluetoothGatt
|
||||||
|
@ -12,7 +14,13 @@ import java.util.concurrent.BlockingQueue
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<CharacteristicType, BluetoothGattCharacteristic>, private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>, private val gatt: BluetoothGatt, private val bleCommCallbacks: BleCommCallbacks) {
|
class BleIO(
|
||||||
|
private val aapsLogger: AAPSLogger,
|
||||||
|
private val chars: Map<CharacteristicType, BluetoothGattCharacteristic>,
|
||||||
|
private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>,
|
||||||
|
private val gatt: BluetoothGatt,
|
||||||
|
private val bleCommCallbacks: BleCommCallbacks
|
||||||
|
) {
|
||||||
|
|
||||||
private var state: IOState = IOState.IDLE
|
private var state: IOState = IOState.IDLE
|
||||||
|
|
||||||
|
@ -41,7 +49,13 @@ class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<Character
|
||||||
* @param payload the data to send
|
* @param payload the data to send
|
||||||
* @throws CouldNotSendBleException
|
* @throws CouldNotSendBleException
|
||||||
*/
|
*/
|
||||||
@Throws(CouldNotSendBleException::class, BleIOBusyException::class, InterruptedException::class, CouldNotConfirmWriteException::class, TimeoutException::class)
|
@Throws(
|
||||||
|
CouldNotSendBleException::class,
|
||||||
|
BleIOBusyException::class,
|
||||||
|
InterruptedException::class,
|
||||||
|
CouldNotConfirmWriteException::class,
|
||||||
|
TimeoutException::class
|
||||||
|
)
|
||||||
fun sendAndConfirmPacket(characteristic: CharacteristicType, payload: ByteArray) {
|
fun sendAndConfirmPacket(characteristic: CharacteristicType, payload: ByteArray) {
|
||||||
synchronized(state) {
|
synchronized(state) {
|
||||||
if (state != IOState.IDLE) {
|
if (state != IOState.IDLE) {
|
||||||
|
@ -74,7 +88,13 @@ class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<Character
|
||||||
* This will signal the pod it can start sending back data
|
* This will signal the pod it can start sending back data
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Throws(CouldNotSendBleException::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, InterruptedException::class, CouldNotConfirmDescriptorWriteException::class)
|
@Throws(
|
||||||
|
CouldNotSendBleException::class,
|
||||||
|
CouldNotEnableNotifications::class,
|
||||||
|
DescriptorNotFoundException::class,
|
||||||
|
InterruptedException::class,
|
||||||
|
CouldNotConfirmDescriptorWriteException::class
|
||||||
|
)
|
||||||
fun readyToRead() {
|
fun readyToRead() {
|
||||||
for (type in CharacteristicType.values()) {
|
for (type in CharacteristicType.values()) {
|
||||||
val ch = chars[type]
|
val ch = chars[type]
|
||||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
|
||||||
import info.nightscout.androidaps.utils.extensions.toHex
|
import info.nightscout.androidaps.utils.extensions.toHex
|
||||||
|
|
||||||
class CrcMismatchException(val expected: Long, val actual: Long, val payload: ByteArray) :
|
class CrcMismatchException(val expected: Long, val actual: Long, val payload: ByteArray) :
|
||||||
Exception("CRC mismatch. Actual: ${actual}. Expected: ${expected}. Payload: ${payload.toHex()}")
|
Exception("CRC mismatch. Actual: $actual. Expected: $expected. Payload: ${payload.toHex()}")
|
||||||
|
|
|
@ -2,4 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
|
||||||
|
|
||||||
import info.nightscout.androidaps.utils.extensions.toHex
|
import info.nightscout.androidaps.utils.extensions.toHex
|
||||||
|
|
||||||
class IncorrectPacketException(val expectedIndex: Byte, val payload: ByteArray) : Exception("Invalid payload: ${payload.toHex()}. Expected index: ${expectedIndex}")
|
class IncorrectPacketException(
|
||||||
|
val expectedIndex: Byte,
|
||||||
|
val payload: ByteArray
|
||||||
|
) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex")
|
||||||
|
|
|
@ -21,7 +21,8 @@ data class MessagePacket(
|
||||||
val gateway: Boolean = false,
|
val gateway: Boolean = false,
|
||||||
val sas: Boolean = false, // TODO: understand
|
val sas: Boolean = false, // TODO: understand
|
||||||
val tfs: Boolean = false, // TODO: understand
|
val tfs: Boolean = false, // TODO: understand
|
||||||
val version: Short = 0.toShort()) {
|
val version: Short = 0.toShort()
|
||||||
|
) {
|
||||||
|
|
||||||
fun asByteArray(): ByteArray {
|
fun asByteArray(): ByteArray {
|
||||||
val bb = ByteBuffer.allocate(16 + payload.size)
|
val bb = ByteBuffer.allocate(16 + payload.size)
|
||||||
|
@ -90,7 +91,8 @@ data class MessagePacket(
|
||||||
val priority = f2.get(1) != 0
|
val priority = f2.get(1) != 0
|
||||||
val lastMessage = f2.get(2) != 0
|
val lastMessage = f2.get(2) != 0
|
||||||
val gateway = f2.get(3) != 0
|
val gateway = f2.get(3) != 0
|
||||||
val type = MessageType.byValue((f1.get(7) or (f1.get(6) shl 1) or (f1.get(5) shl 2) or (f1.get(4) shl 3)).toByte())
|
val type =
|
||||||
|
MessageType.byValue((f1.get(7) or (f1.get(6) shl 1) or (f1.get(5) shl 2) or (f1.get(4) shl 3)).toByte())
|
||||||
if (version.toInt() != 0) {
|
if (version.toInt() != 0) {
|
||||||
throw CouldNotParseMessageException(payload)
|
throw CouldNotParseMessageException(payload)
|
||||||
}
|
}
|
||||||
|
@ -139,7 +141,6 @@ private class Flag(var value: Int = 0) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,12 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
|
||||||
throw IncorrectPacketException(0, firstPacket)
|
throw IncorrectPacketException(0, firstPacket)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
fragments.add(firstPacket.copyOfRange(FirstBlePacket.HEADER_SIZE_WITH_MIDDLE_PACKETS, BlePacket.MAX_SIZE))
|
fragments.add(
|
||||||
|
firstPacket.copyOfRange(
|
||||||
|
FirstBlePacket.HEADER_SIZE_WITH_MIDDLE_PACKETS,
|
||||||
|
BlePacket.MAX_SIZE
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +92,12 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
|
||||||
throw IncorrectPacketException(idx.toByte(), packet)
|
throw IncorrectPacketException(idx.toByte(), packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
fragments.add(packet.copyOfRange(LastOptionalPlusOneBlePacket.HEADER_SIZE, LastOptionalPlusOneBlePacket.HEADER_SIZE + size))
|
fragments.add(
|
||||||
|
packet.copyOfRange(
|
||||||
|
LastOptionalPlusOneBlePacket.HEADER_SIZE,
|
||||||
|
LastOptionalPlusOneBlePacket.HEADER_SIZE + size
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +113,6 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
|
||||||
}
|
}
|
||||||
return bytes.copyOfRange(0, bytes.size)
|
return bytes.copyOfRange(0, bytes.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Int.toUnsignedLong() = this.toLong() and 0xffffffffL
|
internal fun Int.toUnsignedLong() = this.toLong() and 0xffffffffL
|
|
@ -15,51 +15,76 @@ internal class PayloadSplitter(private val payload: ByteArray) {
|
||||||
val crc32 = payload.crc32()
|
val crc32 = payload.crc32()
|
||||||
if (payload.size <= FirstBlePacket.CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET) {
|
if (payload.size <= FirstBlePacket.CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET) {
|
||||||
val end = min(FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS, payload.size)
|
val end = min(FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS, payload.size)
|
||||||
ret.add(FirstBlePacket(
|
ret.add(
|
||||||
totalFragments = 0,
|
FirstBlePacket(
|
||||||
payload = payload.copyOfRange(0, end),
|
totalFragments = 0,
|
||||||
size = payload.size.toByte(),
|
payload = payload.copyOfRange(0, end),
|
||||||
crc32 = crc32,
|
size = payload.size.toByte(),
|
||||||
))
|
crc32 = crc32,
|
||||||
|
)
|
||||||
|
)
|
||||||
if (payload.size > FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS) {
|
if (payload.size > FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS) {
|
||||||
ret.add(LastOptionalPlusOneBlePacket(
|
ret.add(
|
||||||
index = 1,
|
LastOptionalPlusOneBlePacket(
|
||||||
payload = payload.copyOfRange(end, payload.size),
|
index = 1,
|
||||||
size = (payload.size - end).toByte(),
|
payload = payload.copyOfRange(end, payload.size),
|
||||||
))
|
size = (payload.size - end).toByte(),
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
val middleFragments = (payload.size - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS) / MiddleBlePacket.CAPACITY
|
val middleFragments = (payload.size - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS) / MiddleBlePacket.CAPACITY
|
||||||
val rest = ((payload.size - middleFragments * MiddleBlePacket.CAPACITY) - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS).toByte()
|
val rest =
|
||||||
ret.add(FirstBlePacket(
|
((payload.size - middleFragments * MiddleBlePacket.CAPACITY) - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS).toByte()
|
||||||
totalFragments = (middleFragments + 1).toByte(),
|
ret.add(
|
||||||
payload = payload.copyOfRange(0, FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS),
|
FirstBlePacket(
|
||||||
))
|
totalFragments = (middleFragments + 1).toByte(),
|
||||||
|
payload = payload.copyOfRange(0, FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS),
|
||||||
|
)
|
||||||
|
)
|
||||||
for (i in 1..middleFragments) {
|
for (i in 1..middleFragments) {
|
||||||
val p = if (i == 1) {
|
val p = if (i == 1) {
|
||||||
payload.copyOfRange(FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS, FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + MiddleBlePacket.CAPACITY)
|
payload.copyOfRange(
|
||||||
|
FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS,
|
||||||
|
FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + MiddleBlePacket.CAPACITY
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
payload.copyOfRange(FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + (i - 1) * MiddleBlePacket.CAPACITY, FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + i * MiddleBlePacket.CAPACITY)
|
payload.copyOfRange(
|
||||||
|
FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + (i - 1) * MiddleBlePacket.CAPACITY,
|
||||||
|
FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + i * MiddleBlePacket.CAPACITY
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ret.add(MiddleBlePacket(
|
ret.add(
|
||||||
index = i.toByte(),
|
MiddleBlePacket(
|
||||||
payload = p,
|
index = i.toByte(),
|
||||||
))
|
payload = p,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val end = min(LastBlePacket.CAPACITY, rest.toInt())
|
val end = min(LastBlePacket.CAPACITY, rest.toInt())
|
||||||
ret.add(LastBlePacket(
|
ret.add(
|
||||||
index = (middleFragments + 1).toByte(),
|
LastBlePacket(
|
||||||
size = rest,
|
index = (middleFragments + 1).toByte(),
|
||||||
payload = payload.copyOfRange(middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS, middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + end),
|
size = rest,
|
||||||
crc32 = crc32,
|
payload = payload.copyOfRange(
|
||||||
))
|
middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS,
|
||||||
|
middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + end
|
||||||
|
),
|
||||||
|
crc32 = crc32,
|
||||||
|
)
|
||||||
|
)
|
||||||
if (rest > LastBlePacket.CAPACITY) {
|
if (rest > LastBlePacket.CAPACITY) {
|
||||||
ret.add(LastOptionalPlusOneBlePacket(
|
ret.add(
|
||||||
index = (middleFragments + 2).toByte(),
|
LastOptionalPlusOneBlePacket(
|
||||||
size = (rest - LastBlePacket.CAPACITY).toByte(),
|
index = (middleFragments + 2).toByte(),
|
||||||
payload = payload.copyOfRange(middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + LastBlePacket.CAPACITY, payload.size),
|
size = (rest - LastBlePacket.CAPACITY).toByte(),
|
||||||
))
|
payload = payload.copyOfRange(
|
||||||
|
middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + LastBlePacket.CAPACITY,
|
||||||
|
payload.size
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,20 +19,20 @@ class StringLengthPrefixEncoding {
|
||||||
for ((index, key) in keys.withIndex()) {
|
for ((index, key) in keys.withIndex()) {
|
||||||
when {
|
when {
|
||||||
remaining.size < key.length ->
|
remaining.size < key.length ->
|
||||||
throw MessageIOException("Payload too short: ${payload.toHex()} for key: ${key}")
|
throw MessageIOException("Payload too short: ${payload.toHex()} for key: $key")
|
||||||
!(remaining.copyOfRange(0, key.length).decodeToString() == key) ->
|
!(remaining.copyOfRange(0, key.length).decodeToString() == key) ->
|
||||||
throw MessageIOException("Key not found: ${key} in ${payload.toHex()}")
|
throw MessageIOException("Key not found: $key in ${payload.toHex()}")
|
||||||
// last key can be empty, no length
|
// last key can be empty, no length
|
||||||
index == keys.size - 1 && remaining.size == key.length ->
|
index == keys.size - 1 && remaining.size == key.length ->
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
remaining.size < key.length + LENGTH_BYTES ->
|
remaining.size < key.length + LENGTH_BYTES ->
|
||||||
throw MessageIOException("Length not found: for ${key} in ${payload.toHex()}")
|
throw MessageIOException("Length not found: for $key in ${payload.toHex()}")
|
||||||
}
|
}
|
||||||
remaining = remaining.copyOfRange(key.length, remaining.size)
|
remaining = remaining.copyOfRange(key.length, remaining.size)
|
||||||
val length = (remaining[0].toUnsignedInt() shl 1) or remaining[1].toUnsignedInt()
|
val length = (remaining[0].toUnsignedInt() shl 1) or remaining[1].toUnsignedInt()
|
||||||
if (length > remaining.size) {
|
if (length > remaining.size) {
|
||||||
throw MessageIOException("Payload too short, looking for length ${length} for ${key} in ${payload.toHex()}")
|
throw MessageIOException("Payload too short, looking for length $length for $key in ${payload.toHex()}")
|
||||||
}
|
}
|
||||||
ret[index] = remaining.copyOfRange(LENGTH_BYTES, LENGTH_BYTES + length)
|
ret[index] = remaining.copyOfRange(LENGTH_BYTES, LENGTH_BYTES + length)
|
||||||
remaining = remaining.copyOfRange(LENGTH_BYTES + length, remaining.size)
|
remaining = remaining.copyOfRange(LENGTH_BYTES + length, remaining.size)
|
||||||
|
|
|
@ -12,7 +12,12 @@ sealed class BlePacket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class FirstBlePacket(val totalFragments: Byte, val payload: ByteArray, val size: Byte? = null, val crc32: Long? = null) : BlePacket() {
|
data class FirstBlePacket(
|
||||||
|
val totalFragments: Byte,
|
||||||
|
val payload: ByteArray,
|
||||||
|
val size: Byte? = null,
|
||||||
|
val crc32: Long? = null
|
||||||
|
) : BlePacket() {
|
||||||
|
|
||||||
override fun asByteArray(): ByteArray {
|
override fun asByteArray(): ByteArray {
|
||||||
val bb = ByteBuffer
|
val bb = ByteBuffer
|
||||||
|
@ -40,8 +45,10 @@ data class FirstBlePacket(val totalFragments: Byte, val payload: ByteArray, val
|
||||||
internal const val HEADER_SIZE_WITHOUT_MIDDLE_PACKETS = 7 // we are using all fields
|
internal const val HEADER_SIZE_WITHOUT_MIDDLE_PACKETS = 7 // we are using all fields
|
||||||
internal const val HEADER_SIZE_WITH_MIDDLE_PACKETS = 2
|
internal const val HEADER_SIZE_WITH_MIDDLE_PACKETS = 2
|
||||||
|
|
||||||
internal const val CAPACITY_WITHOUT_MIDDLE_PACKETS = MAX_SIZE - HEADER_SIZE_WITHOUT_MIDDLE_PACKETS // we are using all fields
|
internal const val CAPACITY_WITHOUT_MIDDLE_PACKETS =
|
||||||
internal const val CAPACITY_WITH_MIDDLE_PACKETS = MAX_SIZE - HEADER_SIZE_WITH_MIDDLE_PACKETS // we are not using crc32 or size
|
MAX_SIZE - HEADER_SIZE_WITHOUT_MIDDLE_PACKETS // we are using all fields
|
||||||
|
internal const val CAPACITY_WITH_MIDDLE_PACKETS =
|
||||||
|
MAX_SIZE - HEADER_SIZE_WITH_MIDDLE_PACKETS // we are not using crc32 or size
|
||||||
internal const val CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET = 18
|
internal const val CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET = 18
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,4 +99,3 @@ data class LastOptionalPlusOneBlePacket(val index: Byte, val payload: ByteArray,
|
||||||
internal const val HEADER_SIZE = 2
|
internal const val HEADER_SIZE = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,10 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
|
||||||
throw MessageIOException("Invalid payload size")
|
throw MessageIOException("Invalid payload size")
|
||||||
}
|
}
|
||||||
if (!podConf.contentEquals(payload)) {
|
if (!podConf.contentEquals(payload)) {
|
||||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Received invalid podConf. Expected: ${podConf.toHex()}. Got: ${payload.toHex()}")
|
aapsLogger.warn(
|
||||||
|
LTag.PUMPBTCOMM,
|
||||||
|
"Received invalid podConf. Expected: ${podConf.toHex()}. Got: ${payload.toHex()}"
|
||||||
|
)
|
||||||
throw MessageIOException("Invalid podConf value received")
|
throw MessageIOException("Invalid podConf value received")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,24 +228,25 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val PUBLIC_KEY_SIZE = 32
|
private const val PUBLIC_KEY_SIZE = 32
|
||||||
private val NONCE_SIZE = 16
|
private const val NONCE_SIZE = 16
|
||||||
private val CONF_SIZE = 16
|
private const val CONF_SIZE = 16
|
||||||
|
|
||||||
private val CMAC_SIZE = 16
|
private const val CMAC_SIZE = 16
|
||||||
|
|
||||||
private val INTERMEDIAR_KEY_MAGIC_STRING = "TWIt".toByteArray()
|
private val INTERMEDIAR_KEY_MAGIC_STRING = "TWIt".toByteArray()
|
||||||
private val PDM_CONF_MAGIC_PREFIX = "KC_2_U".toByteArray()
|
private val PDM_CONF_MAGIC_PREFIX = "KC_2_U".toByteArray()
|
||||||
private val POD_CONF_MAGIC_PREFIX = "KC_2_V".toByteArray()
|
private val POD_CONF_MAGIC_PREFIX = "KC_2_V".toByteArray()
|
||||||
|
|
||||||
private val GET_POD_STATUS_HEX_COMMAND = "ffc32dbd08030e0100008a" // TODO for now we are assuming this command is build out of constant parameters, use a proper command builder for that.
|
private const val GET_POD_STATUS_HEX_COMMAND =
|
||||||
|
"ffc32dbd08030e0100008a" // TODO for now we are assuming this command is build out of constant parameters, use a proper command builder for that.
|
||||||
|
|
||||||
private val SP1 = "SP1="
|
private const val SP1 = "SP1="
|
||||||
private val SP2 = ",SP2="
|
private const val SP2 = ",SP2="
|
||||||
private val SPS1 = "SPS1="
|
private const val SPS1 = "SPS1="
|
||||||
private val SPS2 = "SPS2="
|
private const val SPS2 = "SPS2="
|
||||||
private val SP0GP0 = "SP0,GP0"
|
private const val SP0GP0 = "SP0,GP0"
|
||||||
private val P0 = "P0="
|
private const val P0 = "P0="
|
||||||
private val UNKNOWN_P0_PAYLOAD = byteArrayOf(0xa5.toByte())
|
private val UNKNOWN_P0_PAYLOAD = byteArrayOf(0xa5.toByte())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,16 +15,22 @@ class BleDiscoveredDevice(val scanResult: ScanResult, private val scanRecord: Sc
|
||||||
val serviceUuids = scanRecord.serviceUuids
|
val serviceUuids = scanRecord.serviceUuids
|
||||||
if (serviceUuids.size != 9) {
|
if (serviceUuids.size != 9) {
|
||||||
throw DiscoveredInvalidPodException("Expected 9 service UUIDs, got" + serviceUuids.size, serviceUuids)
|
throw DiscoveredInvalidPodException("Expected 9 service UUIDs, got" + serviceUuids.size, serviceUuids)
|
||||||
|
|
||||||
}
|
}
|
||||||
if (extractUUID16(serviceUuids[0]) != MAIN_SERVICE_UUID) {
|
if (extractUUID16(serviceUuids[0]) != MAIN_SERVICE_UUID) {
|
||||||
// this is the service that we filtered for
|
// this is the service that we filtered for
|
||||||
throw DiscoveredInvalidPodException("The first exposed service UUID should be 4024, got " + extractUUID16(serviceUuids[0]), serviceUuids)
|
throw DiscoveredInvalidPodException(
|
||||||
|
"The first exposed service UUID should be 4024, got " + extractUUID16(
|
||||||
|
serviceUuids[0]
|
||||||
|
), serviceUuids
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// TODO understand what is serviceUUIDs[1]. 0x2470. Alarms?
|
// TODO understand what is serviceUUIDs[1]. 0x2470. Alarms?
|
||||||
if (extractUUID16(serviceUuids[2]) != UNKNOWN_THIRD_SERVICE_UUID) {
|
if (extractUUID16(serviceUuids[2]) != UNKNOWN_THIRD_SERVICE_UUID) {
|
||||||
// constant?
|
// constant?
|
||||||
throw DiscoveredInvalidPodException("The third exposed service UUID should be 000a, got " + serviceUuids[2], serviceUuids)
|
throw DiscoveredInvalidPodException(
|
||||||
|
"The third exposed service UUID should be 000a, got " + serviceUuids[2],
|
||||||
|
serviceUuids
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +41,10 @@ class BleDiscoveredDevice(val scanResult: ScanResult, private val scanRecord: Sc
|
||||||
val hexPodId = extractUUID16(serviceUUIDs[3]) + extractUUID16(serviceUUIDs[4])
|
val hexPodId = extractUUID16(serviceUUIDs[3]) + extractUUID16(serviceUUIDs[4])
|
||||||
val podId = hexPodId.toLong(16)
|
val podId = hexPodId.toLong(16)
|
||||||
if (this.podId != podId) {
|
if (this.podId != podId) {
|
||||||
throw DiscoveredInvalidPodException("This is not the POD we are looking for. " + this.podId + " found: " + this.podId, serviceUUIDs)
|
throw DiscoveredInvalidPodException(
|
||||||
|
"This is not the POD we are looking for. " + this.podId + " found: " + this.podId,
|
||||||
|
serviceUUIDs
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,5 +45,4 @@ class ScanCollector(private val logger: AAPSLogger, private val podID: Long) : S
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(ret)
|
return Collections.unmodifiableList(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,4 +20,3 @@ sealed class PodEvent {
|
||||||
class CommandSent(val command: Command) : PodEvent()
|
class CommandSent(val command: Command) : PodEvent()
|
||||||
class ResponseReceived(val response: Response) : PodEvent()
|
class ResponseReceived(val response: Response) : PodEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,14 @@ class DeactivateCommand private constructor(
|
||||||
) : NonceEnabledCommand(CommandType.DEACTIVATE, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
) : NonceEnabledCommand(CommandType.DEACTIVATE, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
get() = appendCrc(
|
||||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||||
.put(commandType.value) //
|
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||||
.put(BODY_LENGTH) //
|
.put(commandType.value) //
|
||||||
.putInt(nonce) //
|
.put(BODY_LENGTH) //
|
||||||
.array())
|
.putInt(nonce) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
|
|
||||||
override fun toString(): String = "DeactivateCommand{" +
|
override fun toString(): String = "DeactivateCommand{" +
|
||||||
"nonce=" + nonce +
|
"nonce=" + nonce +
|
||||||
|
@ -31,7 +33,12 @@ class DeactivateCommand private constructor(
|
||||||
class Builder : NonceEnabledCommandBuilder<Builder, DeactivateCommand>() {
|
class Builder : NonceEnabledCommandBuilder<Builder, DeactivateCommand>() {
|
||||||
|
|
||||||
override fun buildCommand(): DeactivateCommand =
|
override fun buildCommand(): DeactivateCommand =
|
||||||
DeactivateCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!) // TODO this might crash if not all are set
|
DeactivateCommand(
|
||||||
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
nonce!!
|
||||||
|
) // TODO this might crash if not all are set
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -14,12 +14,14 @@ class GetStatusCommand private constructor(
|
||||||
) : HeaderEnabledCommand(CommandType.GET_STATUS, uniqueId, sequenceNumber, multiCommandFlag) {
|
) : HeaderEnabledCommand(CommandType.GET_STATUS, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
get() = appendCrc(
|
||||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||||
.put(commandType.value) //
|
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||||
.put(BODY_LENGTH) //
|
.put(commandType.value) //
|
||||||
.put(statusResponseType.value) //
|
.put(BODY_LENGTH) //
|
||||||
.array())
|
.put(statusResponseType.value) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
|
|
||||||
class Builder : HeaderEnabledCommandBuilder<Builder, GetStatusCommand>() {
|
class Builder : HeaderEnabledCommandBuilder<Builder, GetStatusCommand>() {
|
||||||
|
|
||||||
|
@ -35,7 +37,6 @@ class GetStatusCommand private constructor(
|
||||||
|
|
||||||
return GetStatusCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, statusResponseType!!)
|
return GetStatusCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, statusResponseType!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -12,12 +12,14 @@ class GetVersionCommand private constructor(
|
||||||
) : HeaderEnabledCommand(CommandType.GET_VERSION, uniqueId, sequenceNumber, multiCommandFlag) {
|
) : HeaderEnabledCommand(CommandType.GET_VERSION, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
get() = appendCrc(
|
||||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||||
.put(commandType.value) //
|
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||||
.put(BODY_LENGTH) //
|
.put(commandType.value) //
|
||||||
.putInt(uniqueId) //
|
.put(BODY_LENGTH) //
|
||||||
.array())
|
.putInt(uniqueId) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "GetVersionCommand{" +
|
return "GetVersionCommand{" +
|
||||||
|
|
|
@ -48,12 +48,19 @@ class ProgramBasalCommand private constructor(
|
||||||
}
|
}
|
||||||
val basalCommand = buffer.array()
|
val basalCommand = buffer.array()
|
||||||
val interlockCommand = interlockCommand.encoded
|
val interlockCommand = interlockCommand.encoded
|
||||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (basalCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
|
val header: ByteArray = encodeHeader(
|
||||||
return appendCrc(ByteBuffer.allocate(basalCommand.size + interlockCommand.size + header.size) //
|
uniqueId,
|
||||||
.put(header) //
|
sequenceNumber,
|
||||||
.put(interlockCommand) //
|
(basalCommand.size + interlockCommand.size).toShort(),
|
||||||
.put(basalCommand) //
|
multiCommandFlag
|
||||||
.array())
|
)
|
||||||
|
return appendCrc(
|
||||||
|
ByteBuffer.allocate(basalCommand.size + interlockCommand.size + header.size) //
|
||||||
|
.put(header) //
|
||||||
|
.put(interlockCommand) //
|
||||||
|
.put(basalCommand) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
@ -99,15 +106,33 @@ class ProgramBasalCommand private constructor(
|
||||||
val pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram!!)
|
val pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram!!)
|
||||||
val currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime)
|
val currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime)
|
||||||
val checksum = ProgramBasalUtil.calculateChecksum(pulsesPerSlot, currentSlot)
|
val checksum = ProgramBasalUtil.calculateChecksum(pulsesPerSlot, currentSlot)
|
||||||
val longInsulinProgramElements: List<BasalInsulinProgramElement> = mapTenthPulsesPerSlotToLongInsulinProgramElements(ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram!!))
|
val longInsulinProgramElements: List<BasalInsulinProgramElement> =
|
||||||
val shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot)
|
mapTenthPulsesPerSlotToLongInsulinProgramElements(
|
||||||
val currentBasalInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime)
|
ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram!!)
|
||||||
val interlockCommand = ProgramInsulinCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!,
|
)
|
||||||
|
val shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(
|
||||||
|
pulsesPerSlot
|
||||||
|
)
|
||||||
|
val currentBasalInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(
|
||||||
|
longInsulinProgramElements,
|
||||||
|
currentTime
|
||||||
|
)
|
||||||
|
val interlockCommand = ProgramInsulinCommand(
|
||||||
|
uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!,
|
||||||
shortInsulinProgramElements, checksum, currentSlot.index, currentSlot.eighthSecondsRemaining,
|
shortInsulinProgramElements, checksum, currentSlot.index, currentSlot.eighthSecondsRemaining,
|
||||||
currentSlot.pulsesRemaining, ProgramInsulinCommand.DeliveryType.BASAL)
|
currentSlot.pulsesRemaining, ProgramInsulinCommand.DeliveryType.BASAL
|
||||||
return ProgramBasalCommand(interlockCommand, uniqueId!!, sequenceNumber!!, multiCommandFlag,
|
)
|
||||||
longInsulinProgramElements, programReminder!!, currentBasalInsulinProgramElement.index,
|
return ProgramBasalCommand(
|
||||||
currentBasalInsulinProgramElement.remainingTenthPulses, currentBasalInsulinProgramElement.delayUntilNextTenthPulseInUsec)
|
interlockCommand,
|
||||||
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
longInsulinProgramElements,
|
||||||
|
programReminder!!,
|
||||||
|
currentBasalInsulinProgramElement.index,
|
||||||
|
currentBasalInsulinProgramElement.remainingTenthPulses,
|
||||||
|
currentBasalInsulinProgramElement.delayUntilNextTenthPulseInUsec
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,15 +18,17 @@ class ProgramBeepsCommand private constructor(
|
||||||
) : HeaderEnabledCommand(CommandType.PROGRAM_BEEPS, uniqueId, sequenceNumber, multiCommandFlag) {
|
) : HeaderEnabledCommand(CommandType.PROGRAM_BEEPS, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
get() = appendCrc(
|
||||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||||
.put(commandType.value) //
|
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||||
.put(BODY_LENGTH) //
|
.put(commandType.value) //
|
||||||
.put(immediateBeepType.value) //
|
.put(BODY_LENGTH) //
|
||||||
.put(basalReminder.encoded) //
|
.put(immediateBeepType.value) //
|
||||||
.put(tempBasalReminder.encoded) //
|
.put(basalReminder.encoded) //
|
||||||
.put(bolusReminder.encoded) //
|
.put(tempBasalReminder.encoded) //
|
||||||
.array())
|
.put(bolusReminder.encoded) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
|
|
||||||
class Builder : HeaderEnabledCommandBuilder<Builder, ProgramBeepsCommand>() {
|
class Builder : HeaderEnabledCommandBuilder<Builder, ProgramBeepsCommand>() {
|
||||||
|
|
||||||
|
@ -61,7 +63,15 @@ class ProgramBeepsCommand private constructor(
|
||||||
requireNotNull(tempBasalReminder) { "tempBasalReminder can not be null" }
|
requireNotNull(tempBasalReminder) { "tempBasalReminder can not be null" }
|
||||||
requireNotNull(bolusReminder) { "bolusReminder can not be null" }
|
requireNotNull(bolusReminder) { "bolusReminder can not be null" }
|
||||||
|
|
||||||
return ProgramBeepsCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, immediateBeepType!!, basalReminder!!, tempBasalReminder!!, bolusReminder!!)
|
return ProgramBeepsCommand(
|
||||||
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
immediateBeepType!!,
|
||||||
|
basalReminder!!,
|
||||||
|
tempBasalReminder!!,
|
||||||
|
bolusReminder!!
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,19 @@ class ProgramBolusCommand private constructor(
|
||||||
.putInt(0) // Delay between tenth extended pulses in usec
|
.putInt(0) // Delay between tenth extended pulses in usec
|
||||||
.array()
|
.array()
|
||||||
val interlockCommand = interlockCommand.encoded
|
val interlockCommand = interlockCommand.encoded
|
||||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (bolusCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
|
val header: ByteArray = encodeHeader(
|
||||||
return appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + bolusCommand.size) //
|
uniqueId,
|
||||||
.put(header) //
|
sequenceNumber,
|
||||||
.put(interlockCommand) //
|
(bolusCommand.size + interlockCommand.size).toShort(),
|
||||||
.put(bolusCommand) //
|
multiCommandFlag
|
||||||
.array())
|
)
|
||||||
|
return appendCrc(
|
||||||
|
ByteBuffer.allocate(header.size + interlockCommand.size + bolusCommand.size) //
|
||||||
|
.put(header) //
|
||||||
|
.put(interlockCommand) //
|
||||||
|
.put(bolusCommand) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
@ -82,10 +89,28 @@ class ProgramBolusCommand private constructor(
|
||||||
|
|
||||||
val numberOfPulses = Math.round(numberOfUnits!! * 20).toShort()
|
val numberOfPulses = Math.round(numberOfUnits!! * 20).toShort()
|
||||||
val byte10And11 = (numberOfPulses * delayBetweenPulsesInEighthSeconds!!).toShort()
|
val byte10And11 = (numberOfPulses * delayBetweenPulsesInEighthSeconds!!).toShort()
|
||||||
val interlockCommand = ProgramInsulinCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!, listOf(BolusShortInsulinProgramElement(numberOfPulses)), calculateChecksum(0x01.toByte(), byte10And11, numberOfPulses),
|
val interlockCommand = ProgramInsulinCommand(
|
||||||
0x01.toByte(), byte10And11, numberOfPulses, ProgramInsulinCommand.DeliveryType.BOLUS)
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
nonce!!,
|
||||||
|
listOf(BolusShortInsulinProgramElement(numberOfPulses)),
|
||||||
|
calculateChecksum(0x01.toByte(), byte10And11, numberOfPulses),
|
||||||
|
0x01.toByte(),
|
||||||
|
byte10And11,
|
||||||
|
numberOfPulses,
|
||||||
|
ProgramInsulinCommand.DeliveryType.BOLUS
|
||||||
|
)
|
||||||
val delayUntilFirstTenthPulseInUsec = delayBetweenPulsesInEighthSeconds!! / 8 * 100000
|
val delayUntilFirstTenthPulseInUsec = delayBetweenPulsesInEighthSeconds!! / 8 * 100000
|
||||||
return ProgramBolusCommand(interlockCommand, uniqueId!!, sequenceNumber!!, multiCommandFlag, programReminder!!, (numberOfPulses * 10).toShort(), delayUntilFirstTenthPulseInUsec)
|
return ProgramBolusCommand(
|
||||||
|
interlockCommand,
|
||||||
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
programReminder!!,
|
||||||
|
(numberOfPulses * 10).toShort(),
|
||||||
|
delayUntilFirstTenthPulseInUsec
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,12 +119,14 @@ class ProgramBolusCommand private constructor(
|
||||||
private const val LENGTH: Short = 15
|
private const val LENGTH: Short = 15
|
||||||
private const val BODY_LENGTH: Byte = 13
|
private const val BODY_LENGTH: Byte = 13
|
||||||
private fun calculateChecksum(numberOfSlots: Byte, byte10And11: Short, numberOfPulses: Short): Short {
|
private fun calculateChecksum(numberOfSlots: Byte, byte10And11: Short, numberOfPulses: Short): Short {
|
||||||
return MessageUtil.calculateChecksum(ByteBuffer.allocate(7) //
|
return MessageUtil.calculateChecksum(
|
||||||
.put(numberOfSlots) //
|
ByteBuffer.allocate(7) //
|
||||||
.putShort(byte10And11) //
|
.put(numberOfSlots) //
|
||||||
.putShort(numberOfPulses) //
|
.putShort(byte10And11) //
|
||||||
.putShort(numberOfPulses) //
|
.putShort(numberOfPulses) //
|
||||||
.array())
|
.putShort(numberOfPulses) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,7 +11,8 @@ class ProgramInsulinCommand internal constructor(
|
||||||
uniqueId: Int,
|
uniqueId: Int,
|
||||||
sequenceNumber: Short,
|
sequenceNumber: Short,
|
||||||
multiCommandFlag: Boolean,
|
multiCommandFlag: Boolean,
|
||||||
nonce: Int, insulinProgramElements:
|
nonce: Int,
|
||||||
|
insulinProgramElements:
|
||||||
List<ShortInsulinProgramElement>,
|
List<ShortInsulinProgramElement>,
|
||||||
private val checksum: Short,
|
private val checksum: Short,
|
||||||
private val byte9: Byte,
|
private val byte9: Byte,
|
||||||
|
|
|
@ -56,13 +56,36 @@ class ProgramTempBasalCommand private constructor(
|
||||||
|
|
||||||
val durationInSlots = (durationInMinutes!! / 30).toByte()
|
val durationInSlots = (durationInMinutes!! / 30).toByte()
|
||||||
val pulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToPulsesPerSlot(durationInSlots, rateInUnitsPerHour!!)
|
val pulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToPulsesPerSlot(durationInSlots, rateInUnitsPerHour!!)
|
||||||
val tenthPulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToTenthPulsesPerSlot(durationInSlots.toInt(), rateInUnitsPerHour!!)
|
val tenthPulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToTenthPulsesPerSlot(
|
||||||
val shortInsulinProgramElements = ProgramTempBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot)
|
durationInSlots.toInt(),
|
||||||
val insulinProgramElements = ProgramTempBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot)
|
rateInUnitsPerHour!!
|
||||||
val interlockCommand = ProgramInsulinCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!, shortInsulinProgramElements,
|
)
|
||||||
ProgramTempBasalUtil.calculateChecksum(durationInSlots, pulsesPerSlot[0], pulsesPerSlot), durationInSlots,
|
val shortInsulinProgramElements = ProgramTempBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(
|
||||||
0x3840.toShort(), pulsesPerSlot[0], ProgramInsulinCommand.DeliveryType.TEMP_BASAL)
|
pulsesPerSlot
|
||||||
return ProgramTempBasalCommand(interlockCommand, uniqueId!!, sequenceNumber!!, multiCommandFlag, programReminder!!, insulinProgramElements)
|
)
|
||||||
|
val insulinProgramElements = ProgramTempBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(
|
||||||
|
tenthPulsesPerSlot
|
||||||
|
)
|
||||||
|
val interlockCommand = ProgramInsulinCommand(
|
||||||
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
nonce!!,
|
||||||
|
shortInsulinProgramElements,
|
||||||
|
ProgramTempBasalUtil.calculateChecksum(durationInSlots, pulsesPerSlot[0], pulsesPerSlot),
|
||||||
|
durationInSlots,
|
||||||
|
0x3840.toShort(),
|
||||||
|
pulsesPerSlot[0],
|
||||||
|
ProgramInsulinCommand.DeliveryType.TEMP_BASAL
|
||||||
|
)
|
||||||
|
return ProgramTempBasalCommand(
|
||||||
|
interlockCommand,
|
||||||
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
programReminder!!,
|
||||||
|
insulinProgramElements
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,10 +96,12 @@ class ProgramTempBasalCommand private constructor(
|
||||||
val delayUntilNextTenthPulseInUsec: Int
|
val delayUntilNextTenthPulseInUsec: Int
|
||||||
if (firstProgramElement.totalTenthPulses.toInt() == 0) {
|
if (firstProgramElement.totalTenthPulses.toInt() == 0) {
|
||||||
remainingTenthPulsesInFirstElement = firstProgramElement.numberOfSlots.toShort()
|
remainingTenthPulsesInFirstElement = firstProgramElement.numberOfSlots.toShort()
|
||||||
delayUntilNextTenthPulseInUsec = ProgramBasalUtil.MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT
|
delayUntilNextTenthPulseInUsec =
|
||||||
|
ProgramBasalUtil.MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT
|
||||||
} else {
|
} else {
|
||||||
remainingTenthPulsesInFirstElement = firstProgramElement.totalTenthPulses
|
remainingTenthPulsesInFirstElement = firstProgramElement.totalTenthPulses
|
||||||
delayUntilNextTenthPulseInUsec = (firstProgramElement.numberOfSlots.toLong() * 1800.0 / remainingTenthPulsesInFirstElement * 1000000).toInt()
|
delayUntilNextTenthPulseInUsec =
|
||||||
|
(firstProgramElement.numberOfSlots.toLong() * 1800.0 / remainingTenthPulsesInFirstElement * 1000000).toInt()
|
||||||
}
|
}
|
||||||
val buffer = ByteBuffer.allocate(getLength().toInt()) //
|
val buffer = ByteBuffer.allocate(getLength().toInt()) //
|
||||||
.put(commandType.value) //
|
.put(commandType.value) //
|
||||||
|
@ -90,11 +115,18 @@ class ProgramTempBasalCommand private constructor(
|
||||||
}
|
}
|
||||||
val tempBasalCommand = buffer.array()
|
val tempBasalCommand = buffer.array()
|
||||||
val interlockCommand = interlockCommand.encoded
|
val interlockCommand = interlockCommand.encoded
|
||||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (tempBasalCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
|
val header: ByteArray = encodeHeader(
|
||||||
return appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + tempBasalCommand.size) //
|
uniqueId,
|
||||||
.put(header) //
|
sequenceNumber,
|
||||||
.put(interlockCommand) //
|
(tempBasalCommand.size + interlockCommand.size).toShort(),
|
||||||
.put(tempBasalCommand) //
|
multiCommandFlag
|
||||||
.array())
|
)
|
||||||
|
return appendCrc(
|
||||||
|
ByteBuffer.allocate(header.size + interlockCommand.size + tempBasalCommand.size) //
|
||||||
|
.put(header) //
|
||||||
|
.put(interlockCommand) //
|
||||||
|
.put(tempBasalCommand) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,17 +16,19 @@ class SetUniqueIdCommand private constructor(
|
||||||
) : HeaderEnabledCommand(CommandType.SET_UNIQUE_ID, uniqueId, sequenceNumber, multiCommandFlag) {
|
) : HeaderEnabledCommand(CommandType.SET_UNIQUE_ID, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
get() = appendCrc(
|
||||||
.put(encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
|
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||||
.put(commandType.value) //
|
.put(encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||||
.put(BODY_LENGTH) //
|
.put(commandType.value) //
|
||||||
.putInt(uniqueId) //
|
.put(BODY_LENGTH) //
|
||||||
.put(0x14.toByte()) // FIXME ??
|
.putInt(uniqueId) //
|
||||||
.put(0x04.toByte()) // FIXME ??
|
.put(0x14.toByte()) // FIXME ??
|
||||||
.put(encodeInitializationTime(initializationTime)) //
|
.put(0x04.toByte()) // FIXME ??
|
||||||
.putInt(lotNumber) //
|
.put(encodeInitializationTime(initializationTime)) //
|
||||||
.putInt(podSequenceNumber) //
|
.putInt(lotNumber) //
|
||||||
.array())
|
.putInt(podSequenceNumber) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "SetUniqueIdCommand{" +
|
return "SetUniqueIdCommand{" +
|
||||||
|
@ -65,7 +67,14 @@ class SetUniqueIdCommand private constructor(
|
||||||
requireNotNull(lotNumber) { "lotNumber can not be null" }
|
requireNotNull(lotNumber) { "lotNumber can not be null" }
|
||||||
requireNotNull(podSequenceNumber) { "podSequenceNumber can not be null" }
|
requireNotNull(podSequenceNumber) { "podSequenceNumber can not be null" }
|
||||||
requireNotNull(initializationTime) { "initializationTime can not be null" }
|
requireNotNull(initializationTime) { "initializationTime can not be null" }
|
||||||
return SetUniqueIdCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, lotNumber!!, podSequenceNumber!!, initializationTime!!)
|
return SetUniqueIdCommand(
|
||||||
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
lotNumber!!,
|
||||||
|
podSequenceNumber!!,
|
||||||
|
initializationTime!!
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,10 +87,10 @@ class SetUniqueIdCommand private constructor(
|
||||||
val instance = Calendar.getInstance()
|
val instance = Calendar.getInstance()
|
||||||
instance.time = date
|
instance.time = date
|
||||||
return byteArrayOf( //
|
return byteArrayOf( //
|
||||||
(instance[Calendar.MONTH] + 1).toByte(), //
|
(instance[Calendar.MONTH] + 1).toByte(), //
|
||||||
instance[Calendar.DATE].toByte(), //
|
instance[Calendar.DATE].toByte(), //
|
||||||
(instance[Calendar.YEAR] % 100).toByte(), //
|
(instance[Calendar.YEAR] % 100).toByte(), //
|
||||||
instance[Calendar.HOUR_OF_DAY].toByte(), //
|
instance[Calendar.HOUR_OF_DAY].toByte(), //
|
||||||
instance[Calendar.MINUTE].toByte() //
|
instance[Calendar.MINUTE].toByte() //
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,15 @@ class SilenceAlertsCommand private constructor(
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() =
|
get() =
|
||||||
appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
appendCrc(
|
||||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||||
.put(commandType.value) //
|
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||||
.put(BODY_LENGTH) //
|
.put(commandType.value) //
|
||||||
.putInt(nonce) //
|
.put(BODY_LENGTH) //
|
||||||
.put(AlertUtil.encodeAlertSet(alertTypes)) //
|
.putInt(nonce) //
|
||||||
.array())
|
.put(AlertUtil.encodeAlertSet(alertTypes)) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "SilenceAlertsCommand{" +
|
return "SilenceAlertsCommand{" +
|
||||||
|
|
|
@ -19,13 +19,15 @@ class StopDeliveryCommand private constructor(
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() {
|
get() {
|
||||||
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
return appendCrc(
|
||||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||||
.put(commandType.value) //
|
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||||
.put(BODY_LENGTH) //
|
.put(commandType.value) //
|
||||||
.putInt(nonce) //
|
.put(BODY_LENGTH) //
|
||||||
.put((beepType.value.toInt() shl 4 or deliveryType.encoded[0].toInt()).toByte()) //
|
.putInt(nonce) //
|
||||||
.array())
|
.put((beepType.value.toInt() shl 4 or deliveryType.encoded[0].toInt()).toByte()) //
|
||||||
|
.array()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
@ -40,7 +42,12 @@ class StopDeliveryCommand private constructor(
|
||||||
'}'
|
'}'
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class DeliveryType(private val basal: Boolean, private val tempBasal: Boolean, private val bolus: Boolean) : Encodable {
|
enum class DeliveryType(
|
||||||
|
private val basal: Boolean,
|
||||||
|
private val tempBasal: Boolean,
|
||||||
|
private val bolus: Boolean
|
||||||
|
) : Encodable {
|
||||||
|
|
||||||
BASAL(true, false, false), TEMP_BASAL(false, true, false), BOLUS(false, false, true), ALL(true, true, true);
|
BASAL(true, false, false), TEMP_BASAL(false, true, false), BOLUS(false, false, true), ALL(true, true, true);
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
|
@ -72,7 +79,14 @@ class StopDeliveryCommand private constructor(
|
||||||
requireNotNull(deliveryType) { "deliveryType can not be null" }
|
requireNotNull(deliveryType) { "deliveryType can not be null" }
|
||||||
requireNotNull(beepType) { "beepType can not be null" }
|
requireNotNull(beepType) { "beepType can not be null" }
|
||||||
|
|
||||||
return StopDeliveryCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, deliveryType!!, beepType!!, nonce!!)
|
return StopDeliveryCommand(
|
||||||
|
uniqueId!!,
|
||||||
|
sequenceNumber!!,
|
||||||
|
multiCommandFlag,
|
||||||
|
deliveryType!!,
|
||||||
|
beepType!!,
|
||||||
|
nonce!!
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@ enum class CommandType(val value: Byte) {
|
||||||
GET_VERSION(0x07.toByte()),
|
GET_VERSION(0x07.toByte()),
|
||||||
GET_STATUS(0x0e.toByte()),
|
GET_STATUS(0x0e.toByte()),
|
||||||
SILENCE_ALERTS(0x11.toByte()),
|
SILENCE_ALERTS(0x11.toByte()),
|
||||||
PROGRAM_BASAL(0x13.toByte()), // Always preceded by 0x1a
|
PROGRAM_BASAL(0x13.toByte()), // Always preceded by 0x1a
|
||||||
PROGRAM_TEMP_BASAL(0x16.toByte()), // Always preceded by 0x1a
|
PROGRAM_TEMP_BASAL(0x16.toByte()), // Always preceded by 0x1a
|
||||||
PROGRAM_BOLUS(0x17.toByte()), // Always preceded by 0x1a
|
PROGRAM_BOLUS(0x17.toByte()), // Always preceded by 0x1a
|
||||||
PROGRAM_ALERTS(0x19.toByte()),
|
PROGRAM_ALERTS(0x19.toByte()),
|
||||||
PROGRAM_INSULIN(0x1a.toByte()), // Always followed by one of: 0x13, 0x16, 0x17
|
PROGRAM_INSULIN(0x1a.toByte()), // Always followed by one of: 0x13, 0x16, 0x17
|
||||||
DEACTIVATE(0x1c.toByte()),
|
DEACTIVATE(0x1c.toByte()),
|
||||||
PROGRAM_BEEPS(0x1e.toByte()),
|
PROGRAM_BEEPS(0x1e.toByte()),
|
||||||
STOP_DELIVERY(0x1f.toByte());
|
STOP_DELIVERY(0x1f.toByte());
|
||||||
|
|
|
@ -18,7 +18,12 @@ abstract class HeaderEnabledCommand protected constructor(
|
||||||
.putShort(MessageUtil.createCrc(command)) //
|
.putShort(MessageUtil.createCrc(command)) //
|
||||||
.array()
|
.array()
|
||||||
|
|
||||||
internal fun encodeHeader(uniqueId: Int, sequenceNumber: Short, length: Short, multiCommandFlag: Boolean): ByteArray =
|
internal fun encodeHeader(
|
||||||
|
uniqueId: Int,
|
||||||
|
sequenceNumber: Short,
|
||||||
|
length: Short,
|
||||||
|
multiCommandFlag: Boolean
|
||||||
|
): ByteArray =
|
||||||
ByteBuffer.allocate(6) //
|
ByteBuffer.allocate(6) //
|
||||||
.putInt(uniqueId) //
|
.putInt(uniqueId) //
|
||||||
.putShort((sequenceNumber.toInt() and 0x0f shl 10 or length.toInt() or ((if (multiCommandFlag) 1 else 0) shl 15)).toShort()) //
|
.putShort((sequenceNumber.toInt() and 0x0f shl 10 or length.toInt() or ((if (multiCommandFlag) 1 else 0) shl 15)).toShort()) //
|
||||||
|
|
|
@ -5,15 +5,17 @@ import kotlin.experimental.and
|
||||||
|
|
||||||
class BasalShortInsulinProgramElement(
|
class BasalShortInsulinProgramElement(
|
||||||
private val numberOfSlots: Byte, // 4 bits
|
private val numberOfSlots: Byte, // 4 bits
|
||||||
private val pulsesPerSlot: Short, //10 bits
|
private val pulsesPerSlot: Short, // 10 bits
|
||||||
private val extraAlternatePulse: Boolean
|
private val extraAlternatePulse: Boolean
|
||||||
) : ShortInsulinProgramElement {
|
) : ShortInsulinProgramElement {
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() {
|
get() {
|
||||||
val firstByte = (numberOfSlots - 1 and 0x0f shl 4 //
|
val firstByte = (
|
||||||
or ((if (extraAlternatePulse) 1 else 0) shl 3) //
|
numberOfSlots - 1 and 0x0f shl 4 //
|
||||||
or (pulsesPerSlot.toInt() ushr 8 and 0x03)).toByte()
|
or ((if (extraAlternatePulse) 1 else 0) shl 3) //
|
||||||
|
or (pulsesPerSlot.toInt() ushr 8 and 0x03)
|
||||||
|
).toByte()
|
||||||
return ByteBuffer.allocate(2) //
|
return ByteBuffer.allocate(2) //
|
||||||
.put(firstByte) //
|
.put(firstByte) //
|
||||||
.put((pulsesPerSlot and 0xff).toByte()) //
|
.put((pulsesPerSlot and 0xff).toByte()) //
|
||||||
|
|
|
@ -30,7 +30,13 @@ object ProgramBasalUtil {
|
||||||
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]
|
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]
|
||||||
numberOfSlotsInCurrentElement = 1
|
numberOfSlotsInCurrentElement = 1
|
||||||
} else if (previousTenthPulsesPerSlot != tenthPulsesPerSlot[i] || (numberOfSlotsInCurrentElement + 1) * previousTenthPulsesPerSlot > 65534) {
|
} else if (previousTenthPulsesPerSlot != tenthPulsesPerSlot[i] || (numberOfSlotsInCurrentElement + 1) * previousTenthPulsesPerSlot > 65534) {
|
||||||
elements.add(insulinProgramElementFactory(startSlotIndex, numberOfSlotsInCurrentElement, (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement).toShort()))
|
elements.add(
|
||||||
|
insulinProgramElementFactory(
|
||||||
|
startSlotIndex,
|
||||||
|
numberOfSlotsInCurrentElement,
|
||||||
|
(previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement).toShort()
|
||||||
|
)
|
||||||
|
)
|
||||||
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]
|
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]
|
||||||
numberOfSlotsInCurrentElement = 1
|
numberOfSlotsInCurrentElement = 1
|
||||||
startSlotIndex = (numberOfSlotsInCurrentElement + startSlotIndex).toByte()
|
startSlotIndex = (numberOfSlotsInCurrentElement + startSlotIndex).toByte()
|
||||||
|
@ -38,7 +44,13 @@ object ProgramBasalUtil {
|
||||||
numberOfSlotsInCurrentElement++
|
numberOfSlotsInCurrentElement++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elements.add(insulinProgramElementFactory(startSlotIndex, numberOfSlotsInCurrentElement, (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement).toShort()))
|
elements.add(
|
||||||
|
insulinProgramElementFactory(
|
||||||
|
startSlotIndex,
|
||||||
|
numberOfSlotsInCurrentElement,
|
||||||
|
(previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement).toShort()
|
||||||
|
)
|
||||||
|
)
|
||||||
return elements
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +72,13 @@ object ProgramBasalUtil {
|
||||||
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) {
|
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) {
|
||||||
numberOfSlotsInCurrentElement++
|
numberOfSlotsInCurrentElement++
|
||||||
} else {
|
} else {
|
||||||
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
|
elements.add(
|
||||||
|
BasalShortInsulinProgramElement(
|
||||||
|
numberOfSlotsInCurrentElement,
|
||||||
|
previousPulsesPerSlot,
|
||||||
|
extraAlternatePulse
|
||||||
|
)
|
||||||
|
)
|
||||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
||||||
numberOfSlotsInCurrentElement = 1
|
numberOfSlotsInCurrentElement = 1
|
||||||
extraAlternatePulse = false
|
extraAlternatePulse = false
|
||||||
|
@ -82,7 +100,13 @@ object ProgramBasalUtil {
|
||||||
numberOfSlotsInCurrentElement++
|
numberOfSlotsInCurrentElement++
|
||||||
} else {
|
} else {
|
||||||
// End of alternate pulse segment (no slots left in element)
|
// End of alternate pulse segment (no slots left in element)
|
||||||
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
|
elements.add(
|
||||||
|
BasalShortInsulinProgramElement(
|
||||||
|
numberOfSlotsInCurrentElement,
|
||||||
|
previousPulsesPerSlot,
|
||||||
|
extraAlternatePulse
|
||||||
|
)
|
||||||
|
)
|
||||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
||||||
numberOfSlotsInCurrentElement = 1
|
numberOfSlotsInCurrentElement = 1
|
||||||
extraAlternatePulse = false
|
extraAlternatePulse = false
|
||||||
|
@ -90,7 +114,13 @@ object ProgramBasalUtil {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// End of alternate pulse segment (new number of pulses per slot)
|
// End of alternate pulse segment (new number of pulses per slot)
|
||||||
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
|
elements.add(
|
||||||
|
BasalShortInsulinProgramElement(
|
||||||
|
numberOfSlotsInCurrentElement,
|
||||||
|
previousPulsesPerSlot,
|
||||||
|
extraAlternatePulse
|
||||||
|
)
|
||||||
|
)
|
||||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
||||||
numberOfSlotsInCurrentElement = 1
|
numberOfSlotsInCurrentElement = 1
|
||||||
extraAlternatePulse = false
|
extraAlternatePulse = false
|
||||||
|
@ -100,7 +130,13 @@ object ProgramBasalUtil {
|
||||||
}
|
}
|
||||||
} else if (previousPulsesPerSlot != pulsesPerSlot[currentTotalNumberOfSlots.toInt()]) {
|
} else if (previousPulsesPerSlot != pulsesPerSlot[currentTotalNumberOfSlots.toInt()]) {
|
||||||
// End of segment (new number of pulses per slot)
|
// End of segment (new number of pulses per slot)
|
||||||
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
|
elements.add(
|
||||||
|
BasalShortInsulinProgramElement(
|
||||||
|
numberOfSlotsInCurrentElement,
|
||||||
|
previousPulsesPerSlot,
|
||||||
|
extraAlternatePulse
|
||||||
|
)
|
||||||
|
)
|
||||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
||||||
currentTotalNumberOfSlots++
|
currentTotalNumberOfSlots++
|
||||||
extraAlternatePulse = false
|
extraAlternatePulse = false
|
||||||
|
@ -109,7 +145,13 @@ object ProgramBasalUtil {
|
||||||
throw IllegalStateException("Reached illegal point in mapBasalProgramToShortInsulinProgramElements")
|
throw IllegalStateException("Reached illegal point in mapBasalProgramToShortInsulinProgramElements")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
|
elements.add(
|
||||||
|
BasalShortInsulinProgramElement(
|
||||||
|
numberOfSlotsInCurrentElement,
|
||||||
|
previousPulsesPerSlot,
|
||||||
|
extraAlternatePulse
|
||||||
|
)
|
||||||
|
)
|
||||||
return elements
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +159,8 @@ object ProgramBasalUtil {
|
||||||
val tenthPulsesPerSlot = ShortArray(NUMBER_OF_BASAL_SLOTS.toInt())
|
val tenthPulsesPerSlot = ShortArray(NUMBER_OF_BASAL_SLOTS.toInt())
|
||||||
for (segment in basalProgram.segments) {
|
for (segment in basalProgram.segments) {
|
||||||
for (i in segment.startSlotIndex until segment.endSlotIndex) {
|
for (i in segment.startSlotIndex until segment.endSlotIndex) {
|
||||||
tenthPulsesPerSlot[i] = (roundToHalf(segment.getPulsesPerHour() / 2.0) * 10).toInt().toShort() // TODO Adrian: int conversion ok?
|
tenthPulsesPerSlot[i] = (roundToHalf(segment.getPulsesPerHour() / 2.0) * 10).toInt()
|
||||||
|
.toShort() // TODO Adrian: int conversion ok?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tenthPulsesPerSlot
|
return tenthPulsesPerSlot
|
||||||
|
@ -157,7 +200,10 @@ object ProgramBasalUtil {
|
||||||
return CurrentSlot(index, (secondsRemaining * 8).toShort(), pulsesRemaining)
|
return CurrentSlot(index, (secondsRemaining * 8).toShort(), pulsesRemaining)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateCurrentLongInsulinProgramElement(elements: List<BasalInsulinProgramElement>, currentTime: Date?): CurrentBasalInsulinProgramElement {
|
fun calculateCurrentLongInsulinProgramElement(
|
||||||
|
elements: List<BasalInsulinProgramElement>,
|
||||||
|
currentTime: Date?
|
||||||
|
): CurrentBasalInsulinProgramElement {
|
||||||
val instance = Calendar.getInstance()
|
val instance = Calendar.getInstance()
|
||||||
instance.time = currentTime
|
instance.time = currentTime
|
||||||
val hourOfDay = instance[Calendar.HOUR_OF_DAY]
|
val hourOfDay = instance[Calendar.HOUR_OF_DAY]
|
||||||
|
@ -176,8 +222,10 @@ object ProgramBasalUtil {
|
||||||
}
|
}
|
||||||
val durationInSeconds = endTimeInSeconds - startTimeInSeconds
|
val durationInSeconds = endTimeInSeconds - startTimeInSeconds
|
||||||
val secondsPassedInCurrentSlot = secondOfDay - startTimeInSeconds
|
val secondsPassedInCurrentSlot = secondOfDay - startTimeInSeconds
|
||||||
val remainingTenThousandthPulses = ((durationInSeconds - secondsPassedInCurrentSlot) / durationInSeconds.toDouble() * totalNumberOfTenThousandthPulsesInSlot).toLong()
|
val remainingTenThousandthPulses =
|
||||||
val delayBetweenTenthPulsesInUsec = (durationInSeconds * 1000000L * 1000 / totalNumberOfTenThousandthPulsesInSlot).toInt()
|
((durationInSeconds - secondsPassedInCurrentSlot) / durationInSeconds.toDouble() * totalNumberOfTenThousandthPulsesInSlot).toLong()
|
||||||
|
val delayBetweenTenthPulsesInUsec =
|
||||||
|
(durationInSeconds * 1000000L * 1000 / totalNumberOfTenThousandthPulsesInSlot).toInt()
|
||||||
val secondsRemaining = secondsPassedInCurrentSlot % 1800
|
val secondsRemaining = secondsPassedInCurrentSlot % 1800
|
||||||
var delayUntilNextTenthPulseInUsec = delayBetweenTenthPulsesInUsec
|
var delayUntilNextTenthPulseInUsec = delayBetweenTenthPulsesInUsec
|
||||||
for (i in 0 until secondsRemaining) {
|
for (i in 0 until secondsRemaining) {
|
||||||
|
@ -186,7 +234,8 @@ object ProgramBasalUtil {
|
||||||
delayUntilNextTenthPulseInUsec += delayBetweenTenthPulsesInUsec
|
delayUntilNextTenthPulseInUsec += delayBetweenTenthPulsesInUsec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val remainingTenthPulses = ((if (remainingTenThousandthPulses % 1000 != 0L) 1 else 0) + remainingTenThousandthPulses / 1000).toShort()
|
val remainingTenthPulses =
|
||||||
|
((if (remainingTenThousandthPulses % 1000 != 0L) 1 else 0) + remainingTenThousandthPulses / 1000).toShort()
|
||||||
return CurrentBasalInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses)
|
return CurrentBasalInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses)
|
||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
|
|
|
@ -10,7 +10,13 @@ import kotlin.math.roundToInt
|
||||||
object ProgramTempBasalUtil {
|
object ProgramTempBasalUtil {
|
||||||
|
|
||||||
fun mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot: ShortArray): List<BasalInsulinProgramElement> {
|
fun mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot: ShortArray): List<BasalInsulinProgramElement> {
|
||||||
return ProgramBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot) { startSlotIndex: Byte, numberOfSlots: Byte, totalTenthPulses: Short -> TempBasalInsulinProgramElement(startSlotIndex, numberOfSlots, totalTenthPulses) }
|
return ProgramBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot) { startSlotIndex: Byte, numberOfSlots: Byte, totalTenthPulses: Short ->
|
||||||
|
TempBasalInsulinProgramElement(
|
||||||
|
startSlotIndex,
|
||||||
|
numberOfSlots,
|
||||||
|
totalTenthPulses
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mapTempBasalToTenthPulsesPerSlot(durationInSlots: Int, rateInUnitsPerHour: Double): ShortArray {
|
fun mapTempBasalToTenthPulsesPerSlot(durationInSlots: Int, rateInUnitsPerHour: Double): ShortArray {
|
||||||
|
|
|
@ -8,10 +8,9 @@ enum class ActivationProgress {
|
||||||
REPROGRAMMED_LUMP_OF_COAL_ALERT,
|
REPROGRAMMED_LUMP_OF_COAL_ALERT,
|
||||||
PRIMING,
|
PRIMING,
|
||||||
PRIME_COMPLETED,
|
PRIME_COMPLETED,
|
||||||
PROGRAMMED_USER_SET_EXPIRATION_ALERT,
|
|
||||||
PHASE_1_COMPLETED,
|
PHASE_1_COMPLETED,
|
||||||
PROGRAMMED_BASAL,
|
PROGRAMMED_BASAL,
|
||||||
PROGRAMMED_CANCEL_LOC_ETC_ALERT,
|
UPDATED_EXPIRATION_ALERTS,
|
||||||
INSERTING_CANNULA,
|
INSERTING_CANNULA,
|
||||||
CANNULA_INSERTED,
|
CANNULA_INSERTED,
|
||||||
COMPLETED;
|
COMPLETED;
|
||||||
|
|
|
@ -159,5 +159,4 @@ enum class AlarmType(override val value: Byte) : HasValue {
|
||||||
ALARM_BLE_QN_EXCEED_MAX_RETRY(0xc1.toByte()),
|
ALARM_BLE_QN_EXCEED_MAX_RETRY(0xc1.toByte()),
|
||||||
ALARM_BLE_QN_CRIT_VAR_FAIL(0xc2.toByte()),
|
ALARM_BLE_QN_CRIT_VAR_FAIL(0xc2.toByte()),
|
||||||
UNKNOWN(0xff.toByte());
|
UNKNOWN(0xff.toByte());
|
||||||
|
|
||||||
}
|
}
|
|
@ -29,15 +29,17 @@ class AlertConfiguration(
|
||||||
return ByteBuffer.allocate(6) //
|
return ByteBuffer.allocate(6) //
|
||||||
.put(firstByte)
|
.put(firstByte)
|
||||||
.put(durationInMinutes.toByte()) //
|
.put(durationInMinutes.toByte()) //
|
||||||
.putShort(when (trigger) {
|
.putShort(
|
||||||
is AlertTrigger.ReservoirVolumeTrigger -> {
|
when (trigger) {
|
||||||
trigger.thresholdInMicroLiters
|
is AlertTrigger.ReservoirVolumeTrigger -> {
|
||||||
}
|
trigger.thresholdInMicroLiters
|
||||||
|
}
|
||||||
|
|
||||||
is AlertTrigger.TimerTrigger -> {
|
is AlertTrigger.TimerTrigger -> {
|
||||||
trigger.offsetInMinutes
|
trigger.offsetInMinutes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}) //
|
) //
|
||||||
.put(beepRepetition.value) //
|
.put(beepRepetition.value) //
|
||||||
.put(beepType.value) //
|
.put(beepType.value) //
|
||||||
.array()
|
.array()
|
||||||
|
|
|
@ -10,5 +10,4 @@ enum class BeepRepetitionType(
|
||||||
XXX3(0x05.toByte()), // Used in user pod expiration alert
|
XXX3(0x05.toByte()), // Used in user pod expiration alert
|
||||||
XXX4(0x06.toByte()), // Used in pod expiration alert
|
XXX4(0x06.toByte()), // Used in pod expiration alert
|
||||||
XXX5(0x08.toByte()); // Used in imminent pod expiration alert
|
XXX5(0x08.toByte()); // Used in imminent pod expiration alert
|
||||||
|
|
||||||
}
|
}
|
|
@ -10,7 +10,11 @@ class ProgramReminder(
|
||||||
) : Encodable, Serializable {
|
) : Encodable, Serializable {
|
||||||
|
|
||||||
override val encoded: ByteArray
|
override val encoded: ByteArray
|
||||||
get() = byteArrayOf(((if (atStart) 1 else 0) shl 7
|
get() = byteArrayOf(
|
||||||
or ((if (atEnd) 1 else 0) shl 6)
|
(
|
||||||
or ((atInterval and 0x3f).toInt())).toByte())
|
(if (atStart) 1 else 0) shl 7
|
||||||
|
or ((if (atEnd) 1 else 0) shl 6)
|
||||||
|
or ((atInterval and 0x3f).toInt())
|
||||||
|
).toByte()
|
||||||
|
)
|
||||||
}
|
}
|
|
@ -84,5 +84,4 @@ class AlarmStatusResponse(
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun Byte.shr(i: Int): Int = toInt() shr i
|
infix fun Byte.shr(i: Int): Int = toInt() shr i
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,11 +15,14 @@ class DefaultStatusResponse(
|
||||||
val messageType: Byte = encoded[0]
|
val messageType: Byte = encoded[0]
|
||||||
val deliveryStatus: DeliveryStatus = byValue((encoded[1].toInt() shr 4 and 0x0f).toByte(), DeliveryStatus.UNKNOWN)
|
val deliveryStatus: DeliveryStatus = byValue((encoded[1].toInt() shr 4 and 0x0f).toByte(), DeliveryStatus.UNKNOWN)
|
||||||
val podStatus: PodStatus = byValue((encoded[1] and 0x0f), PodStatus.UNKNOWN)
|
val podStatus: PodStatus = byValue((encoded[1] and 0x0f), PodStatus.UNKNOWN)
|
||||||
val totalPulsesDelivered: Short = ((encoded[2] and 0x0f shl 12 or (encoded[3].toInt() and 0xff shl 1) or (encoded[4].toInt() and 0xff ushr 7)).toShort())
|
val totalPulsesDelivered: Short =
|
||||||
|
((encoded[2] and 0x0f shl 12 or (encoded[3].toInt() and 0xff shl 1) or (encoded[4].toInt() and 0xff ushr 7)).toShort())
|
||||||
val sequenceNumberOfLastProgrammingCommand: Short = (encoded[4] ushr 3 and 0x0f).toShort()
|
val sequenceNumberOfLastProgrammingCommand: Short = (encoded[4] ushr 3 and 0x0f).toShort()
|
||||||
val bolusPulsesRemaining: Short = ((encoded[4] and 0x07 shl 10 or (encoded[5].toInt() and 0xff) and 2047).toShort())
|
val bolusPulsesRemaining: Short = ((encoded[4] and 0x07 shl 10 or (encoded[5].toInt() and 0xff) and 2047).toShort())
|
||||||
val activeAlerts: EnumSet<AlertType> = AlertUtil.decodeAlertSet((encoded[6].toInt() and 0xff shl 1 or (encoded[7] ushr 7)).toByte())
|
val activeAlerts: EnumSet<AlertType> =
|
||||||
val minutesSinceActivation: Short = (encoded[7] and 0x7f shl 6 or (encoded[8].toInt() and 0xff ushr 2 and 0x3f)).toShort()
|
AlertUtil.decodeAlertSet((encoded[6].toInt() and 0xff shl 1 or (encoded[7] ushr 7)).toByte())
|
||||||
|
val minutesSinceActivation: Short =
|
||||||
|
(encoded[7] and 0x7f shl 6 or (encoded[8].toInt() and 0xff ushr 2 and 0x3f)).toShort()
|
||||||
val reservoirPulsesRemaining: Short = (encoded[8] shl 8 or encoded[9].toInt() and 0x3ff).toShort()
|
val reservoirPulsesRemaining: Short = (encoded[8] shl 8 or encoded[9].toInt() and 0x3ff).toShort()
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
@ -34,7 +37,6 @@ class DefaultStatusResponse(
|
||||||
", minutesSinceActivation=$minutesSinceActivation" +
|
", minutesSinceActivation=$minutesSinceActivation" +
|
||||||
", reservoirPulsesRemaining=$reservoirPulsesRemaining)"
|
", reservoirPulsesRemaining=$reservoirPulsesRemaining)"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun Byte.ushr(i: Int) = toInt() ushr i
|
infix fun Byte.ushr(i: Int) = toInt() ushr i
|
||||||
|
|
|
@ -6,5 +6,4 @@ abstract class ResponseBase(
|
||||||
) : Response {
|
) : Response {
|
||||||
|
|
||||||
override val encoded: ByteArray = encoded.copyOf(encoded.size)
|
override val encoded: ByteArray = encoded.copyOf(encoded.size)
|
||||||
|
|
||||||
}
|
}
|
|
@ -25,9 +25,42 @@ class SetUniqueIdResponse(
|
||||||
val bleVersionInterim: Short = (encoded[14].toInt() and 0xff).toShort()
|
val bleVersionInterim: Short = (encoded[14].toInt() and 0xff).toShort()
|
||||||
val productId: Short = (encoded[15].toInt() and 0xff).toShort()
|
val productId: Short = (encoded[15].toInt() and 0xff).toShort()
|
||||||
val podStatus: PodStatus = byValue(encoded[16], PodStatus.UNKNOWN)
|
val podStatus: PodStatus = byValue(encoded[16], PodStatus.UNKNOWN)
|
||||||
val lotNumber: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[17], encoded[18], encoded[19], encoded[20])).long
|
val lotNumber: Long = ByteBuffer.wrap(
|
||||||
val podSequenceNumber: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[21], encoded[22], encoded[23], encoded[24])).long
|
byteArrayOf(
|
||||||
val uniqueIdReceivedInCommand: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[25], encoded[26], encoded[27], encoded[28])).long
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
encoded[17],
|
||||||
|
encoded[18],
|
||||||
|
encoded[19],
|
||||||
|
encoded[20]
|
||||||
|
)
|
||||||
|
).long
|
||||||
|
val podSequenceNumber: Long = ByteBuffer.wrap(
|
||||||
|
byteArrayOf(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
encoded[21],
|
||||||
|
encoded[22],
|
||||||
|
encoded[23],
|
||||||
|
encoded[24]
|
||||||
|
)
|
||||||
|
).long
|
||||||
|
val uniqueIdReceivedInCommand: Long = ByteBuffer.wrap(
|
||||||
|
byteArrayOf(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
encoded[25],
|
||||||
|
encoded[26],
|
||||||
|
encoded[27],
|
||||||
|
encoded[28]
|
||||||
|
)
|
||||||
|
).long
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "SetUniqueIdResponse{" +
|
return "SetUniqueIdResponse{" +
|
||||||
|
@ -55,5 +88,4 @@ class SetUniqueIdResponse(
|
||||||
", encoded=" + encoded.contentToString() +
|
", encoded=" + encoded.contentToString() +
|
||||||
'}'
|
'}'
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,11 +21,44 @@ class VersionResponse(
|
||||||
val bleVersionInterim: Short = (encoded[7].toInt() and 0xff).toShort()
|
val bleVersionInterim: Short = (encoded[7].toInt() and 0xff).toShort()
|
||||||
val productId: Short = (encoded[8].toInt() and 0xff).toShort()
|
val productId: Short = (encoded[8].toInt() and 0xff).toShort()
|
||||||
val podStatus: PodStatus = byValue((encoded[9] and 0xf), PodStatus.UNKNOWN)
|
val podStatus: PodStatus = byValue((encoded[9] and 0xf), PodStatus.UNKNOWN)
|
||||||
val lotNumber: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[10], encoded[11], encoded[12], encoded[13])).long
|
val lotNumber: Long = ByteBuffer.wrap(
|
||||||
val podSequenceNumber: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[14], encoded[15], encoded[16], encoded[17])).long
|
byteArrayOf(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
encoded[10],
|
||||||
|
encoded[11],
|
||||||
|
encoded[12],
|
||||||
|
encoded[13]
|
||||||
|
)
|
||||||
|
).long
|
||||||
|
val podSequenceNumber: Long = ByteBuffer.wrap(
|
||||||
|
byteArrayOf(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
encoded[14],
|
||||||
|
encoded[15],
|
||||||
|
encoded[16],
|
||||||
|
encoded[17]
|
||||||
|
)
|
||||||
|
).long
|
||||||
val rssi: Byte = (encoded[18] and 0x3f)
|
val rssi: Byte = (encoded[18] and 0x3f)
|
||||||
val receiverLowerGain: Byte = ((encoded[18].toInt() shr 6 and 0x03).toByte())
|
val receiverLowerGain: Byte = ((encoded[18].toInt() shr 6 and 0x03).toByte())
|
||||||
val uniqueIdReceivedInCommand: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[19], encoded[20], encoded[21], encoded[22])).long
|
val uniqueIdReceivedInCommand: Long = ByteBuffer.wrap(
|
||||||
|
byteArrayOf(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
encoded[19],
|
||||||
|
encoded[20],
|
||||||
|
encoded[21],
|
||||||
|
encoded[22]
|
||||||
|
)
|
||||||
|
).long
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "VersionResponse{" +
|
return "VersionResponse{" +
|
||||||
|
@ -49,5 +82,4 @@ class VersionResponse(
|
||||||
", encoded=" + Arrays.toString(encoded) +
|
", encoded=" + Arrays.toString(encoded) +
|
||||||
'}'
|
'}'
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@ interface OmnipodDashPodStateManager {
|
||||||
val messageSequenceNumber: Short
|
val messageSequenceNumber: Short
|
||||||
val sequenceNumberOfLastProgrammingCommand: Short?
|
val sequenceNumberOfLastProgrammingCommand: Short?
|
||||||
val activationTime: Long?
|
val activationTime: Long?
|
||||||
var uniqueId: Long?
|
var uniqueId: Long? // TODO make Int
|
||||||
var bluetoothAddress: String?
|
var bluetoothAddress: String?
|
||||||
|
|
||||||
val bluetoothVersion: SoftwareVersion?
|
val bluetoothVersion: SoftwareVersion?
|
||||||
|
|
|
@ -3,6 +3,8 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.EventOmnipodDashPumpValuesChanged
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse
|
||||||
|
@ -18,7 +20,8 @@ import javax.inject.Singleton
|
||||||
@Singleton
|
@Singleton
|
||||||
class OmnipodDashPodStateManagerImpl @Inject constructor(
|
class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
private val logger: AAPSLogger,
|
private val logger: AAPSLogger,
|
||||||
private val sharedPreferences: SP
|
private val sharedPreferences: SP,
|
||||||
|
private val rxBus: RxBusWrapper
|
||||||
) : OmnipodDashPodStateManager {
|
) : OmnipodDashPodStateManager {
|
||||||
|
|
||||||
private var podState: PodState
|
private var podState: PodState
|
||||||
|
@ -158,17 +161,27 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
|
|
||||||
podState.lastUpdated = System.currentTimeMillis()
|
podState.lastUpdated = System.currentTimeMillis()
|
||||||
store()
|
store()
|
||||||
|
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFromVersionResponse(response: VersionResponse) {
|
override fun updateFromVersionResponse(response: VersionResponse) {
|
||||||
podState.bleVersion = SoftwareVersion(response.bleVersionMajor, response.bleVersionMinor, response.bleVersionInterim)
|
podState.bleVersion = SoftwareVersion(
|
||||||
podState.firmwareVersion = SoftwareVersion(response.firmwareVersionMajor, response.firmwareVersionMinor, response.firmwareVersionInterim)
|
response.bleVersionMajor,
|
||||||
|
response.bleVersionMinor,
|
||||||
|
response.bleVersionInterim
|
||||||
|
)
|
||||||
|
podState.firmwareVersion = SoftwareVersion(
|
||||||
|
response.firmwareVersionMajor,
|
||||||
|
response.firmwareVersionMinor,
|
||||||
|
response.firmwareVersionInterim
|
||||||
|
)
|
||||||
podState.podStatus = response.podStatus
|
podState.podStatus = response.podStatus
|
||||||
podState.lotNumber = response.lotNumber
|
podState.lotNumber = response.lotNumber
|
||||||
podState.podSequenceNumber = response.podSequenceNumber
|
podState.podSequenceNumber = response.podSequenceNumber
|
||||||
|
|
||||||
podState.lastUpdated = System.currentTimeMillis()
|
podState.lastUpdated = System.currentTimeMillis()
|
||||||
store()
|
store()
|
||||||
|
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFromSetUniqueIdResponse(response: SetUniqueIdResponse) {
|
override fun updateFromSetUniqueIdResponse(response: SetUniqueIdResponse) {
|
||||||
|
@ -177,8 +190,16 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
podState.firstPrimeBolusVolume = response.numberOfPrimePulses
|
podState.firstPrimeBolusVolume = response.numberOfPrimePulses
|
||||||
podState.secondPrimeBolusVolume = response.numberOfEngagingClutchDrivePulses
|
podState.secondPrimeBolusVolume = response.numberOfEngagingClutchDrivePulses
|
||||||
podState.podLifeInHours = response.podExpirationTimeInHours
|
podState.podLifeInHours = response.podExpirationTimeInHours
|
||||||
podState.bleVersion = SoftwareVersion(response.bleVersionMajor, response.bleVersionMinor, response.bleVersionInterim)
|
podState.bleVersion = SoftwareVersion(
|
||||||
podState.firmwareVersion = SoftwareVersion(response.firmwareVersionMajor, response.firmwareVersionMinor, response.firmwareVersionInterim)
|
response.bleVersionMajor,
|
||||||
|
response.bleVersionMinor,
|
||||||
|
response.bleVersionInterim
|
||||||
|
)
|
||||||
|
podState.firmwareVersion = SoftwareVersion(
|
||||||
|
response.firmwareVersionMajor,
|
||||||
|
response.firmwareVersionMinor,
|
||||||
|
response.firmwareVersionInterim
|
||||||
|
)
|
||||||
podState.podStatus = response.podStatus
|
podState.podStatus = response.podStatus
|
||||||
podState.lotNumber = response.lotNumber
|
podState.lotNumber = response.lotNumber
|
||||||
podState.podSequenceNumber = response.podSequenceNumber
|
podState.podSequenceNumber = response.podSequenceNumber
|
||||||
|
@ -186,10 +207,18 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
|
|
||||||
podState.lastUpdated = System.currentTimeMillis()
|
podState.lastUpdated = System.currentTimeMillis()
|
||||||
store()
|
store()
|
||||||
|
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFromAlarmStatusResponse(response: AlarmStatusResponse) {
|
override fun updateFromAlarmStatusResponse(response: AlarmStatusResponse) {
|
||||||
TODO("Not yet implemented")
|
// TODO
|
||||||
|
logger.error(
|
||||||
|
LTag.PUMP,
|
||||||
|
"Not implemented: OmnipodDashPodStateManagerImpl.updateFromAlarmStatusResponse(AlarmStatusResponse)"
|
||||||
|
)
|
||||||
|
|
||||||
|
store()
|
||||||
|
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reset() {
|
override fun reset() {
|
||||||
|
@ -210,7 +239,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
private fun load(): PodState {
|
private fun load(): PodState {
|
||||||
if (sharedPreferences.contains(R.string.key_omnipod_dash_pod_state)) {
|
if (sharedPreferences.contains(R.string.key_omnipod_dash_pod_state)) {
|
||||||
try {
|
try {
|
||||||
return Gson().fromJson(sharedPreferences.getString(R.string.key_omnipod_dash_pod_state, ""), PodState::class.java)
|
return Gson().fromJson(
|
||||||
|
sharedPreferences.getString(R.string.key_omnipod_dash_pod_state, ""),
|
||||||
|
PodState::class.java
|
||||||
|
)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
logger.error(LTag.PUMP, "Failed to deserialize Pod state", ex)
|
logger.error(LTag.PUMP, "Failed to deserialize Pod state", ex)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ object AlertUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeAlertSet(alertSet: EnumSet<AlertType>): Byte =
|
fun encodeAlertSet(alertSet: EnumSet<AlertType>): Byte =
|
||||||
alertSet.fold(0,
|
alertSet.fold(
|
||||||
|
0,
|
||||||
{ out, slot ->
|
{ out, slot ->
|
||||||
out or (slot.value.toInt() and 0xff)
|
out or (slot.value.toInt() and 0xff)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,264 @@ import kotlin.experimental.xor
|
||||||
|
|
||||||
object MessageUtil {
|
object MessageUtil {
|
||||||
|
|
||||||
private val crc16table = shortArrayOf(0, -32763, -32753, 10, -32741, 30, 20, -32751, -32717, 54, 60, -32711, 40, -32723, -32729, 34, -32669, 102, 108, -32663, 120, -32643, -32649, 114, 80, -32683, -32673, 90, -32693, 78, 68, -32703, -32573, 198, 204, -32567, 216, -32547, -32553, 210, 240, -32523, -32513, 250, -32533, 238, 228, -32543, 160, -32603, -32593, 170, -32581, 190, 180, -32591, -32621, 150, 156, -32615, 136, -32627, -32633, 130, -32381, 390, 396, -32375, 408, -32355, -32361, 402, 432, -32331, -32321, 442, -32341, 430, 420, -32351, 480, -32283, -32273, 490, -32261, 510, 500, -32271, -32301, 470, 476, -32295, 456, -32307, -32313, 450, 320, -32443, -32433, 330, -32421, 350, 340, -32431, -32397, 374, 380, -32391, 360, -32403, -32409, 354, -32477, 294, 300, -32471, 312, -32451, -32457, 306, 272, -32491, -32481, 282, -32501, 270, 260, -32511, -31997, 774, 780, -31991, 792, -31971, -31977, 786, 816, -31947, -31937, 826, -31957, 814, 804, -31967, 864, -31899, -31889, 874, -31877, 894, 884, -31887, -31917, 854, 860, -31911, 840, -31923, -31929, 834, 960, -31803, -31793, 970, -31781, 990, 980, -31791, -31757, 1014, 1020, -31751, 1000, -31763, -31769, 994, -31837, 934, 940, -31831, 952, -31811, -31817, 946, 912, -31851, -31841, 922, -31861, 910, 900, -31871, 640, -32123, -32113, 650, -32101, 670, 660, -32111, -32077, 694, 700, -32071, 680, -32083, -32089, 674, -32029, 742, 748, -32023, 760, -32003, -32009, 754, 720, -32043, -32033, 730, -32053, 718, 708, -32063, -32189, 582, 588, -32183, 600, -32163, -32169, 594, 624, -32139, -32129, 634, -32149, 622, 612, -32159, 544, -32219, -32209, 554, -32197, 574, 564, -32207, -32237, 534, 540, -32231, 520, -32243, -32249, 514)
|
private val crc16table = shortArrayOf(
|
||||||
|
0,
|
||||||
|
-32763,
|
||||||
|
-32753,
|
||||||
|
10,
|
||||||
|
-32741,
|
||||||
|
30,
|
||||||
|
20,
|
||||||
|
-32751,
|
||||||
|
-32717,
|
||||||
|
54,
|
||||||
|
60,
|
||||||
|
-32711,
|
||||||
|
40,
|
||||||
|
-32723,
|
||||||
|
-32729,
|
||||||
|
34,
|
||||||
|
-32669,
|
||||||
|
102,
|
||||||
|
108,
|
||||||
|
-32663,
|
||||||
|
120,
|
||||||
|
-32643,
|
||||||
|
-32649,
|
||||||
|
114,
|
||||||
|
80,
|
||||||
|
-32683,
|
||||||
|
-32673,
|
||||||
|
90,
|
||||||
|
-32693,
|
||||||
|
78,
|
||||||
|
68,
|
||||||
|
-32703,
|
||||||
|
-32573,
|
||||||
|
198,
|
||||||
|
204,
|
||||||
|
-32567,
|
||||||
|
216,
|
||||||
|
-32547,
|
||||||
|
-32553,
|
||||||
|
210,
|
||||||
|
240,
|
||||||
|
-32523,
|
||||||
|
-32513,
|
||||||
|
250,
|
||||||
|
-32533,
|
||||||
|
238,
|
||||||
|
228,
|
||||||
|
-32543,
|
||||||
|
160,
|
||||||
|
-32603,
|
||||||
|
-32593,
|
||||||
|
170,
|
||||||
|
-32581,
|
||||||
|
190,
|
||||||
|
180,
|
||||||
|
-32591,
|
||||||
|
-32621,
|
||||||
|
150,
|
||||||
|
156,
|
||||||
|
-32615,
|
||||||
|
136,
|
||||||
|
-32627,
|
||||||
|
-32633,
|
||||||
|
130,
|
||||||
|
-32381,
|
||||||
|
390,
|
||||||
|
396,
|
||||||
|
-32375,
|
||||||
|
408,
|
||||||
|
-32355,
|
||||||
|
-32361,
|
||||||
|
402,
|
||||||
|
432,
|
||||||
|
-32331,
|
||||||
|
-32321,
|
||||||
|
442,
|
||||||
|
-32341,
|
||||||
|
430,
|
||||||
|
420,
|
||||||
|
-32351,
|
||||||
|
480,
|
||||||
|
-32283,
|
||||||
|
-32273,
|
||||||
|
490,
|
||||||
|
-32261,
|
||||||
|
510,
|
||||||
|
500,
|
||||||
|
-32271,
|
||||||
|
-32301,
|
||||||
|
470,
|
||||||
|
476,
|
||||||
|
-32295,
|
||||||
|
456,
|
||||||
|
-32307,
|
||||||
|
-32313,
|
||||||
|
450,
|
||||||
|
320,
|
||||||
|
-32443,
|
||||||
|
-32433,
|
||||||
|
330,
|
||||||
|
-32421,
|
||||||
|
350,
|
||||||
|
340,
|
||||||
|
-32431,
|
||||||
|
-32397,
|
||||||
|
374,
|
||||||
|
380,
|
||||||
|
-32391,
|
||||||
|
360,
|
||||||
|
-32403,
|
||||||
|
-32409,
|
||||||
|
354,
|
||||||
|
-32477,
|
||||||
|
294,
|
||||||
|
300,
|
||||||
|
-32471,
|
||||||
|
312,
|
||||||
|
-32451,
|
||||||
|
-32457,
|
||||||
|
306,
|
||||||
|
272,
|
||||||
|
-32491,
|
||||||
|
-32481,
|
||||||
|
282,
|
||||||
|
-32501,
|
||||||
|
270,
|
||||||
|
260,
|
||||||
|
-32511,
|
||||||
|
-31997,
|
||||||
|
774,
|
||||||
|
780,
|
||||||
|
-31991,
|
||||||
|
792,
|
||||||
|
-31971,
|
||||||
|
-31977,
|
||||||
|
786,
|
||||||
|
816,
|
||||||
|
-31947,
|
||||||
|
-31937,
|
||||||
|
826,
|
||||||
|
-31957,
|
||||||
|
814,
|
||||||
|
804,
|
||||||
|
-31967,
|
||||||
|
864,
|
||||||
|
-31899,
|
||||||
|
-31889,
|
||||||
|
874,
|
||||||
|
-31877,
|
||||||
|
894,
|
||||||
|
884,
|
||||||
|
-31887,
|
||||||
|
-31917,
|
||||||
|
854,
|
||||||
|
860,
|
||||||
|
-31911,
|
||||||
|
840,
|
||||||
|
-31923,
|
||||||
|
-31929,
|
||||||
|
834,
|
||||||
|
960,
|
||||||
|
-31803,
|
||||||
|
-31793,
|
||||||
|
970,
|
||||||
|
-31781,
|
||||||
|
990,
|
||||||
|
980,
|
||||||
|
-31791,
|
||||||
|
-31757,
|
||||||
|
1014,
|
||||||
|
1020,
|
||||||
|
-31751,
|
||||||
|
1000,
|
||||||
|
-31763,
|
||||||
|
-31769,
|
||||||
|
994,
|
||||||
|
-31837,
|
||||||
|
934,
|
||||||
|
940,
|
||||||
|
-31831,
|
||||||
|
952,
|
||||||
|
-31811,
|
||||||
|
-31817,
|
||||||
|
946,
|
||||||
|
912,
|
||||||
|
-31851,
|
||||||
|
-31841,
|
||||||
|
922,
|
||||||
|
-31861,
|
||||||
|
910,
|
||||||
|
900,
|
||||||
|
-31871,
|
||||||
|
640,
|
||||||
|
-32123,
|
||||||
|
-32113,
|
||||||
|
650,
|
||||||
|
-32101,
|
||||||
|
670,
|
||||||
|
660,
|
||||||
|
-32111,
|
||||||
|
-32077,
|
||||||
|
694,
|
||||||
|
700,
|
||||||
|
-32071,
|
||||||
|
680,
|
||||||
|
-32083,
|
||||||
|
-32089,
|
||||||
|
674,
|
||||||
|
-32029,
|
||||||
|
742,
|
||||||
|
748,
|
||||||
|
-32023,
|
||||||
|
760,
|
||||||
|
-32003,
|
||||||
|
-32009,
|
||||||
|
754,
|
||||||
|
720,
|
||||||
|
-32043,
|
||||||
|
-32033,
|
||||||
|
730,
|
||||||
|
-32053,
|
||||||
|
718,
|
||||||
|
708,
|
||||||
|
-32063,
|
||||||
|
-32189,
|
||||||
|
582,
|
||||||
|
588,
|
||||||
|
-32183,
|
||||||
|
600,
|
||||||
|
-32163,
|
||||||
|
-32169,
|
||||||
|
594,
|
||||||
|
624,
|
||||||
|
-32139,
|
||||||
|
-32129,
|
||||||
|
634,
|
||||||
|
-32149,
|
||||||
|
622,
|
||||||
|
612,
|
||||||
|
-32159,
|
||||||
|
544,
|
||||||
|
-32219,
|
||||||
|
-32209,
|
||||||
|
554,
|
||||||
|
-32197,
|
||||||
|
574,
|
||||||
|
564,
|
||||||
|
-32207,
|
||||||
|
-32237,
|
||||||
|
534,
|
||||||
|
540,
|
||||||
|
-32231,
|
||||||
|
520,
|
||||||
|
-32243,
|
||||||
|
-32249,
|
||||||
|
514
|
||||||
|
)
|
||||||
|
|
||||||
fun createCrc(sArr: ShortArray): Int {
|
fun createCrc(sArr: ShortArray): Int {
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
|
@ -22,10 +22,19 @@ class DashHistory @Inject constructor(
|
||||||
private val historyMapper: HistoryMapper
|
private val historyMapper: HistoryMapper
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun markSuccess(id: String, date: Long): Completable = dao.markResolved(id, ResolvedResult.SUCCESS, currentTimeMillis())
|
fun markSuccess(id: String, date: Long): Completable = dao.markResolved(
|
||||||
|
id,
|
||||||
|
ResolvedResult.SUCCESS,
|
||||||
|
currentTimeMillis()
|
||||||
|
)
|
||||||
|
|
||||||
fun markFailure(id: String, date: Long): Completable = dao.markResolved(id, ResolvedResult.FAILURE, currentTimeMillis())
|
fun markFailure(id: String, date: Long): Completable = dao.markResolved(
|
||||||
|
id,
|
||||||
|
ResolvedResult.FAILURE,
|
||||||
|
currentTimeMillis()
|
||||||
|
)
|
||||||
|
|
||||||
|
@Suppress("ReturnCount")
|
||||||
fun createRecord(
|
fun createRecord(
|
||||||
commandType: OmnipodCommandType,
|
commandType: OmnipodCommandType,
|
||||||
date: Long,
|
date: Long,
|
||||||
|
@ -44,16 +53,18 @@ class DashHistory @Inject constructor(
|
||||||
return Single.error(IllegalArgumentException("tempBasalRecord missing on SET_TEMPORARY_BASAL"))
|
return Single.error(IllegalArgumentException("tempBasalRecord missing on SET_TEMPORARY_BASAL"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return dao.save(HistoryRecordEntity(
|
return dao.save(
|
||||||
id = id,
|
HistoryRecordEntity(
|
||||||
date = date,
|
id = id,
|
||||||
createdAt = currentTimeMillis(),
|
date = date,
|
||||||
commandType = commandType,
|
createdAt = currentTimeMillis(),
|
||||||
tempBasalRecord = tempBasalRecord,
|
commandType = commandType,
|
||||||
bolusRecord = bolusRecord,
|
tempBasalRecord = tempBasalRecord,
|
||||||
initialResult = initialResult,
|
bolusRecord = bolusRecord,
|
||||||
resolvedResult = resolveResult,
|
initialResult = initialResult,
|
||||||
resolvedAt = resolvedAt)
|
resolvedResult = resolveResult,
|
||||||
|
resolvedAt = resolvedAt
|
||||||
|
)
|
||||||
).toSingle { id }
|
).toSingle { id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,5 +72,4 @@ class DashHistory @Inject constructor(
|
||||||
dao.all().map { list -> list.map(historyMapper::entityToDomain) }
|
dao.all().map { list -> list.map(historyMapper::entityToDomain) }
|
||||||
|
|
||||||
fun getRecordsAfter(time: Long): Single<List<HistoryRecordEntity>> = dao.allSince(time)
|
fun getRecordsAfter(time: Long): Single<List<HistoryRecordEntity>> = dao.allSince(time)
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,9 +2,9 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data
|
||||||
|
|
||||||
sealed class Record
|
sealed class Record
|
||||||
|
|
||||||
data class BolusRecord(val amout: Double, val bolusType: BolusType): Record()
|
data class BolusRecord(val amout: Double, val bolusType: BolusType) : Record()
|
||||||
|
|
||||||
data class TempBasalRecord(val duration: Long, val rate: Double): Record()
|
data class TempBasalRecord(val duration: Long, val rate: Double) : Record()
|
||||||
|
|
||||||
enum class BolusType {
|
enum class BolusType {
|
||||||
DEFAULT, SMB
|
DEFAULT, SMB
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.room.Database
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
import androidx.room.migration.Migration
|
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [HistoryRecordEntity::class],
|
entities = [HistoryRecordEntity::class],
|
||||||
|
@ -15,16 +14,19 @@ import androidx.room.migration.Migration
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
abstract class DashHistoryDatabase : RoomDatabase() {
|
abstract class DashHistoryDatabase : RoomDatabase() {
|
||||||
|
|
||||||
abstract fun historyRecordDao() : HistoryRecordDao
|
abstract fun historyRecordDao(): HistoryRecordDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val VERSION = 1
|
const val VERSION = 1
|
||||||
|
|
||||||
fun build(context: Context) =
|
fun build(context: Context) =
|
||||||
Room.databaseBuilder(context.applicationContext, DashHistoryDatabase::class.java, "omnipod_dash_history_database.db")
|
Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
DashHistoryDatabase::class.java,
|
||||||
|
"omnipod_dash_history_database.db"
|
||||||
|
)
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration()
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -32,5 +32,4 @@ abstract class HistoryRecordDao {
|
||||||
|
|
||||||
@Query("UPDATE historyrecords SET resolvedResult = :resolvedResult, resolvedAt = :resolvedAt WHERE id = :id ")
|
@Query("UPDATE historyrecords SET resolvedResult = :resolvedResult, resolvedAt = :resolvedAt WHERE id = :id ")
|
||||||
abstract fun markResolved(id: String, resolvedResult: ResolvedResult, resolvedAt: Long): Completable
|
abstract fun markResolved(id: String, resolvedResult: ResolvedResult, resolvedAt: Long): Completable
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,5 +19,5 @@ data class HistoryRecordEntity(
|
||||||
@Embedded(prefix = "tempBasalRecord_") val tempBasalRecord: TempBasalRecord?,
|
@Embedded(prefix = "tempBasalRecord_") val tempBasalRecord: TempBasalRecord?,
|
||||||
@Embedded(prefix = "bolusRecord_") val bolusRecord: BolusRecord?,
|
@Embedded(prefix = "bolusRecord_") val bolusRecord: BolusRecord?,
|
||||||
val resolvedResult: ResolvedResult?,
|
val resolvedResult: ResolvedResult?,
|
||||||
val resolvedAt: Long?)
|
val resolvedAt: Long?
|
||||||
|
)
|
||||||
|
|
|
@ -21,7 +21,8 @@ class HistoryMapper {
|
||||||
)
|
)
|
||||||
|
|
||||||
fun entityToDomain(entity: HistoryRecordEntity): HistoryRecord =
|
fun entityToDomain(entity: HistoryRecordEntity): HistoryRecord =
|
||||||
HistoryRecord(id = entity.id,
|
HistoryRecord(
|
||||||
|
id = entity.id,
|
||||||
createdAt = entity.createdAt,
|
createdAt = entity.createdAt,
|
||||||
date = entity.date,
|
date = entity.date,
|
||||||
initialResult = entity.initialResult,
|
initialResult = entity.initialResult,
|
||||||
|
@ -30,5 +31,4 @@ class HistoryMapper {
|
||||||
resolvedResult = entity.resolvedResult,
|
resolvedResult = entity.resolvedResult,
|
||||||
resolvedAt = entity.resolvedAt
|
resolvedAt = entity.resolvedAt
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
|
@ -66,10 +66,13 @@ class DashPodManagementActivity : NoSplashAppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonDiscardPod.setOnClickListener {
|
binding.buttonDiscardPod.setOnClickListener {
|
||||||
OKDialog.showConfirmation(this,
|
OKDialog.showConfirmation(
|
||||||
resourceHelper.gs(R.string.omnipod_common_pod_management_discard_pod_confirmation), Thread {
|
this,
|
||||||
// TODO discard Pod
|
resourceHelper.gs(R.string.omnipod_common_pod_management_discard_pod_confirmation),
|
||||||
})
|
Thread {
|
||||||
|
// TODO discard Pod
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonPlayTestBeep.setOnClickListener {
|
binding.buttonPlayTestBeep.setOnClickListener {
|
||||||
|
@ -77,13 +80,24 @@ class DashPodManagementActivity : NoSplashAppCompatActivity() {
|
||||||
binding.buttonPlayTestBeep.isEnabled = false
|
binding.buttonPlayTestBeep.isEnabled = false
|
||||||
binding.buttonPlayTestBeep.setText(R.string.omnipod_common_pod_management_button_playing_test_beep)
|
binding.buttonPlayTestBeep.setText(R.string.omnipod_common_pod_management_button_playing_test_beep)
|
||||||
|
|
||||||
commandQueue.customCommand(CommandPlayTestBeep(), object : Callback() {
|
commandQueue.customCommand(
|
||||||
override fun run() {
|
CommandPlayTestBeep(),
|
||||||
if (!result.success) {
|
object : Callback() {
|
||||||
displayErrorDialog(resourceHelper.gs(R.string.omnipod_common_warning), resourceHelper.gs(R.string.omnipod_common_two_strings_concatenated_by_colon, resourceHelper.gs(R.string.omnipod_common_error_failed_to_play_test_beep), result.comment), false)
|
override fun run() {
|
||||||
|
if (!result.success) {
|
||||||
|
displayErrorDialog(
|
||||||
|
resourceHelper.gs(R.string.omnipod_common_warning),
|
||||||
|
resourceHelper.gs(
|
||||||
|
R.string.omnipod_common_two_strings_concatenated_by_colon,
|
||||||
|
resourceHelper.gs(R.string.omnipod_common_error_failed_to_play_test_beep),
|
||||||
|
result.comment
|
||||||
|
),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,38 +107,59 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
|
|
||||||
buttonBinding.buttonResumeDelivery.setOnClickListener {
|
buttonBinding.buttonResumeDelivery.setOnClickListener {
|
||||||
disablePodActionButtons()
|
disablePodActionButtons()
|
||||||
commandQueue.customCommand(CommandResumeDelivery(),
|
commandQueue.customCommand(
|
||||||
DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_common_error_failed_to_resume_delivery), true).messageOnSuccess(resourceHelper.gs(R.string.omnipod_common_confirmation_delivery_resumed)))
|
CommandResumeDelivery(),
|
||||||
|
DisplayResultDialogCallback(
|
||||||
|
resourceHelper.gs(R.string.omnipod_common_error_failed_to_resume_delivery),
|
||||||
|
true
|
||||||
|
).messageOnSuccess(resourceHelper.gs(R.string.omnipod_common_confirmation_delivery_resumed))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonBinding.buttonRefreshStatus.setOnClickListener {
|
buttonBinding.buttonRefreshStatus.setOnClickListener {
|
||||||
disablePodActionButtons()
|
disablePodActionButtons()
|
||||||
commandQueue.readStatus("REQUESTED BY USER",
|
commandQueue.readStatus(
|
||||||
DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_common_error_failed_to_refresh_status), false))
|
"REQUESTED BY USER",
|
||||||
|
DisplayResultDialogCallback(
|
||||||
|
resourceHelper.gs(R.string.omnipod_common_error_failed_to_refresh_status),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonBinding.buttonSilenceAlerts.setOnClickListener {
|
buttonBinding.buttonSilenceAlerts.setOnClickListener {
|
||||||
disablePodActionButtons()
|
disablePodActionButtons()
|
||||||
commandQueue.customCommand(CommandAcknowledgeAlerts(),
|
commandQueue.customCommand(
|
||||||
DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_common_error_failed_to_silence_alerts), false)
|
CommandAcknowledgeAlerts(),
|
||||||
|
DisplayResultDialogCallback(
|
||||||
|
resourceHelper.gs(R.string.omnipod_common_error_failed_to_silence_alerts),
|
||||||
|
false
|
||||||
|
)
|
||||||
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_common_confirmation_silenced_alerts))
|
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_common_confirmation_silenced_alerts))
|
||||||
.actionOnSuccess { rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_ALERTS)) })
|
.actionOnSuccess { rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_ALERTS)) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonBinding.buttonSuspendDelivery.setOnClickListener {
|
buttonBinding.buttonSuspendDelivery.setOnClickListener {
|
||||||
disablePodActionButtons()
|
disablePodActionButtons()
|
||||||
commandQueue.customCommand(CommandSuspendDelivery(),
|
commandQueue.customCommand(
|
||||||
DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_common_error_failed_to_suspend_delivery), true)
|
CommandSuspendDelivery(),
|
||||||
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_common_confirmation_suspended_delivery)))
|
DisplayResultDialogCallback(
|
||||||
|
resourceHelper.gs(R.string.omnipod_common_error_failed_to_suspend_delivery),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_common_confirmation_suspended_delivery))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonBinding.buttonSetTime.setOnClickListener {
|
buttonBinding.buttonSetTime.setOnClickListener {
|
||||||
disablePodActionButtons()
|
disablePodActionButtons()
|
||||||
commandQueue.customCommand(CommandHandleTimeChange(true),
|
commandQueue.customCommand(
|
||||||
|
CommandHandleTimeChange(true),
|
||||||
DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_common_error_failed_to_set_time), true)
|
DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_common_error_failed_to_set_time), true)
|
||||||
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_common_confirmation_time_on_pod_updated)))
|
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_common_confirmation_time_on_pod_updated))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -147,23 +168,32 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
disposables += rxBus
|
disposables += rxBus
|
||||||
.toObservable(EventOmnipodDashPumpValuesChanged::class.java)
|
.toObservable(EventOmnipodDashPumpValuesChanged::class.java)
|
||||||
.observeOn(aapsSchedulers.main)
|
.observeOn(aapsSchedulers.main)
|
||||||
.subscribe({
|
.subscribe(
|
||||||
updateOmnipodStatus()
|
{
|
||||||
updatePodActionButtons()
|
updateOmnipodStatus()
|
||||||
}, fabricPrivacy::logException)
|
updatePodActionButtons()
|
||||||
|
},
|
||||||
|
fabricPrivacy::logException
|
||||||
|
)
|
||||||
disposables += rxBus
|
disposables += rxBus
|
||||||
.toObservable(EventQueueChanged::class.java)
|
.toObservable(EventQueueChanged::class.java)
|
||||||
.observeOn(aapsSchedulers.main)
|
.observeOn(aapsSchedulers.main)
|
||||||
.subscribe({
|
.subscribe(
|
||||||
updateQueueStatus()
|
{
|
||||||
updatePodActionButtons()
|
updateQueueStatus()
|
||||||
}, fabricPrivacy::logException)
|
updatePodActionButtons()
|
||||||
|
},
|
||||||
|
fabricPrivacy::logException
|
||||||
|
)
|
||||||
disposables += rxBus
|
disposables += rxBus
|
||||||
.toObservable(EventPreferenceChange::class.java)
|
.toObservable(EventPreferenceChange::class.java)
|
||||||
.observeOn(aapsSchedulers.main)
|
.observeOn(aapsSchedulers.main)
|
||||||
.subscribe({
|
.subscribe(
|
||||||
updatePodActionButtons()
|
{
|
||||||
}, fabricPrivacy::logException)
|
updatePodActionButtons()
|
||||||
|
},
|
||||||
|
fabricPrivacy::logException
|
||||||
|
)
|
||||||
updateUi()
|
updateUi()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +241,11 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
podInfoBinding.uniqueId.text = podStateManager.uniqueId.toString()
|
podInfoBinding.uniqueId.text = podStateManager.uniqueId.toString()
|
||||||
podInfoBinding.podLot.text = podStateManager.lotNumber.toString()
|
podInfoBinding.podLot.text = podStateManager.lotNumber.toString()
|
||||||
podInfoBinding.podSequenceNumber.text = podStateManager.podSequenceNumber.toString()
|
podInfoBinding.podSequenceNumber.text = podStateManager.podSequenceNumber.toString()
|
||||||
podInfoBinding.firmwareVersion.text = resourceHelper.gs(R.string.omnipod_dash_overview_firmware_version_value, podStateManager.firmwareVersion.toString(), podStateManager.bluetoothVersion.toString())
|
podInfoBinding.firmwareVersion.text = resourceHelper.gs(
|
||||||
|
R.string.omnipod_dash_overview_firmware_version_value,
|
||||||
|
podStateManager.firmwareVersion.toString(),
|
||||||
|
podStateManager.bluetoothVersion.toString()
|
||||||
|
)
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
/*
|
/*
|
||||||
|
@ -249,34 +283,45 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
|
|
||||||
// base basal rate
|
// base basal rate
|
||||||
podInfoBinding.baseBasalRate.text = if (podStateManager.basalProgram != null) {
|
podInfoBinding.baseBasalRate.text = if (podStateManager.basalProgram != null) {
|
||||||
resourceHelper.gs(R.string.pump_basebasalrate, omnipodDashPumpPlugin.model().determineCorrectBasalSize(podStateManager.basalProgram!!.rateAt(Date())))
|
resourceHelper.gs(
|
||||||
|
R.string.pump_basebasalrate,
|
||||||
|
omnipodDashPumpPlugin.model()
|
||||||
|
.determineCorrectBasalSize(podStateManager.basalProgram!!.rateAt(Date()))
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
PLACEHOLDER
|
PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
// total delivered
|
// total delivered
|
||||||
podInfoBinding.totalDelivered.text = if (podStateManager.isActivationCompleted && podStateManager.pulsesDelivered != null) {
|
podInfoBinding.totalDelivered.text =
|
||||||
resourceHelper.gs(R.string.omnipod_common_overview_total_delivered_value, podStateManager.pulseRate)
|
if (podStateManager.isActivationCompleted && podStateManager.pulsesDelivered != null) {
|
||||||
} else {
|
resourceHelper.gs(R.string.omnipod_common_overview_total_delivered_value, podStateManager.pulseRate)
|
||||||
PLACEHOLDER
|
} else {
|
||||||
}
|
PLACEHOLDER
|
||||||
|
}
|
||||||
|
|
||||||
// reservoir
|
// reservoir
|
||||||
if (podStateManager.pulsesRemaining == null) {
|
if (podStateManager.pulsesRemaining == null) {
|
||||||
podInfoBinding.reservoir.text = resourceHelper.gs(R.string.omnipod_common_overview_reservoir_value_over50)
|
podInfoBinding.reservoir.text =
|
||||||
|
resourceHelper.gs(R.string.omnipod_common_overview_reservoir_value_over50)
|
||||||
podInfoBinding.reservoir.setTextColor(Color.WHITE)
|
podInfoBinding.reservoir.setTextColor(Color.WHITE)
|
||||||
} else {
|
} else {
|
||||||
// TODO
|
// TODO
|
||||||
//val lowReservoirThreshold = (omnipodAlertUtil.lowReservoirAlertUnits
|
// val lowReservoirThreshold = (omnipodAlertUtil.lowReservoirAlertUnits
|
||||||
// ?: OmnipodConstants.DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD).toDouble()
|
// ?: OmnipodConstants.DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD).toDouble()
|
||||||
val lowReservoirThreshold: Short = 20
|
val lowReservoirThreshold: Short = 20
|
||||||
|
|
||||||
podInfoBinding.reservoir.text = resourceHelper.gs(R.string.omnipod_common_overview_reservoir_value, podStateManager.pulsesRemaining)
|
podInfoBinding.reservoir.text = resourceHelper.gs(
|
||||||
podInfoBinding.reservoir.setTextColor(if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) {
|
R.string.omnipod_common_overview_reservoir_value,
|
||||||
Color.RED
|
podStateManager.pulsesRemaining
|
||||||
} else {
|
)
|
||||||
Color.WHITE
|
podInfoBinding.reservoir.setTextColor(
|
||||||
})
|
if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) {
|
||||||
|
Color.RED
|
||||||
|
} else {
|
||||||
|
Color.WHITE
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
podInfoBinding.podActiveAlerts.text = if (podStateManager.activeAlerts!!.size > 0) {
|
podInfoBinding.podActiveAlerts.text = if (podStateManager.activeAlerts!!.size > 0) {
|
||||||
|
@ -345,11 +390,12 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val podStatusColor = if (!podStateManager.isActivationCompleted ||/* TODO podStateManager.isPodDead || */ podStateManager.isSuspended) {
|
val podStatusColor =
|
||||||
Color.RED
|
if (!podStateManager.isActivationCompleted || /* TODO podStateManager.isPodDead || */ podStateManager.isSuspended) {
|
||||||
} else {
|
Color.RED
|
||||||
Color.WHITE
|
} else {
|
||||||
}
|
Color.WHITE
|
||||||
|
}
|
||||||
podInfoBinding.podStatus.setTextColor(podStatusColor)
|
podInfoBinding.podStatus.setTextColor(podStatusColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +431,13 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
|
|
||||||
val minutesRunning = 0 // TODO
|
val minutesRunning = 0 // TODO
|
||||||
|
|
||||||
podInfoBinding.tempBasal.text = resourceHelper.gs(R.string.omnipod_common_overview_temp_basal_value, rate, dateUtil.timeString(startTime), minutesRunning, duration)
|
podInfoBinding.tempBasal.text = resourceHelper.gs(
|
||||||
|
R.string.omnipod_common_overview_temp_basal_value,
|
||||||
|
rate,
|
||||||
|
dateUtil.timeString(startTime),
|
||||||
|
minutesRunning,
|
||||||
|
duration
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
podInfoBinding.tempBasal.text = PLACEHOLDER
|
podInfoBinding.tempBasal.text = PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
@ -417,12 +469,17 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateRefreshStatusButton() {
|
private fun updateRefreshStatusButton() {
|
||||||
buttonBinding.buttonRefreshStatus.isEnabled = podStateManager.isUniqueIdSet && podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED)
|
buttonBinding.buttonRefreshStatus.isEnabled =
|
||||||
&& isQueueEmpty()
|
podStateManager.isUniqueIdSet && podStateManager.activationProgress.isAtLeast(
|
||||||
|
ActivationProgress.PHASE_1_COMPLETED
|
||||||
|
) &&
|
||||||
|
isQueueEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateResumeDeliveryButton() {
|
private fun updateResumeDeliveryButton() {
|
||||||
if (podStateManager.isPodRunning && (podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandResumeDelivery::class.java))) {
|
if (podStateManager.isPodRunning && (podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(
|
||||||
|
CommandResumeDelivery::class.java
|
||||||
|
))) {
|
||||||
buttonBinding.buttonResumeDelivery.visibility = View.VISIBLE
|
buttonBinding.buttonResumeDelivery.visibility = View.VISIBLE
|
||||||
buttonBinding.buttonResumeDelivery.isEnabled = isQueueEmpty()
|
buttonBinding.buttonResumeDelivery.isEnabled = isQueueEmpty()
|
||||||
} else {
|
} else {
|
||||||
|
@ -431,7 +488,9 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSilenceAlertsButton() {
|
private fun updateSilenceAlertsButton() {
|
||||||
if (isAutomaticallySilenceAlertsEnabled() && podStateManager.isPodRunning && (podStateManager.activeAlerts!!.size > 0 || commandQueue.isCustomCommandInQueue(CommandAcknowledgeAlerts::class.java))) {
|
if (isAutomaticallySilenceAlertsEnabled() && podStateManager.isPodRunning && (podStateManager.activeAlerts!!.size > 0 || commandQueue.isCustomCommandInQueue(
|
||||||
|
CommandAcknowledgeAlerts::class.java
|
||||||
|
))) {
|
||||||
buttonBinding.buttonSilenceAlerts.visibility = View.VISIBLE
|
buttonBinding.buttonSilenceAlerts.visibility = View.VISIBLE
|
||||||
buttonBinding.buttonSilenceAlerts.isEnabled = isQueueEmpty()
|
buttonBinding.buttonSilenceAlerts.isEnabled = isQueueEmpty()
|
||||||
} else {
|
} else {
|
||||||
|
@ -441,9 +500,12 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
|
|
||||||
private fun updateSuspendDeliveryButton() {
|
private fun updateSuspendDeliveryButton() {
|
||||||
// If the Pod is currently suspended, we show the Resume delivery button instead.
|
// If the Pod is currently suspended, we show the Resume delivery button instead.
|
||||||
if (isSuspendDeliveryButtonEnabled() && podStateManager.isPodRunning && (!podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandSuspendDelivery::class.java))) {
|
if (isSuspendDeliveryButtonEnabled() && podStateManager.isPodRunning && (!podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(
|
||||||
|
CommandSuspendDelivery::class.java
|
||||||
|
))) {
|
||||||
buttonBinding.buttonSuspendDelivery.visibility = View.VISIBLE
|
buttonBinding.buttonSuspendDelivery.visibility = View.VISIBLE
|
||||||
buttonBinding.buttonSuspendDelivery.isEnabled = podStateManager.isPodRunning && !podStateManager.isSuspended && isQueueEmpty()
|
buttonBinding.buttonSuspendDelivery.isEnabled =
|
||||||
|
podStateManager.isPodRunning && !podStateManager.isSuspended && isQueueEmpty()
|
||||||
} else {
|
} else {
|
||||||
buttonBinding.buttonSuspendDelivery.visibility = View.GONE
|
buttonBinding.buttonSuspendDelivery.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
@ -546,10 +608,18 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
|
|
||||||
// FIXME ideally we should just have access to LocalAlertUtils here
|
// FIXME ideally we should just have access to LocalAlertUtils here
|
||||||
private fun getPumpUnreachableTimeout(): Duration {
|
private fun getPumpUnreachableTimeout(): Duration {
|
||||||
return Duration.standardMinutes(sp.getInt(R.string.key_pump_unreachable_threshold_minutes, Constants.DEFAULT_PUMP_UNREACHABLE_THRESHOLD_MINUTES).toLong())
|
return Duration.standardMinutes(
|
||||||
|
sp.getInt(
|
||||||
|
R.string.key_pump_unreachable_threshold_minutes,
|
||||||
|
Constants.DEFAULT_PUMP_UNREACHABLE_THRESHOLD_MINUTES
|
||||||
|
).toLong()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class DisplayResultDialogCallback(private val errorMessagePrefix: String, private val withSoundOnError: Boolean) : Callback() {
|
inner class DisplayResultDialogCallback(
|
||||||
|
private val errorMessagePrefix: String,
|
||||||
|
private val withSoundOnError: Boolean
|
||||||
|
) : Callback() {
|
||||||
|
|
||||||
private var messageOnSuccess: String? = null
|
private var messageOnSuccess: String? = null
|
||||||
private var actionOnSuccess: Runnable? = null
|
private var actionOnSuccess: Runnable? = null
|
||||||
|
@ -562,7 +632,15 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
actionOnSuccess?.run()
|
actionOnSuccess?.run()
|
||||||
} else {
|
} else {
|
||||||
displayErrorDialog(resourceHelper.gs(R.string.omnipod_common_warning), resourceHelper.gs(R.string.omnipod_common_two_strings_concatenated_by_colon, errorMessagePrefix, result.comment), withSoundOnError)
|
displayErrorDialog(
|
||||||
|
resourceHelper.gs(R.string.omnipod_common_warning),
|
||||||
|
resourceHelper.gs(
|
||||||
|
R.string.omnipod_common_two_strings_concatenated_by_colon,
|
||||||
|
errorMessagePrefix,
|
||||||
|
result.comment
|
||||||
|
),
|
||||||
|
withSoundOnError
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,5 +654,4 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -8,6 +8,7 @@ import info.nightscout.androidaps.logging.LTag
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InitializePodViewModel
|
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InitializePodViewModel
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertTrigger
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -26,8 +27,14 @@ class DashInitializePodViewModel @Inject constructor(
|
||||||
|
|
||||||
override fun doExecuteAction(): Single<PumpEnactResult> =
|
override fun doExecuteAction(): Single<PumpEnactResult> =
|
||||||
Single.create { source ->
|
Single.create { source ->
|
||||||
val disposable = omnipodManager.activatePodPart1().subscribeBy(
|
// TODO use configured value for low reservoir trigger
|
||||||
onNext = { podEvent -> logger.debug(LTag.PUMP, "Received PodEvent in Pod activation part 1: $podEvent") },
|
val disposable = omnipodManager.activatePodPart1(AlertTrigger.ReservoirVolumeTrigger(200)).subscribeBy(
|
||||||
|
onNext = { podEvent ->
|
||||||
|
logger.debug(
|
||||||
|
LTag.PUMP,
|
||||||
|
"Received PodEvent in Pod activation part 1: $podEvent"
|
||||||
|
)
|
||||||
|
},
|
||||||
onError = { throwable ->
|
onError = { throwable ->
|
||||||
logger.error(LTag.PUMP, "Error in Pod activation part 1", throwable)
|
logger.error(LTag.PUMP, "Error in Pod activation part 1", throwable)
|
||||||
source.onSuccess(PumpEnactResult(injector).success(false).comment(throwable.message))
|
source.onSuccess(PumpEnactResult(injector).success(false).comment(throwable.message))
|
||||||
|
|
|
@ -5,12 +5,17 @@ import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.data.PumpEnactResult
|
import info.nightscout.androidaps.data.PumpEnactResult
|
||||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InsertCannulaViewModel
|
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InsertCannulaViewModel
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DashInsertCannulaViewModel @Inject constructor(
|
class DashInsertCannulaViewModel @Inject constructor(
|
||||||
|
private val omnipodManager: OmnipodDashManager,
|
||||||
private val profileFunction: ProfileFunction,
|
private val profileFunction: ProfileFunction,
|
||||||
injector: HasAndroidInjector,
|
injector: HasAndroidInjector,
|
||||||
logger: AAPSLogger
|
logger: AAPSLogger
|
||||||
|
@ -22,7 +27,29 @@ class DashInsertCannulaViewModel @Inject constructor(
|
||||||
|
|
||||||
override fun isPodDeactivatable(): Boolean = true // TODO
|
override fun isPodDeactivatable(): Boolean = true // TODO
|
||||||
|
|
||||||
override fun doExecuteAction(): Single<PumpEnactResult> = Single.just(PumpEnactResult(injector).success(false).comment("TODO")) // TODO
|
override fun doExecuteAction(): Single<PumpEnactResult> = Single.create { source ->
|
||||||
|
val profile = profileFunction.getProfile()
|
||||||
|
if (profile == null) {
|
||||||
|
source.onError(IllegalStateException("No profile set"))
|
||||||
|
} else {
|
||||||
|
val disposable = omnipodManager.activatePodPart2(mapProfileToBasalProgram(profile)).subscribeBy(
|
||||||
|
onNext = { podEvent ->
|
||||||
|
logger.debug(
|
||||||
|
LTag.PUMP,
|
||||||
|
"Received PodEvent in Pod activation part 2: $podEvent"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onError = { throwable ->
|
||||||
|
logger.error(LTag.PUMP, "Error in Pod activation part 2", throwable)
|
||||||
|
source.onSuccess(PumpEnactResult(injector).success(false).comment(throwable.message))
|
||||||
|
},
|
||||||
|
onComplete = {
|
||||||
|
logger.debug("Pod activation part 2 completed")
|
||||||
|
source.onSuccess(PumpEnactResult(injector).success(true))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@StringRes
|
@StringRes
|
||||||
override fun getTitleId(): Int = R.string.omnipod_common_pod_activation_wizard_insert_cannula_title
|
override fun getTitleId(): Int = R.string.omnipod_common_pod_activation_wizard_insert_cannula_title
|
||||||
|
|
|
@ -14,7 +14,9 @@ class DashDeactivatePodViewModel @Inject constructor(
|
||||||
logger: AAPSLogger
|
logger: AAPSLogger
|
||||||
) : DeactivatePodViewModel(injector, logger) {
|
) : DeactivatePodViewModel(injector, logger) {
|
||||||
|
|
||||||
override fun doExecuteAction(): Single<PumpEnactResult> = Single.just(PumpEnactResult(injector).success(false).comment("TODO")) // TODO
|
override fun doExecuteAction(): Single<PumpEnactResult> = Single.just(
|
||||||
|
PumpEnactResult(injector).success(false).comment("TODO")
|
||||||
|
) // TODO
|
||||||
|
|
||||||
override fun discardPod() {
|
override fun discardPod() {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
@ -12,5 +12,4 @@ class DashPodDeactivatedViewModel @Inject constructor() : PodDeactivatedViewMode
|
||||||
|
|
||||||
@StringRes
|
@StringRes
|
||||||
override fun getTextId() = R.string.omnipod_common_pod_deactivation_wizard_pod_deactivated_text
|
override fun getTextId() = R.string.omnipod_common_pod_deactivation_wizard_pod_deactivated_text
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
|
||||||
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertConfiguration
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertConfiguration
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertTrigger
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertTrigger
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepRepetitionType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepRepetitionType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
|
||||||
import org.apache.commons.codec.DecoderException
|
import org.apache.commons.codec.DecoderException
|
||||||
|
|
|
@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlarmType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlarmType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.NakErrorType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.NakErrorType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType
|
|
||||||
import org.apache.commons.codec.DecoderException
|
import org.apache.commons.codec.DecoderException
|
||||||
import org.apache.commons.codec.binary.Hex
|
import org.apache.commons.codec.binary.Hex
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
|
|
|
@ -26,11 +26,13 @@ class FunctionsTest {
|
||||||
val value3 = Mockito.mock(ProfileValue::class.java)
|
val value3 = Mockito.mock(ProfileValue::class.java)
|
||||||
value3.timeAsSeconds = 50400
|
value3.timeAsSeconds = 50400
|
||||||
value3.value = 3.05
|
value3.value = 3.05
|
||||||
PowerMockito.`when`(profile.basalValues).thenReturn(arrayOf(
|
PowerMockito.`when`(profile.basalValues).thenReturn(
|
||||||
value1,
|
arrayOf(
|
||||||
value2,
|
value1,
|
||||||
value3
|
value2,
|
||||||
))
|
value3
|
||||||
|
)
|
||||||
|
)
|
||||||
val basalProgram: BasalProgram = mapProfileToBasalProgram(profile)
|
val basalProgram: BasalProgram = mapProfileToBasalProgram(profile)
|
||||||
val entries: List<BasalProgram.Segment> = basalProgram.segments
|
val entries: List<BasalProgram.Segment> = basalProgram.segments
|
||||||
assertEquals(3, entries.size)
|
assertEquals(3, entries.size)
|
||||||
|
@ -69,8 +71,11 @@ class FunctionsTest {
|
||||||
val value = Mockito.mock(ProfileValue::class.java)
|
val value = Mockito.mock(ProfileValue::class.java)
|
||||||
value.timeAsSeconds = 1800
|
value.timeAsSeconds = 1800
|
||||||
value.value = 0.5
|
value.value = 0.5
|
||||||
PowerMockito.`when`(profile.basalValues).thenReturn(arrayOf(
|
PowerMockito.`when`(profile.basalValues).thenReturn(
|
||||||
value))
|
arrayOf(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
)
|
||||||
mapProfileToBasalProgram(profile)
|
mapProfileToBasalProgram(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,10 +90,12 @@ class FunctionsTest {
|
||||||
val value2 = Mockito.mock(ProfileValue::class.java)
|
val value2 = Mockito.mock(ProfileValue::class.java)
|
||||||
value2.timeAsSeconds = 86400
|
value2.timeAsSeconds = 86400
|
||||||
value2.value = 0.5
|
value2.value = 0.5
|
||||||
PowerMockito.`when`(profile.basalValues).thenReturn(arrayOf(
|
PowerMockito.`when`(profile.basalValues).thenReturn(
|
||||||
value1,
|
arrayOf(
|
||||||
value2
|
value1,
|
||||||
))
|
value2
|
||||||
|
)
|
||||||
|
)
|
||||||
mapProfileToBasalProgram(profile)
|
mapProfileToBasalProgram(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,8 +106,11 @@ class FunctionsTest {
|
||||||
val value = Mockito.mock(ProfileValue::class.java)
|
val value = Mockito.mock(ProfileValue::class.java)
|
||||||
value.timeAsSeconds = -1
|
value.timeAsSeconds = -1
|
||||||
value.value = 0.5
|
value.value = 0.5
|
||||||
PowerMockito.`when`(profile.basalValues).thenReturn(arrayOf(
|
PowerMockito.`when`(profile.basalValues).thenReturn(
|
||||||
value))
|
arrayOf(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
)
|
||||||
mapProfileToBasalProgram(profile)
|
mapProfileToBasalProgram(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,8 +119,11 @@ class FunctionsTest {
|
||||||
val value = Mockito.mock(ProfileValue::class.java)
|
val value = Mockito.mock(ProfileValue::class.java)
|
||||||
value.timeAsSeconds = 0
|
value.timeAsSeconds = 0
|
||||||
value.value = 0.04
|
value.value = 0.04
|
||||||
PowerMockito.`when`(profile.basalValues).thenReturn(arrayOf(
|
PowerMockito.`when`(profile.basalValues).thenReturn(
|
||||||
value))
|
arrayOf(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
)
|
||||||
val basalProgram: BasalProgram = mapProfileToBasalProgram(profile)
|
val basalProgram: BasalProgram = mapProfileToBasalProgram(profile)
|
||||||
val basalProgramElement: BasalProgram.Segment = basalProgram.segments[0]
|
val basalProgramElement: BasalProgram.Segment = basalProgram.segments[0]
|
||||||
assertEquals(5, basalProgramElement.basalRateInHundredthUnitsPerHour)
|
assertEquals(5, basalProgramElement.basalRateInHundredthUnitsPerHour)
|
||||||
|
|
Loading…
Reference in a new issue