This commit is contained in:
00asdf 2025-04-11 18:44:49 +02:00
commit b24ebba1f6
12 changed files with 315 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
### IntelliJ IDEA ###
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Kotlin ###
.kotlin
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
/.idea/misc.xml

3
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1,7 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<ScalaCodeStyleSettings>
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
</ScalaCodeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ReplaceUntilWithRangeUntil" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
</profile>
</component>

10
.idea/kotlinc.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.1" />
<option name="languageVersion" value="2.1" />
</component>
</project>

View File

@ -0,0 +1,17 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib:2.1.0" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
</SOURCES>
</library>
</component>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/VFHeatControl.iml" filepath="$PROJECT_DIR$/VFHeatControl.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

15
VFHeatControl.iml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>

View File

@ -0,0 +1,90 @@
package dev.asdf00.visionfive.hc
import java.nio.file.Files
import java.nio.file.Path
class GpioPin(val num: Int, val isInput: Boolean) : AutoCloseable {
var state: Boolean = false
get() {
if (isInput)
field = Files.readString(valuePath) == "1\n"
return field
}
set(newVal) {
if (isInput)
throw IllegalStateException("can not write state of input")
if (field == newVal)
return
if (!isInput)
Files.writeString(valuePath, if (newVal) "1" else "0")
field = newVal
}
private val basePath get() = Path.of("/sys/class/gpio")
private val pinPath get() = basePath.resolve("gpio$num")
private val valuePath get() = pinPath.resolve("value")
init {
if (num !in ALLOWED_PINS)
throw IllegalArgumentException("GPIO$num does not exist!")
println("initializing with 'echo $num > ${basePath.resolve("export")}'")
Files.writeString(basePath.resolve("export"), "$num")
Files.writeString(basePath.resolve("gpio$num").resolve("direction"), if (isInput) "in" else "out")
}
override fun close() {
if (!isInput)
state = false
Files.writeString(basePath.resolve("unexport"), "$num")
}
companion object {
private val ALLOWED_PINS = setOf(58, 57, 55, 5, 6, 42, 38, 43, 47, 54, 51, 52, 53, 50, 48, 49, 56, 45, 40, 37, 39, 46, 59, 63, 36, 60, 61, 44)
}
}
class PinGroup(vararg val pins: GpioPin) {
var state: BooleanArray
get() = BooleanArray(pins.size) { pins[it].state }
set(nuStates) {
if (nuStates.size != pins.size)
throw IllegalArgumentException("state count mismatch, expected ${pins.size}, got ${nuStates.size}")
for (i in nuStates.indices)
pins[i].state = nuStates[i]
}
var intState: Long
get() {
if (pins.size > 64)
throw UnsupportedOperationException("can not use intState for PinGroup with more than 64 pins")
var l = 0L
for (i in pins.indices.reversed()) {
l = l shl 1
l = l or if (pins[i].state) 1 else 0
}
return l
}
set(value) {
var l = value
if (pins.size > 64)
throw UnsupportedOperationException("can not use intState for PinGroup with more than 64 pins")
for (i in pins.indices) {
pins[i].state = l and 1 == 1L
l = l ushr 1
}
}
}
fun usingPins(vararg pins: Pair<Int, Boolean>, body: (Array<GpioPin>) -> Unit) {
fun recUsingPins(i: Int, pins: Array<GpioPin?>, pinIds: Array<out Pair<Int, Boolean>>, body: (Array<GpioPin>) -> Unit) {
if (i < pins.size) {
GpioPin(pinIds[i].first, pinIds[i].second).use {
pins[i] = it
recUsingPins(i + 1, pins, pinIds, body)
}
} else {
body(Array(pins.size) { pins[it]!! })
}
}
recUsingPins(0, arrayOfNulls(pins.size), pins, body)
}

View File

@ -0,0 +1,115 @@
package dev.asdf00.visionfive.hc
class Controller(private val senseBot: GpioPin, private val senseTop: GpioPin, private val motor: PinGroup) {
constructor(senseBot: GpioPin, senseTop: GpioPin, m0: GpioPin, m1: GpioPin, m2: GpioPin, m3: GpioPin) : this(
senseBot,
senseTop,
PinGroup(m0, m1, m2, m3)
)
init {
motor.intState = initialStep
}
private var internalState = -1
private var maxStep = -1
var state: Double
get() = if (internalState < 0) Double.NaN else internalState.toDouble() / maxStep.toDouble()
set(target) {
if (internalState < 0)
throw IllegalStateException("call Controller#runInitSequence before using it")
TODO()
}
fun runInitSequence() {
val prevState = runToBot()
val range = runToTop()
stepClock(range - prevState)
internalState = prevState
maxStep = range
}
private fun stepClock(cnt: Int = 1): Int {
var actual = cnt
for (i in 0..<cnt) {
if (!senseBot.state) {
doStep(clockStep)
} else {
actual = i
break
}
}
return actual
}
private fun stepAnti(cnt: Int = 1): Int {
var actual = cnt
for (i in 0..<cnt) {
if (!senseTop.state) {
doStep(antiStep)
} else {
actual = i
break
}
}
return actual
}
private fun runToBot(): Int {
var i = 0
while (true) {
if (senseBot.state)
return i
doStep(clockStep)
i++
}
}
private fun runToTop(): Int {
var i = 0
while (true) {
if (senseTop.state)
return i
doStep(antiStep)
i++
}
}
private fun doStep(step: LongArray) {
assert(motor.intState == initialStep, { "step must ALWAYS start on initial step state" })
for (l in step) {
motor.intState = l
Thread.sleep(stepDelay)
}
assert(motor.intState == initialStep, { "step must ALWAYS end on initial step state" })
}
companion object {
private val stepDelay = 10L
private val initialStep = 0b1001L
private val clockStep = longArrayOf(
0b0001,
0b0011,
0b0010,
0b0110,
0b0100,
0b1100,
0b1000,
0b1001,
)
private val antiStep = longArrayOf(
0b1000,
0b1100,
0b0100,
0b0110,
0b0010,
0b0011,
0b0001,
0b1001,
)
}
}