commit
f40450548f
154 changed files with 2421 additions and 558 deletions
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_FOR_MEMBERS" value="6" />
|
||||
<option name="BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES" value="1" />
|
||||
<option name="WRAP_EXPRESSION_BODY_FUNCTIONS" value="1" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="METHOD_ANNOTATION_WRAP" value="0" />
|
||||
|
@ -140,14 +141,28 @@
|
|||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="RIGHT_MARGIN" value="120" />
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="LINE_COMMENT_ADD_SPACE" value="true" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" 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="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>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</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://dl.bintray.com/kotlin/kotlinx" }
|
||||
}
|
||||
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.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 {
|
||||
repositories {
|
||||
google()
|
||||
|
@ -78,6 +84,8 @@ allprojects {
|
|||
includeModule("org.jetbrains.trove4j", "trove4j")
|
||||
}
|
||||
}
|
||||
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
|
||||
|
||||
}
|
||||
//Support @JvmDefault
|
||||
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
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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-kapt'
|
||||
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_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 {
|
||||
implementation project(':core')
|
||||
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() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
database = Room.inMemoryDatabaseBuilder(
|
||||
context, DashHistoryDatabase::class.java).build()
|
||||
context,
|
||||
DashHistoryDatabase::class.java
|
||||
).build()
|
||||
dao = database.historyRecordDao()
|
||||
dashHistory = DashHistory(dao, HistoryMapper())
|
||||
}
|
||||
|
@ -69,4 +71,4 @@ class DashHistoryTest {
|
|||
fun tearDown() {
|
||||
database.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ class RxSchedulerRule(val scheduler: Scheduler) : TestRule {
|
|||
RxJavaPlugins.reset()
|
||||
RxAndroidPlugins.reset()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash
|
|||
|
||||
import info.nightscout.androidaps.events.Event
|
||||
|
||||
class EventOmnipodDashPumpValuesChanged : Event()
|
||||
class EventOmnipodDashPumpValuesChanged : Event()
|
||||
|
|
|
@ -129,19 +129,31 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
// 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
|
||||
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
|
||||
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 {
|
||||
// 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 {
|
||||
|
@ -151,7 +163,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
|
||||
override fun cancelExtendedBolus(): PumpEnactResult {
|
||||
// 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 {
|
||||
|
@ -184,7 +197,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
|
||||
override fun loadTDDs(): PumpEnactResult {
|
||||
// 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 {
|
||||
|
@ -207,4 +221,4 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ class OmnipodDashHistoryModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideHistoryRecordDao(dashHistoryDatabase: DashHistoryDatabase): HistoryRecordDao = dashHistoryDatabase.historyRecordDao()
|
||||
internal fun provideHistoryRecordDao(dashHistoryDatabase: DashHistoryDatabase): HistoryRecordDao =
|
||||
dashHistoryDatabase.historyRecordDao()
|
||||
|
||||
@Provides
|
||||
@Reusable // no state, let system decide when to reuse or create new.
|
||||
|
@ -29,5 +30,4 @@ class OmnipodDashHistoryModule {
|
|||
@Singleton
|
||||
internal fun provideDashHistory(dao: HistoryRecordDao, historyMapper: HistoryMapper) =
|
||||
DashHistory(dao, historyMapper)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,4 +47,4 @@ abstract class OmnipodDashModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindsOmnipodDashManagerImpl(omnipodManager: OmnipodDashManagerImpl): OmnipodDashManager
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,4 +86,4 @@ abstract class OmnipodDashWizardViewModelsModule {
|
|||
@OmnipodPluginQualifier
|
||||
@ViewModelKey(PodDiscardedViewModel::class)
|
||||
internal abstract fun podDiscardedViewModel(viewModel: DashPodDiscardedViewModel): ViewModel
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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.BasalProgram
|
||||
import io.reactivex.Observable
|
||||
|
@ -9,9 +10,9 @@ import java.util.*
|
|||
|
||||
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>
|
||||
|
||||
|
@ -36,4 +37,4 @@ interface OmnipodDashManager {
|
|||
fun silenceAlerts(alerts: EnumSet<AlertType>): Observable<PodEvent>
|
||||
|
||||
fun deactivatePod(): Observable<PodEvent>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,21 +4,15 @@ import info.nightscout.androidaps.logging.AAPSLogger
|
|||
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.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.definition.ActivationProgress
|
||||
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.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.definition.*
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.*
|
||||
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.retryWithBackoff
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.Action
|
||||
import io.reactivex.functions.Consumer
|
||||
import java.util.*
|
||||
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>
|
||||
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>
|
||||
get() = Observable.defer {
|
||||
bleManager.sendCommand(GetVersionCommand.Builder() //
|
||||
.setSequenceNumber(podStateManager.messageSequenceNumber) //
|
||||
.setUniqueId(DEFAULT_UNIQUE_ID) //
|
||||
.build()) //
|
||||
bleManager.sendCommand(
|
||||
GetVersionCommand.Builder() //
|
||||
.setSequenceNumber(podStateManager.messageSequenceNumber) //
|
||||
.setUniqueId(DEFAULT_UNIQUE_ID) //
|
||||
.build()
|
||||
) //
|
||||
}
|
||||
|
||||
override fun activatePodPart1(): Observable<PodEvent> {
|
||||
override fun activatePodPart1(lowReservoirAlertTrigger: AlertTrigger.ReservoirVolumeTrigger?): Observable<PodEvent> {
|
||||
return Observable.concat(
|
||||
observePodReadyForActivationPart1,
|
||||
observeConnectToPod,
|
||||
observeSendGetVersionCommand
|
||||
// ... Send more commands
|
||||
) //
|
||||
observeActivationPart1Commands(lowReservoirAlertTrigger)
|
||||
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PHASE_1_COMPLETED))
|
||||
// TODO these would be common for any observable returned in a public function in this class
|
||||
.doOnNext(PodEventInterceptor()) //
|
||||
.doOnError(ErrorInterceptor())
|
||||
.subscribeOn(aapsSchedulers.io)
|
||||
}
|
||||
|
||||
override fun activatePodPart2(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
private fun observeActivationPart1Commands(lowReservoirAlertTrigger: AlertTrigger.ReservoirVolumeTrigger?): Observable<PodEvent> {
|
||||
val observables = createActivationPart1Observables(lowReservoirAlertTrigger)
|
||||
|
||||
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> {
|
||||
|
@ -149,6 +417,10 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
podStateManager.uniqueId = event.uniqueId
|
||||
}
|
||||
|
||||
is PodEvent.CommandSent -> {
|
||||
podStateManager.increaseMessageSequenceNumber()
|
||||
}
|
||||
|
||||
is PodEvent.ResponseReceived -> {
|
||||
podStateManager.increaseMessageSequenceNumber()
|
||||
handleResponse(event.response)
|
||||
|
@ -179,7 +451,6 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inner class ErrorInterceptor : Consumer<Throwable> {
|
||||
|
@ -187,6 +458,12 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
override fun accept(throwable: Throwable) {
|
||||
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 {
|
||||
val asInt = ByteBuffer.wrap(address).int
|
||||
return "${asInt}/${address.toHex()}"
|
||||
return "$asInt/${address.toHex()}"
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -33,5 +33,4 @@ data class Id(val address: ByteArray) {
|
|||
return Id(ByteBuffer.allocate(4).putInt(v).array())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,4 +14,4 @@ interface OmnipodDashBleManager {
|
|||
fun connect(): Observable<PodEvent>
|
||||
|
||||
fun disconnect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,18 +29,34 @@ import javax.inject.Inject
|
|||
import javax.inject.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
|
||||
|
||||
@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 {
|
||||
// TODO: locking?
|
||||
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
|
||||
val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> =
|
||||
mapOf(CharacteristicType.CMD to LinkedBlockingDeque(),
|
||||
CharacteristicType.DATA to LinkedBlockingDeque())
|
||||
mapOf(
|
||||
CharacteristicType.CMD to LinkedBlockingDeque(),
|
||||
CharacteristicType.DATA to LinkedBlockingDeque()
|
||||
)
|
||||
val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to $podAddress")
|
||||
var autoConnect = true
|
||||
|
@ -74,7 +90,18 @@ class OmnipodDashBleManagerImpl @Inject constructor(private val context: Context
|
|||
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 ->
|
||||
// TODO: when we are already connected,
|
||||
// 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)
|
||||
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";
|
||||
emitter.onNext(PodEvent.BluetoothConnecting)
|
||||
|
||||
|
@ -118,5 +148,4 @@ class OmnipodDashBleManagerImpl @Inject constructor(private val context: Context
|
|||
private const val CONNECT_TIMEOUT_MS = 7000
|
||||
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 info.nightscout.androidaps.logging.AAPSLogger
|
||||
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.exceptions.CharacteristicNotFoundException
|
||||
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.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
|
||||
|
@ -29,8 +32,10 @@ class ServiceDiscoverer(private val logger: AAPSLogger, private val gatt: Blueto
|
|||
?: throw CharacteristicNotFoundException(CharacteristicType.CMD.value)
|
||||
val dataChar = service.getCharacteristic(CharacteristicType.DATA.uuid) // TODO: this is never used
|
||||
?: throw CharacteristicNotFoundException(CharacteristicType.DATA.value)
|
||||
var chars = mapOf(CharacteristicType.CMD to cmdChar,
|
||||
CharacteristicType.DATA to dataChar)
|
||||
var chars = mapOf(
|
||||
CharacteristicType.CMD to cmdChar,
|
||||
CharacteristicType.DATA to dataChar
|
||||
)
|
||||
return chars
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,10 @@ import java.util.concurrent.LinkedBlockingQueue
|
|||
import java.util.concurrent.TimeUnit
|
||||
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 connected: CountDownLatch = CountDownLatch(1)
|
||||
|
@ -64,7 +67,10 @@ class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingP
|
|||
|
||||
private fun confirmWritePayload(expectedPayload: ByteArray, received: CharacteristicWriteConfirmationPayload) {
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
CharacteristicWriteConfirmationError(status)
|
||||
}
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicWrite with status/char/value " +
|
||||
status + "/" + byValue(characteristic.uuid.toString()) + "/" + characteristic.value.toHex())
|
||||
aapsLogger.debug(
|
||||
LTag.PUMPBTCOMM,
|
||||
"OnCharacteristicWrite with status/char/value " +
|
||||
status + "/" + byValue(characteristic.uuid.toString()) + "/" + characteristic.value.toHex()
|
||||
)
|
||||
try {
|
||||
if (writeQueue.size > 0) {
|
||||
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)
|
||||
val payload = characteristic.value
|
||||
val characteristicType = byValue(characteristic.uuid.toString())
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicChanged with char/value " +
|
||||
characteristicType + "/" +
|
||||
payload.toHex())
|
||||
aapsLogger.debug(
|
||||
LTag.PUMPBTCOMM,
|
||||
"OnCharacteristicChanged with char/value " +
|
||||
characteristicType + "/" +
|
||||
payload.toHex()
|
||||
)
|
||||
incomingPackets[characteristicType]!!.add(payload)
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class, CouldNotConfirmDescriptorWriteException::class)
|
||||
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()
|
||||
when (confirmed) {
|
||||
is DescriptorWriteConfirmationError -> throw CouldNotConfirmWriteException(confirmed.status)
|
||||
is DescriptorWriteConfirmationUUID -> if (confirmed.uuid != descriptorUUID) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "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)
|
||||
}
|
||||
is DescriptorWriteConfirmationUUID ->
|
||||
if (confirmed.uuid != descriptorUUID) {
|
||||
aapsLogger.warn(
|
||||
LTag.PUMPBTCOMM,
|
||||
"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 {
|
||||
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()
|
||||
}
|
||||
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) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Received delayed descriptor write confirmation")
|
||||
}
|
||||
|
@ -144,4 +170,4 @@ class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingP
|
|||
|
||||
private const val WRITE_CONFIRM_TIMEOUT_MS = 10 // the confirmation queue should be empty anyway
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@ sealed class CharacteristicWriteConfirmation
|
|||
|
||||
data class CharacteristicWriteConfirmationPayload(val payload: ByteArray) : CharacteristicWriteConfirmation()
|
||||
|
||||
data class CharacteristicWriteConfirmationError(val status: Int) : CharacteristicWriteConfirmation()
|
||||
data class CharacteristicWriteConfirmationError(val status: Int) : CharacteristicWriteConfirmation()
|
||||
|
|
|
@ -4,4 +4,4 @@ sealed class DescriptorWriteConfirmation
|
|||
|
||||
data class DescriptorWriteConfirmationUUID(val uuid: String) : DescriptorWriteConfirmation()
|
||||
|
||||
data class DescriptorWriteConfirmationError(val status: Int) : DescriptorWriteConfirmation()
|
||||
data class DescriptorWriteConfirmationError(val status: Int) : DescriptorWriteConfirmation()
|
||||
|
|
|
@ -2,9 +2,10 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
|||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class BleCommandHello(controllerId: Int) : BleCommand(BleCommandType.HELLO,
|
||||
class BleCommandHello(controllerId: Int) : BleCommand(
|
||||
BleCommandType.HELLO,
|
||||
ByteBuffer.allocate(6)
|
||||
.put(1.toByte()) // TODO find the meaning of this constant
|
||||
.put(4.toByte()) // TODO find the meaning of this constant
|
||||
.putInt(controllerId).array()
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
||||
|
||||
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 {
|
||||
|
||||
|
@ -10,4 +18,4 @@ enum class BleCommandType(val value: Byte) {
|
|||
values().firstOrNull { it.value == value }
|
||||
?: throw IllegalArgumentException("Unknown BleCommandType: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class BleIOBusyException : Exception()
|
||||
class BleIOBusyException : Exception()
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class CharacteristicNotFoundException(cmdCharacteristicUuid: String) : FailedToConnectException("characteristic not found: $cmdCharacteristicUuid")
|
||||
class CharacteristicNotFoundException(cmdCharacteristicUuid: String) : FailedToConnectException("characteristic not found: $cmdCharacteristicUuid")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class CouldNotConfirmDescriptorWriteException(override val message: String?) : Exception(message) {
|
||||
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(sent: String, confirmed: String) : this("Could not confirm write. Sent: {$sent} .Received: $confirmed")
|
||||
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
|
||||
|
||||
class CouldNotConfirmWriteException(override val message: String?) : Exception(message) {
|
||||
constructor(sent: ByteArray, confirmed: ByteArray) : this("Could not confirm write. Sent: {$sent} .Received: ${confirmed}")
|
||||
constructor(status: Int) : this("Could not confirm write. Write status: ${status}")
|
||||
}
|
||||
constructor(
|
||||
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.io.CharacteristicType
|
||||
|
||||
class CouldNotEnableNotifications(cmd: CharacteristicType) : Exception(cmd.value)
|
||||
class CouldNotEnableNotifications(cmd: CharacteristicType) : Exception(cmd.value)
|
||||
|
|
|
@ -2,4 +2,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.excepti
|
|||
|
||||
import info.nightscout.androidaps.utils.extensions.toHex
|
||||
|
||||
class CouldNotParseMessageException(val payload: ByteArray) : Exception("Could not parse message payload: ${payload.toHex()}")
|
||||
class CouldNotParseMessageException(val payload: ByteArray) : Exception("Could not parse message payload: ${payload.toHex()}")
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class CouldNotSendBleException(msg: String?) : Exception(msg)
|
||||
class CouldNotSendBleException(msg: String?) : Exception(msg)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class DescriptorNotFoundException : Exception()
|
||||
class DescriptorNotFoundException : Exception()
|
||||
|
|
|
@ -5,4 +5,4 @@ import android.os.ParcelUuid
|
|||
class DiscoveredInvalidPodException : Exception {
|
||||
constructor(message: String) : super(message)
|
||||
constructor(message: String, serviceUUIds: List<ParcelUuid?>) : super("$message service UUIDs: $serviceUUIds")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.excepti
|
|||
open class FailedToConnectException : Exception {
|
||||
constructor() : super()
|
||||
constructor(message: String?) : super(message)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.excepti
|
|||
class MessageIOException : Exception {
|
||||
constructor(msg: String) : super(msg)
|
||||
constructor(cause: Throwable) : super("Caught Exception during Message I/O", cause)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.excepti
|
|||
open class ScanFailException : Exception {
|
||||
constructor(message: String) : super(message)
|
||||
constructor(errorCode: Int) : super("errorCode$errorCode")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,4 @@ class ScanFailFoundTooManyException(devices: List<BleDiscoveredDevice>) : ScanFa
|
|||
private val devices: List<BleDiscoveredDevice> = ArrayList(devices)
|
||||
val discoveredDevices: List<BleDiscoveredDevice>
|
||||
get() = Collections.unmodifiableList(devices)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class ScanFailNotFoundException : ScanFailException("No Pod found")
|
||||
class ScanFailNotFoundException : ScanFailException("No Pod found")
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class ServiceNotFoundException(serviceUuid: String) : FailedToConnectException("service not found: $serviceUuid")
|
||||
class ServiceNotFoundException(serviceUuid: String) : FailedToConnectException("service not found: $serviceUuid")
|
||||
|
|
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
import android.bluetooth.BluetoothGatt
|
||||
|
@ -12,7 +14,13 @@ import java.util.concurrent.BlockingQueue
|
|||
import java.util.concurrent.TimeUnit
|
||||
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
|
||||
|
||||
|
@ -41,7 +49,13 @@ class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<Character
|
|||
* @param payload the data to send
|
||||
* @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) {
|
||||
synchronized(state) {
|
||||
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
|
||||
* @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() {
|
||||
for (type in CharacteristicType.values()) {
|
||||
val ch = chars[type]
|
||||
|
@ -97,4 +117,4 @@ class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<Character
|
|||
|
||||
private const val DEFAULT_IO_TIMEOUT_MS = 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,4 +19,4 @@ enum class CharacteristicType(val value: String) {
|
|||
values().firstOrNull { it.value == value }
|
||||
?: throw IllegalArgumentException("Unknown Characteristic Type: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io
|
|||
|
||||
enum class IOState {
|
||||
IDLE, WRITING, READING
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
|
|||
import info.nightscout.androidaps.utils.extensions.toHex
|
||||
|
||||
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
|
||||
|
||||
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")
|
||||
|
|
|
@ -63,4 +63,4 @@ class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) {
|
|||
throw MessageIOException(cause = e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ data class MessagePacket(
|
|||
val gateway: Boolean = false,
|
||||
val sas: Boolean = false, // TODO: understand
|
||||
val tfs: Boolean = false, // TODO: understand
|
||||
val version: Short = 0.toShort()) {
|
||||
val version: Short = 0.toShort()
|
||||
) {
|
||||
|
||||
fun asByteArray(): ByteArray {
|
||||
val bb = ByteBuffer.allocate(16 + payload.size)
|
||||
|
@ -90,7 +91,8 @@ data class MessagePacket(
|
|||
val priority = f2.get(1) != 0
|
||||
val lastMessage = f2.get(2) != 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) {
|
||||
throw CouldNotParseMessageException(payload)
|
||||
}
|
||||
|
@ -139,8 +141,7 @@ private class Flag(var value: Int = 0) {
|
|||
return 0
|
||||
}
|
||||
return 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Byte.toUnsignedInt() = this.toInt() and 0xff
|
||||
internal fun Byte.toUnsignedInt() = this.toInt() and 0xff
|
||||
|
|
|
@ -12,4 +12,4 @@ enum class MessageType(val value: Byte) {
|
|||
MessageType.values().firstOrNull { it.value == value }
|
||||
?: throw IllegalArgumentException("Unknown MessageType: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,12 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
|
|||
throw IncorrectPacketException(0, firstPacket)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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()
|
||||
if (payload.size <= FirstBlePacket.CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET) {
|
||||
val end = min(FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS, payload.size)
|
||||
ret.add(FirstBlePacket(
|
||||
totalFragments = 0,
|
||||
payload = payload.copyOfRange(0, end),
|
||||
size = payload.size.toByte(),
|
||||
crc32 = crc32,
|
||||
))
|
||||
ret.add(
|
||||
FirstBlePacket(
|
||||
totalFragments = 0,
|
||||
payload = payload.copyOfRange(0, end),
|
||||
size = payload.size.toByte(),
|
||||
crc32 = crc32,
|
||||
)
|
||||
)
|
||||
if (payload.size > FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS) {
|
||||
ret.add(LastOptionalPlusOneBlePacket(
|
||||
index = 1,
|
||||
payload = payload.copyOfRange(end, payload.size),
|
||||
size = (payload.size - end).toByte(),
|
||||
))
|
||||
ret.add(
|
||||
LastOptionalPlusOneBlePacket(
|
||||
index = 1,
|
||||
payload = payload.copyOfRange(end, payload.size),
|
||||
size = (payload.size - end).toByte(),
|
||||
)
|
||||
)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
val middleFragments = (payload.size - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS) / MiddleBlePacket.CAPACITY
|
||||
val rest = ((payload.size - middleFragments * MiddleBlePacket.CAPACITY) - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS).toByte()
|
||||
ret.add(FirstBlePacket(
|
||||
totalFragments = (middleFragments + 1).toByte(),
|
||||
payload = payload.copyOfRange(0, FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS),
|
||||
))
|
||||
val rest =
|
||||
((payload.size - middleFragments * MiddleBlePacket.CAPACITY) - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS).toByte()
|
||||
ret.add(
|
||||
FirstBlePacket(
|
||||
totalFragments = (middleFragments + 1).toByte(),
|
||||
payload = payload.copyOfRange(0, FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS),
|
||||
)
|
||||
)
|
||||
for (i in 1..middleFragments) {
|
||||
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 {
|
||||
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(
|
||||
index = i.toByte(),
|
||||
payload = p,
|
||||
))
|
||||
ret.add(
|
||||
MiddleBlePacket(
|
||||
index = i.toByte(),
|
||||
payload = p,
|
||||
)
|
||||
)
|
||||
}
|
||||
val end = min(LastBlePacket.CAPACITY, rest.toInt())
|
||||
ret.add(LastBlePacket(
|
||||
index = (middleFragments + 1).toByte(),
|
||||
size = rest,
|
||||
payload = payload.copyOfRange(middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS, middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + end),
|
||||
crc32 = crc32,
|
||||
))
|
||||
ret.add(
|
||||
LastBlePacket(
|
||||
index = (middleFragments + 1).toByte(),
|
||||
size = rest,
|
||||
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) {
|
||||
ret.add(LastOptionalPlusOneBlePacket(
|
||||
index = (middleFragments + 2).toByte(),
|
||||
size = (rest - LastBlePacket.CAPACITY).toByte(),
|
||||
payload = payload.copyOfRange(middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + LastBlePacket.CAPACITY, payload.size),
|
||||
))
|
||||
ret.add(
|
||||
LastOptionalPlusOneBlePacket(
|
||||
index = (middleFragments + 2).toByte(),
|
||||
size = (rest - LastBlePacket.CAPACITY).toByte(),
|
||||
payload = payload.copyOfRange(
|
||||
middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + LastBlePacket.CAPACITY,
|
||||
payload.size
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -19,20 +19,20 @@ class StringLengthPrefixEncoding {
|
|||
for ((index, key) in keys.withIndex()) {
|
||||
when {
|
||||
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) ->
|
||||
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
|
||||
index == keys.size - 1 && remaining.size == key.length ->
|
||||
return ret
|
||||
|
||||
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)
|
||||
val length = (remaining[0].toUnsignedInt() shl 1) or remaining[1].toUnsignedInt()
|
||||
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)
|
||||
remaining = remaining.copyOfRange(LENGTH_BYTES + length, remaining.size)
|
||||
|
@ -60,4 +60,4 @@ class StringLengthPrefixEncoding {
|
|||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
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_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_WITH_MIDDLE_PACKETS = MAX_SIZE - HEADER_SIZE_WITH_MIDDLE_PACKETS // we are not using crc32 or size
|
||||
internal const val CAPACITY_WITHOUT_MIDDLE_PACKETS =
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -92,4 +99,3 @@ data class LastOptionalPlusOneBlePacket(val index: Byte, val payload: ByteArray,
|
|||
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")
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -225,24 +228,25 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
|
|||
|
||||
companion object {
|
||||
|
||||
private val PUBLIC_KEY_SIZE = 32
|
||||
private val NONCE_SIZE = 16
|
||||
private val CONF_SIZE = 16
|
||||
private const val PUBLIC_KEY_SIZE = 32
|
||||
private const val NONCE_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 PDM_CONF_MAGIC_PREFIX = "KC_2_U".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 val SP2 = ",SP2="
|
||||
private val SPS1 = "SPS1="
|
||||
private val SPS2 = "SPS2="
|
||||
private val SP0GP0 = "SP0,GP0"
|
||||
private val P0 = "P0="
|
||||
private const val SP1 = "SP1="
|
||||
private const val SP2 = ",SP2="
|
||||
private const val SPS1 = "SPS1="
|
||||
private const val SPS2 = "SPS2="
|
||||
private const val SP0GP0 = "SP0,GP0"
|
||||
private const val P0 = "P0="
|
||||
private val UNKNOWN_P0_PAYLOAD = byteArrayOf(0xa5.toByte())
|
||||
}
|
||||
}
|
||||
|
@ -253,4 +257,4 @@ private fun aesCmac(key: ByteArray, data: ByteArray, result: ByteArray) {
|
|||
mac.init(KeyParameter(key))
|
||||
mac.update(data, 0, data.size)
|
||||
mac.doFinal(result, 0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,4 +17,4 @@ data class PairMessage(
|
|||
sequenceNumber = sequenceNumber,
|
||||
sas = true // TODO: understand why this is true for PairMessages
|
||||
),
|
||||
)
|
||||
)
|
||||
|
|
|
@ -15,16 +15,22 @@ class BleDiscoveredDevice(val scanResult: ScanResult, private val scanRecord: Sc
|
|||
val serviceUuids = scanRecord.serviceUuids
|
||||
if (serviceUuids.size != 9) {
|
||||
throw DiscoveredInvalidPodException("Expected 9 service UUIDs, got" + serviceUuids.size, serviceUuids)
|
||||
|
||||
}
|
||||
if (extractUUID16(serviceUuids[0]) != MAIN_SERVICE_UUID) {
|
||||
// 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?
|
||||
if (extractUUID16(serviceUuids[2]) != UNKNOWN_THIRD_SERVICE_UUID) {
|
||||
// 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 podId = hexPodId.toLong(16)
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,4 +88,4 @@ class BleDiscoveredDevice(val scanResult: ScanResult, private val scanRecord: Sc
|
|||
lotNo = parseLotNo()
|
||||
sequenceNo = parseSeqNo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,4 +45,4 @@ class PodScanner(private val logger: AAPSLogger, private val bluetoothAdapter: B
|
|||
const val POD_ID_NOT_ACTIVATED = 0xFFFFFFFFL
|
||||
private const val SCAN_DURATION_MS = 5000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,5 +45,4 @@ class ScanCollector(private val logger: AAPSLogger, private val podID: Long) : S
|
|||
}
|
||||
return Collections.unmodifiableList(ret)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status
|
|||
enum class ConnectionStatus {
|
||||
CONNECTED,
|
||||
NOT_CONNECTED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,3 @@ sealed class PodEvent {
|
|||
class CommandSent(val command: Command) : PodEvent()
|
||||
class ResponseReceived(val response: Response) : PodEvent()
|
||||
}
|
||||
|
||||
|
|
|
@ -13,12 +13,14 @@ class DeactivateCommand private constructor(
|
|||
) : NonceEnabledCommand(CommandType.DEACTIVATE, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(nonce) //
|
||||
.array())
|
||||
get() = appendCrc(
|
||||
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(nonce) //
|
||||
.array()
|
||||
)
|
||||
|
||||
override fun toString(): String = "DeactivateCommand{" +
|
||||
"nonce=" + nonce +
|
||||
|
@ -31,7 +33,12 @@ class DeactivateCommand private constructor(
|
|||
class Builder : NonceEnabledCommandBuilder<Builder, 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 {
|
||||
|
@ -39,4 +46,4 @@ class DeactivateCommand private constructor(
|
|||
private const val LENGTH: Short = 6
|
||||
private const val BODY_LENGTH: Byte = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,14 @@ class GetStatusCommand private constructor(
|
|||
) : HeaderEnabledCommand(CommandType.GET_STATUS, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.put(statusResponseType.value) //
|
||||
.array())
|
||||
get() = appendCrc(
|
||||
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.put(statusResponseType.value) //
|
||||
.array()
|
||||
)
|
||||
|
||||
class Builder : HeaderEnabledCommandBuilder<Builder, GetStatusCommand>() {
|
||||
|
||||
|
@ -35,7 +37,6 @@ class GetStatusCommand private constructor(
|
|||
|
||||
return GetStatusCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, statusResponseType!!)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -43,4 +44,4 @@ class GetStatusCommand private constructor(
|
|||
private const val LENGTH: Short = 3
|
||||
private const val BODY_LENGTH: Byte = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,14 @@ class GetVersionCommand private constructor(
|
|||
) : HeaderEnabledCommand(CommandType.GET_VERSION, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(uniqueId) //
|
||||
.array())
|
||||
get() = appendCrc(
|
||||
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(uniqueId) //
|
||||
.array()
|
||||
)
|
||||
|
||||
override fun toString(): String {
|
||||
return "GetVersionCommand{" +
|
||||
|
@ -41,4 +43,4 @@ class GetVersionCommand private constructor(
|
|||
private const val LENGTH: Short = 6
|
||||
private const val BODY_LENGTH: Byte = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,4 +67,4 @@ class ProgramAlertsCommand private constructor(
|
|||
init {
|
||||
this.alertConfigurations = ArrayList(alertConfigurations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,12 +48,19 @@ class ProgramBasalCommand private constructor(
|
|||
}
|
||||
val basalCommand = buffer.array()
|
||||
val interlockCommand = interlockCommand.encoded
|
||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (basalCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
|
||||
return appendCrc(ByteBuffer.allocate(basalCommand.size + interlockCommand.size + header.size) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(basalCommand) //
|
||||
.array())
|
||||
val header: ByteArray = encodeHeader(
|
||||
uniqueId,
|
||||
sequenceNumber,
|
||||
(basalCommand.size + interlockCommand.size).toShort(),
|
||||
multiCommandFlag
|
||||
)
|
||||
return appendCrc(
|
||||
ByteBuffer.allocate(basalCommand.size + interlockCommand.size + header.size) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(basalCommand) //
|
||||
.array()
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
@ -99,15 +106,33 @@ class ProgramBasalCommand private constructor(
|
|||
val pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram!!)
|
||||
val currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime)
|
||||
val checksum = ProgramBasalUtil.calculateChecksum(pulsesPerSlot, currentSlot)
|
||||
val longInsulinProgramElements: List<BasalInsulinProgramElement> = mapTenthPulsesPerSlotToLongInsulinProgramElements(ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram!!))
|
||||
val shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot)
|
||||
val currentBasalInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime)
|
||||
val interlockCommand = ProgramInsulinCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!,
|
||||
val longInsulinProgramElements: List<BasalInsulinProgramElement> =
|
||||
mapTenthPulsesPerSlotToLongInsulinProgramElements(
|
||||
ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram!!)
|
||||
)
|
||||
val shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(
|
||||
pulsesPerSlot
|
||||
)
|
||||
val currentBasalInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(
|
||||
longInsulinProgramElements,
|
||||
currentTime
|
||||
)
|
||||
val interlockCommand = ProgramInsulinCommand(
|
||||
uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!,
|
||||
shortInsulinProgramElements, checksum, currentSlot.index, currentSlot.eighthSecondsRemaining,
|
||||
currentSlot.pulsesRemaining, ProgramInsulinCommand.DeliveryType.BASAL)
|
||||
return ProgramBasalCommand(interlockCommand, uniqueId!!, sequenceNumber!!, multiCommandFlag,
|
||||
longInsulinProgramElements, programReminder!!, currentBasalInsulinProgramElement.index,
|
||||
currentBasalInsulinProgramElement.remainingTenthPulses, currentBasalInsulinProgramElement.delayUntilNextTenthPulseInUsec)
|
||||
currentSlot.pulsesRemaining, ProgramInsulinCommand.DeliveryType.BASAL
|
||||
)
|
||||
return ProgramBasalCommand(
|
||||
interlockCommand,
|
||||
uniqueId!!,
|
||||
sequenceNumber!!,
|
||||
multiCommandFlag,
|
||||
longInsulinProgramElements,
|
||||
programReminder!!,
|
||||
currentBasalInsulinProgramElement.index,
|
||||
currentBasalInsulinProgramElement.remainingTenthPulses,
|
||||
currentBasalInsulinProgramElement.delayUntilNextTenthPulseInUsec
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,4 +143,4 @@ class ProgramBasalCommand private constructor(
|
|||
this.remainingTenthPulsesInCurrentInsulinProgramElement = remainingTenthPulsesInCurrentInsulinProgramElement
|
||||
this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,15 +18,17 @@ class ProgramBeepsCommand private constructor(
|
|||
) : HeaderEnabledCommand(CommandType.PROGRAM_BEEPS, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.put(immediateBeepType.value) //
|
||||
.put(basalReminder.encoded) //
|
||||
.put(tempBasalReminder.encoded) //
|
||||
.put(bolusReminder.encoded) //
|
||||
.array())
|
||||
get() = appendCrc(
|
||||
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.put(immediateBeepType.value) //
|
||||
.put(basalReminder.encoded) //
|
||||
.put(tempBasalReminder.encoded) //
|
||||
.put(bolusReminder.encoded) //
|
||||
.array()
|
||||
)
|
||||
|
||||
class Builder : HeaderEnabledCommandBuilder<Builder, ProgramBeepsCommand>() {
|
||||
|
||||
|
@ -61,7 +63,15 @@ class ProgramBeepsCommand private constructor(
|
|||
requireNotNull(tempBasalReminder) { "tempBasalReminder 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!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,4 +80,4 @@ class ProgramBeepsCommand private constructor(
|
|||
private const val LENGTH: Short = 6
|
||||
private const val BODY_LENGTH: Byte = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,12 +31,19 @@ class ProgramBolusCommand private constructor(
|
|||
.putInt(0) // Delay between tenth extended pulses in usec
|
||||
.array()
|
||||
val interlockCommand = interlockCommand.encoded
|
||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (bolusCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
|
||||
return appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + bolusCommand.size) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(bolusCommand) //
|
||||
.array())
|
||||
val header: ByteArray = encodeHeader(
|
||||
uniqueId,
|
||||
sequenceNumber,
|
||||
(bolusCommand.size + interlockCommand.size).toShort(),
|
||||
multiCommandFlag
|
||||
)
|
||||
return appendCrc(
|
||||
ByteBuffer.allocate(header.size + interlockCommand.size + bolusCommand.size) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(bolusCommand) //
|
||||
.array()
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
@ -82,10 +89,28 @@ class ProgramBolusCommand private constructor(
|
|||
|
||||
val numberOfPulses = Math.round(numberOfUnits!! * 20).toShort()
|
||||
val byte10And11 = (numberOfPulses * delayBetweenPulsesInEighthSeconds!!).toShort()
|
||||
val interlockCommand = ProgramInsulinCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!, listOf(BolusShortInsulinProgramElement(numberOfPulses)), calculateChecksum(0x01.toByte(), byte10And11, numberOfPulses),
|
||||
0x01.toByte(), byte10And11, numberOfPulses, ProgramInsulinCommand.DeliveryType.BOLUS)
|
||||
val interlockCommand = ProgramInsulinCommand(
|
||||
uniqueId!!,
|
||||
sequenceNumber!!,
|
||||
multiCommandFlag,
|
||||
nonce!!,
|
||||
listOf(BolusShortInsulinProgramElement(numberOfPulses)),
|
||||
calculateChecksum(0x01.toByte(), byte10And11, numberOfPulses),
|
||||
0x01.toByte(),
|
||||
byte10And11,
|
||||
numberOfPulses,
|
||||
ProgramInsulinCommand.DeliveryType.BOLUS
|
||||
)
|
||||
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 BODY_LENGTH: Byte = 13
|
||||
private fun calculateChecksum(numberOfSlots: Byte, byte10And11: Short, numberOfPulses: Short): Short {
|
||||
return MessageUtil.calculateChecksum(ByteBuffer.allocate(7) //
|
||||
.put(numberOfSlots) //
|
||||
.putShort(byte10And11) //
|
||||
.putShort(numberOfPulses) //
|
||||
.putShort(numberOfPulses) //
|
||||
.array())
|
||||
return MessageUtil.calculateChecksum(
|
||||
ByteBuffer.allocate(7) //
|
||||
.put(numberOfSlots) //
|
||||
.putShort(byte10And11) //
|
||||
.putShort(numberOfPulses) //
|
||||
.putShort(numberOfPulses) //
|
||||
.array()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ class ProgramInsulinCommand internal constructor(
|
|||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
nonce: Int, insulinProgramElements:
|
||||
nonce: Int,
|
||||
insulinProgramElements:
|
||||
List<ShortInsulinProgramElement>,
|
||||
private val checksum: Short,
|
||||
private val byte9: Byte,
|
||||
|
@ -66,4 +67,4 @@ class ProgramInsulinCommand internal constructor(
|
|||
", multiCommandFlag=" + multiCommandFlag +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,13 +56,36 @@ class ProgramTempBasalCommand private constructor(
|
|||
|
||||
val durationInSlots = (durationInMinutes!! / 30).toByte()
|
||||
val pulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToPulsesPerSlot(durationInSlots, rateInUnitsPerHour!!)
|
||||
val tenthPulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToTenthPulsesPerSlot(durationInSlots.toInt(), rateInUnitsPerHour!!)
|
||||
val shortInsulinProgramElements = ProgramTempBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot)
|
||||
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)
|
||||
val tenthPulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToTenthPulsesPerSlot(
|
||||
durationInSlots.toInt(),
|
||||
rateInUnitsPerHour!!
|
||||
)
|
||||
val shortInsulinProgramElements = ProgramTempBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(
|
||||
pulsesPerSlot
|
||||
)
|
||||
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
|
||||
if (firstProgramElement.totalTenthPulses.toInt() == 0) {
|
||||
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 {
|
||||
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()) //
|
||||
.put(commandType.value) //
|
||||
|
@ -90,11 +115,18 @@ class ProgramTempBasalCommand private constructor(
|
|||
}
|
||||
val tempBasalCommand = buffer.array()
|
||||
val interlockCommand = interlockCommand.encoded
|
||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (tempBasalCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
|
||||
return appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + tempBasalCommand.size) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(tempBasalCommand) //
|
||||
.array())
|
||||
val header: ByteArray = encodeHeader(
|
||||
uniqueId,
|
||||
sequenceNumber,
|
||||
(tempBasalCommand.size + interlockCommand.size).toShort(),
|
||||
multiCommandFlag
|
||||
)
|
||||
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) {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(uniqueId) //
|
||||
.put(0x14.toByte()) // FIXME ??
|
||||
.put(0x04.toByte()) // FIXME ??
|
||||
.put(encodeInitializationTime(initializationTime)) //
|
||||
.putInt(lotNumber) //
|
||||
.putInt(podSequenceNumber) //
|
||||
.array())
|
||||
get() = appendCrc(
|
||||
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(uniqueId) //
|
||||
.put(0x14.toByte()) // FIXME ??
|
||||
.put(0x04.toByte()) // FIXME ??
|
||||
.put(encodeInitializationTime(initializationTime)) //
|
||||
.putInt(lotNumber) //
|
||||
.putInt(podSequenceNumber) //
|
||||
.array()
|
||||
)
|
||||
|
||||
override fun toString(): String {
|
||||
return "SetUniqueIdCommand{" +
|
||||
|
@ -65,7 +67,14 @@ class SetUniqueIdCommand private constructor(
|
|||
requireNotNull(lotNumber) { "lotNumber can not be null" }
|
||||
requireNotNull(podSequenceNumber) { "podSequenceNumber 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,12 +87,12 @@ class SetUniqueIdCommand private constructor(
|
|||
val instance = Calendar.getInstance()
|
||||
instance.time = date
|
||||
return byteArrayOf( //
|
||||
(instance[Calendar.MONTH] + 1).toByte(), //
|
||||
instance[Calendar.DATE].toByte(), //
|
||||
(instance[Calendar.YEAR] % 100).toByte(), //
|
||||
instance[Calendar.HOUR_OF_DAY].toByte(), //
|
||||
(instance[Calendar.MONTH] + 1).toByte(), //
|
||||
instance[Calendar.DATE].toByte(), //
|
||||
(instance[Calendar.YEAR] % 100).toByte(), //
|
||||
instance[Calendar.HOUR_OF_DAY].toByte(), //
|
||||
instance[Calendar.MINUTE].toByte() //
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,13 +18,15 @@ class SilenceAlertsCommand private constructor(
|
|||
|
||||
override val encoded: ByteArray
|
||||
get() =
|
||||
appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(nonce) //
|
||||
.put(AlertUtil.encodeAlertSet(alertTypes)) //
|
||||
.array())
|
||||
appendCrc(
|
||||
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(nonce) //
|
||||
.put(AlertUtil.encodeAlertSet(alertTypes)) //
|
||||
.array()
|
||||
)
|
||||
|
||||
override fun toString(): String {
|
||||
return "SilenceAlertsCommand{" +
|
||||
|
@ -57,4 +59,4 @@ class SilenceAlertsCommand private constructor(
|
|||
private const val LENGTH = 7.toShort()
|
||||
private const val BODY_LENGTH = 5.toByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,15 @@ class StopDeliveryCommand private constructor(
|
|||
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(nonce) //
|
||||
.put((beepType.value.toInt() shl 4 or deliveryType.encoded[0].toInt()).toByte()) //
|
||||
.array())
|
||||
return appendCrc(
|
||||
ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(nonce) //
|
||||
.put((beepType.value.toInt() shl 4 or deliveryType.encoded[0].toInt()).toByte()) //
|
||||
.array()
|
||||
)
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
override val encoded: ByteArray
|
||||
|
@ -72,7 +79,14 @@ class StopDeliveryCommand private constructor(
|
|||
requireNotNull(deliveryType) { "deliveryType 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!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,4 +95,4 @@ class StopDeliveryCommand private constructor(
|
|||
private const val LENGTH: Short = 7
|
||||
private const val BODY_LENGTH: Byte = 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,4 @@ import java.io.Serializable
|
|||
interface Command : Encodable, Serializable {
|
||||
|
||||
val commandType: CommandType
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ enum class CommandType(val value: Byte) {
|
|||
GET_VERSION(0x07.toByte()),
|
||||
GET_STATUS(0x0e.toByte()),
|
||||
SILENCE_ALERTS(0x11.toByte()),
|
||||
PROGRAM_BASAL(0x13.toByte()), // Always preceded by 0x1a
|
||||
PROGRAM_TEMP_BASAL(0x16.toByte()), // Always preceded by 0x1a
|
||||
PROGRAM_BOLUS(0x17.toByte()), // Always preceded by 0x1a
|
||||
PROGRAM_BASAL(0x13.toByte()), // Always preceded by 0x1a
|
||||
PROGRAM_TEMP_BASAL(0x16.toByte()), // Always preceded by 0x1a
|
||||
PROGRAM_BOLUS(0x17.toByte()), // Always preceded by 0x1a
|
||||
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()),
|
||||
PROGRAM_BEEPS(0x1e.toByte()),
|
||||
STOP_DELIVERY(0x1f.toByte());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,12 @@ abstract class HeaderEnabledCommand protected constructor(
|
|||
.putShort(MessageUtil.createCrc(command)) //
|
||||
.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) //
|
||||
.putInt(uniqueId) //
|
||||
.putShort((sequenceNumber.toInt() and 0x0f shl 10 or length.toInt() or ((if (multiCommandFlag) 1 else 0) shl 15)).toShort()) //
|
||||
|
|
|
@ -6,4 +6,4 @@ abstract class NonceEnabledCommand protected constructor(
|
|||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
protected val nonce: Int
|
||||
) : HeaderEnabledCommand(commandType, uniqueId, sequenceNumber, multiCommandFlag)
|
||||
) : HeaderEnabledCommand(commandType, uniqueId, sequenceNumber, multiCommandFlag)
|
||||
|
|
|
@ -5,4 +5,4 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.b
|
|||
interface CommandBuilder<R : Command> {
|
||||
|
||||
fun build(): R
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,4 +31,4 @@ abstract class HeaderEnabledCommandBuilder<T : HeaderEnabledCommandBuilder<T, R>
|
|||
}
|
||||
|
||||
protected abstract fun buildCommand(): R
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,4 +15,4 @@ abstract class NonceEnabledCommandBuilder<T : NonceEnabledCommandBuilder<T, R>,
|
|||
this.nonce = nonce
|
||||
return this as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,4 +31,4 @@ open class BasalInsulinProgramElement(
|
|||
", delayBetweenTenthPulsesInUsec=" + delayBetweenTenthPulsesInUsec +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,17 @@ import kotlin.experimental.and
|
|||
|
||||
class BasalShortInsulinProgramElement(
|
||||
private val numberOfSlots: Byte, // 4 bits
|
||||
private val pulsesPerSlot: Short, //10 bits
|
||||
private val pulsesPerSlot: Short, // 10 bits
|
||||
private val extraAlternatePulse: Boolean
|
||||
) : ShortInsulinProgramElement {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
val firstByte = (numberOfSlots - 1 and 0x0f shl 4 //
|
||||
or ((if (extraAlternatePulse) 1 else 0) shl 3) //
|
||||
or (pulsesPerSlot.toInt() ushr 8 and 0x03)).toByte()
|
||||
val firstByte = (
|
||||
numberOfSlots - 1 and 0x0f shl 4 //
|
||||
or ((if (extraAlternatePulse) 1 else 0) shl 3) //
|
||||
or (pulsesPerSlot.toInt() ushr 8 and 0x03)
|
||||
).toByte()
|
||||
return ByteBuffer.allocate(2) //
|
||||
.put(firstByte) //
|
||||
.put((pulsesPerSlot and 0xff).toByte()) //
|
||||
|
@ -27,4 +29,4 @@ class BasalShortInsulinProgramElement(
|
|||
", extraAlternatePulse=" + extraAlternatePulse +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,4 @@ class BolusShortInsulinProgramElement(
|
|||
|
||||
override val encoded: ByteArray
|
||||
get() = ByteBuffer.allocate(2).putShort(numberOfPulses).array()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class CurrentBasalInsulinProgramElement(
|
|||
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
|
||||
", remainingTenthPulses=" + remainingTenthPulses +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class CurrentSlot(
|
|||
", eighthSecondsRemaining=" + eighthSecondsRemaining +
|
||||
", pulsesRemaining=" + pulsesRemaining +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable
|
||||
import java.io.Serializable
|
||||
|
||||
interface ShortInsulinProgramElement : Encodable, Serializable
|
||||
interface ShortInsulinProgramElement : Encodable, Serializable
|
||||
|
|
|
@ -21,4 +21,4 @@ class TempBasalInsulinProgramElement(
|
|||
}
|
||||
return buffer.array()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,13 @@ object ProgramBasalUtil {
|
|||
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]
|
||||
numberOfSlotsInCurrentElement = 1
|
||||
} 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]
|
||||
numberOfSlotsInCurrentElement = 1
|
||||
startSlotIndex = (numberOfSlotsInCurrentElement + startSlotIndex).toByte()
|
||||
|
@ -38,7 +44,13 @@ object ProgramBasalUtil {
|
|||
numberOfSlotsInCurrentElement++
|
||||
}
|
||||
}
|
||||
elements.add(insulinProgramElementFactory(startSlotIndex, numberOfSlotsInCurrentElement, (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement).toShort()))
|
||||
elements.add(
|
||||
insulinProgramElementFactory(
|
||||
startSlotIndex,
|
||||
numberOfSlotsInCurrentElement,
|
||||
(previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement).toShort()
|
||||
)
|
||||
)
|
||||
return elements
|
||||
}
|
||||
|
||||
|
@ -60,7 +72,13 @@ object ProgramBasalUtil {
|
|||
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) {
|
||||
numberOfSlotsInCurrentElement++
|
||||
} else {
|
||||
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
|
||||
elements.add(
|
||||
BasalShortInsulinProgramElement(
|
||||
numberOfSlotsInCurrentElement,
|
||||
previousPulsesPerSlot,
|
||||
extraAlternatePulse
|
||||
)
|
||||
)
|
||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
|
||||
numberOfSlotsInCurrentElement = 1
|
||||
extraAlternatePulse = false
|
||||
|
@ -82,7 +100,13 @@ object ProgramBasalUtil {
|
|||
numberOfSlotsInCurrentElement++
|
||||
} else {
|
||||
// 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()]
|
||||
numberOfSlotsInCurrentElement = 1
|
||||
extraAlternatePulse = false
|
||||
|
@ -90,7 +114,13 @@ object ProgramBasalUtil {
|
|||
}
|
||||
} else {
|
||||
// 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()]
|
||||
numberOfSlotsInCurrentElement = 1
|
||||
extraAlternatePulse = false
|
||||
|
@ -100,7 +130,13 @@ object ProgramBasalUtil {
|
|||
}
|
||||
} else if (previousPulsesPerSlot != pulsesPerSlot[currentTotalNumberOfSlots.toInt()]) {
|
||||
// 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()]
|
||||
currentTotalNumberOfSlots++
|
||||
extraAlternatePulse = false
|
||||
|
@ -109,7 +145,13 @@ object ProgramBasalUtil {
|
|||
throw IllegalStateException("Reached illegal point in mapBasalProgramToShortInsulinProgramElements")
|
||||
}
|
||||
}
|
||||
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
|
||||
elements.add(
|
||||
BasalShortInsulinProgramElement(
|
||||
numberOfSlotsInCurrentElement,
|
||||
previousPulsesPerSlot,
|
||||
extraAlternatePulse
|
||||
)
|
||||
)
|
||||
return elements
|
||||
}
|
||||
|
||||
|
@ -117,7 +159,8 @@ object ProgramBasalUtil {
|
|||
val tenthPulsesPerSlot = ShortArray(NUMBER_OF_BASAL_SLOTS.toInt())
|
||||
for (segment in basalProgram.segments) {
|
||||
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
|
||||
|
@ -157,7 +200,10 @@ object ProgramBasalUtil {
|
|||
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()
|
||||
instance.time = currentTime
|
||||
val hourOfDay = instance[Calendar.HOUR_OF_DAY]
|
||||
|
@ -176,8 +222,10 @@ object ProgramBasalUtil {
|
|||
}
|
||||
val durationInSeconds = endTimeInSeconds - startTimeInSeconds
|
||||
val secondsPassedInCurrentSlot = secondOfDay - startTimeInSeconds
|
||||
val remainingTenThousandthPulses = ((durationInSeconds - secondsPassedInCurrentSlot) / durationInSeconds.toDouble() * totalNumberOfTenThousandthPulsesInSlot).toLong()
|
||||
val delayBetweenTenthPulsesInUsec = (durationInSeconds * 1000000L * 1000 / totalNumberOfTenThousandthPulsesInSlot).toInt()
|
||||
val remainingTenThousandthPulses =
|
||||
((durationInSeconds - secondsPassedInCurrentSlot) / durationInSeconds.toDouble() * totalNumberOfTenThousandthPulsesInSlot).toLong()
|
||||
val delayBetweenTenthPulsesInUsec =
|
||||
(durationInSeconds * 1000000L * 1000 / totalNumberOfTenThousandthPulsesInSlot).toInt()
|
||||
val secondsRemaining = secondsPassedInCurrentSlot % 1800
|
||||
var delayUntilNextTenthPulseInUsec = delayBetweenTenthPulsesInUsec
|
||||
for (i in 0 until secondsRemaining) {
|
||||
|
@ -186,7 +234,8 @@ object ProgramBasalUtil {
|
|||
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)
|
||||
}
|
||||
index++
|
||||
|
@ -205,4 +254,4 @@ object ProgramBasalUtil {
|
|||
}
|
||||
return MessageUtil.calculateChecksum(buffer.array())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,13 @@ import kotlin.math.roundToInt
|
|||
object ProgramTempBasalUtil {
|
||||
|
||||
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 {
|
||||
|
@ -59,4 +65,4 @@ object ProgramTempBasalUtil {
|
|||
|
||||
fun mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot: ShortArray?): List<ShortInsulinProgramElement> =
|
||||
ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,9 @@ enum class ActivationProgress {
|
|||
REPROGRAMMED_LUMP_OF_COAL_ALERT,
|
||||
PRIMING,
|
||||
PRIME_COMPLETED,
|
||||
PROGRAMMED_USER_SET_EXPIRATION_ALERT,
|
||||
PHASE_1_COMPLETED,
|
||||
PROGRAMMED_BASAL,
|
||||
PROGRAMMED_CANCEL_LOC_ETC_ALERT,
|
||||
UPDATED_EXPIRATION_ALERTS,
|
||||
INSERTING_CANNULA,
|
||||
CANNULA_INSERTED,
|
||||
COMPLETED;
|
||||
|
@ -19,4 +18,4 @@ enum class ActivationProgress {
|
|||
fun isBefore(other: ActivationProgress): Boolean = ordinal < other.ordinal
|
||||
|
||||
fun isAtLeast(other: ActivationProgress): Boolean = ordinal >= other.ordinal
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,5 +159,4 @@ enum class AlarmType(override val value: Byte) : HasValue {
|
|||
ALARM_BLE_QN_EXCEED_MAX_RETRY(0xc1.toByte()),
|
||||
ALARM_BLE_QN_CRIT_VAR_FAIL(0xc2.toByte()),
|
||||
UNKNOWN(0xff.toByte());
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,15 +29,17 @@ class AlertConfiguration(
|
|||
return ByteBuffer.allocate(6) //
|
||||
.put(firstByte)
|
||||
.put(durationInMinutes.toByte()) //
|
||||
.putShort(when (trigger) {
|
||||
is AlertTrigger.ReservoirVolumeTrigger -> {
|
||||
trigger.thresholdInMicroLiters
|
||||
}
|
||||
.putShort(
|
||||
when (trigger) {
|
||||
is AlertTrigger.ReservoirVolumeTrigger -> {
|
||||
trigger.thresholdInMicroLiters
|
||||
}
|
||||
|
||||
is AlertTrigger.TimerTrigger -> {
|
||||
trigger.offsetInMinutes
|
||||
is AlertTrigger.TimerTrigger -> {
|
||||
trigger.offsetInMinutes
|
||||
}
|
||||
}
|
||||
}) //
|
||||
) //
|
||||
.put(beepRepetition.value) //
|
||||
.put(beepType.value) //
|
||||
.array()
|
||||
|
@ -54,4 +56,4 @@ class AlertConfiguration(
|
|||
", beepRepetition=" + beepRepetition +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definiti
|
|||
sealed class AlertTrigger {
|
||||
class TimerTrigger(val offsetInMinutes: Short) : AlertTrigger()
|
||||
class ReservoirVolumeTrigger(val thresholdInMicroLiters: Short) : AlertTrigger()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,4 @@ enum class AlertType(val index: Byte) : HasValue {
|
|||
} else {
|
||||
(1 shl index.toInt()).toByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,4 +87,4 @@ class BasalProgram(
|
|||
override fun hashCode(): Int {
|
||||
return segments.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,5 +10,4 @@ enum class BeepRepetitionType(
|
|||
XXX3(0x05.toByte()), // Used in user pod expiration alert
|
||||
XXX4(0x06.toByte()), // Used in pod expiration alert
|
||||
XXX5(0x08.toByte()); // Used in imminent pod expiration alert
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ enum class BeepType(val value: Byte) {
|
|||
SILENT(0x00.toByte()),
|
||||
FOUR_TIMES_BIP_BEEP(0x02.toByte()), // Used in low reservoir alert, user expiration alert, expiration alert, imminent expiration alert, lump of coal alert
|
||||
LONG_SINGLE_BEEP(0x06.toByte()); // Used in stop delivery command
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,4 +11,4 @@ enum class DeliveryStatus(override val value: Byte) : HasValue {
|
|||
BOLUS_AND_BASAL_ACTIVE(0x05.toByte()),
|
||||
BOLUS_AND_TEMP_BASAL_ACTIVE(0x06.toByte()),
|
||||
UNKNOWN(0xff.toByte());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definiti
|
|||
interface Encodable {
|
||||
|
||||
val encoded: ByteArray
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,4 +34,4 @@ enum class NakErrorType(override val value: Byte) : HasValue {
|
|||
IGNORE_COMMAND(0x1c.toByte()),
|
||||
INVALID_CRC(0x1d.toByte()),
|
||||
UNKNOWN(0xff.toByte());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,4 @@ enum class PodStatus(override val value: Byte) : HasValue {
|
|||
UNKNOWN(0xff.toByte());
|
||||
|
||||
fun isRunning(): Boolean = this == RUNNING_ABOVE_MIN_VOLUME || this == RUNNING_BELOW_MIN_VOLUME
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@ class ProgramReminder(
|
|||
) : Encodable, Serializable {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() = byteArrayOf(((if (atStart) 1 else 0) shl 7
|
||||
or ((if (atEnd) 1 else 0) shl 6)
|
||||
or ((atInterval and 0x3f).toInt())).toByte())
|
||||
}
|
||||
get() = byteArrayOf(
|
||||
(
|
||||
(if (atStart) 1 else 0) shl 7
|
||||
or ((if (atEnd) 1 else 0) shl 6)
|
||||
or ((atInterval and 0x3f).toInt())
|
||||
).toByte()
|
||||
)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue