I try to use the pigpio library with Kotlin/Native. To get started I followed this talk:
Bridge The Physical World: Kotlin Native on Raspberry Pi
The sigHandler: Unhandled signal 11, terminating occurs, when I try to assign a value I get from a callback to a global variable lastChange = tick on line 54
The whole code for my testing looks like this:
package ch.lichtwellenreiter.omrr
import kotlinx.cinterop.staticCFunction
import pigpio.*
const val GPIO_BUTTON = 6
var lastChange: UInt = 0u
fun main() {
initGPIO()
println()
setupButton()
println()
while (true) {}
}
private fun initGPIO() {
println("Init GPIO")
if (gpioInitialise() < 0) {
println("GPIO Error initialising")
return
}
}
private fun setupButton() {
println("Setup pin")
val buttonPort = GPIO_BUTTON.toUInt()
initPortWithMode(buttonPort, PI_INPUT)
println("Register callback for pin")
gpioSetAlertFunc(buttonPort, flankChangeDetected)
}
private fun initPortWithMode(port: UInt, mode: Int) {
if (gpioSetMode(port, mode.toUInt()) < 0) {
println("Could not set mode for GPIO$port")
return
}
}
val flankChangeDetected = staticCFunction<Int, Int, UInt, Unit> { gpio, level, tick ->
println("Callback called")
val ticker: UInt = tick
val pin: Int = gpio
val lvl: Int = level
println("Calculate time")
val time = ticker - lastChange
println("Set lastChange")
lastChange = tick
println("Is DCC signal?")
if ((time > 55u && time < 61u) || (time > 113u && time < 119u)) println(time)
println()
}
How can I prevent this error?
After some trying I have found a solution, but don't know if it is the best way to do it.
A global variable as an AtomicInt didn't work, but this way it works:
#SharedImmutable
val sharedData = SharedData()
class SharedData {
var lastChange = AtomicInt(0)
}
This way I can access the valua via val lastChange = sharedData.lastChange.value and sharedData.lastChange.compareAndSet(lastChange, tick.toInt())
I would guess the problem is related to Kotlin/Native immutability rules. This issue I found convinced me to think that the callback is being called not on the main thread. If that's true, the error is caused by violating mutable XOR shared K/N's rule. Cannot write a snippet at the moment, but I think you can either try using AtomicInt or, if it fits the need here, #ThreadLocal annotation.
Related
I am looking for an Android USB-Serial example in Kotlin.
Almost every example I have found is in Java, which I suppose is still ok, but I haven't been able to compile any of them successfully.
I am quite new to Android / Kotlin, and just want to get the most basic send and receive going from my phone to a USB device over an OTG cable.
Any help / pointers / suggestions gratefully received.
Thank you
Garrett
I was able to dig up some old code to reference for this. I don't guarantee any of it, but in combination with your Java based libraries, this should be a start, and then combining with reference from: https://github.com/mik3y/usb-serial-for-android
One thing I recall being a massive problem, was we had to have the app as a system level app. Which was either by installing it in a specific directory and checking the "System App" permission.
class SerialPort(private val serialPort: Any) {
private val serialPortR: Class<*> = Class.forName("android.hardware.SerialPort")
fun read(buffer: ByteBuffer): Int {
val read = serialPortR.getMethod("read", ByteBuffer::class.java)
return read.invoke(this, buffer) as Int
}
fun write(buffer: ByteBuffer, len: Int) {
val write = serialPortR.getMethod("write", ByteBuffer::class.java, Int::class.java)
write.invoke(this, buffer, len)
}
fun close() {
val close = serialPortR.getMethod("close")
close.invoke(this)
}
}
class SerialManager private constructor(private val serialService: Any) {
private val serialManager: Class<*> = Class.forName("android.hardware.SerialManager")
fun openSerialPort(port: String, baudRate: Int): SerialPort = reflectSerialPort(port, baudRate)
private fun reflectSerialPort(port: String, baudRate: Int): SerialPort {
serialManager.cast(this)
val openSerialPort = serialManager.getMethod("openSerialPort", String::class.java, Int::class.java)
return SerialPort(openSerialPort.invoke(serialManager, port, baudRate))
}
companion object {
fun get(c: Context): SerialManager {
val serialService = getSerialService(c)
return SerialManager(serialService)
}
#SuppressLint("WrongConstant")
private fun getSerialService(c: Context) = c.getSystemService("serial") ?: throw Exception("No Serial Service")
}
}
For some reasom my CoroutineScope just stopped working, though, nothing crucial has been changed and errors don't show up. The program skips it and my progress bar remains unchanged. Do you have any idea what could have possibly caused this issue?
class HomeFragment : BaseFragment(R.layout.fragment_home) {
private var todayEventsCount: Int = 0
private var todayCheckedEventsCount: Int = 0
private val mHandler = Handler()
private var mAdapter: FirebaseRecyclerAdapter<EventModel, EventsHolder>? = null
private lateinit var mRunnable: Runnable
private lateinit var barList: ArrayList<BarEntry>
private lateinit var barDataSet: BarDataSet
private lateinit var barData: BarData
override fun onResume() {
super.onResume()
initFields()
}
override fun onPause() {
super.onPause()
home_events_list.removeAllViewsInLayout()
todayCheckedEventsCount = 0
}
#SuppressLint("SetTextI18n")
private fun initFields() {
val progress: ProgressBar = ev_progress_bar
val text: TextView = count
getTodayEvents()
CoroutineScope(Dispatchers.IO).launch {
mRunnable = Runnable {
if (mAdapter?.itemCount != 0) {
todayEventsCount = mAdapter?.itemCount!!
for (i in 0 until todayEventsCount) {
if (mAdapter?.getItem(i)!!.checked == "1") {
todayCheckedEventsCount++
progress.progress = todayCheckedEventsCount
text.text = progress.progress.toString() + "/" + progress.max.toString()
}
}
todayCheckedEventsCount = 0
}
mHandler.postDelayed(mRunnable, 30)
}
mHandler.post(mRunnable)
}
initChart()
}
There's no point in posting runnables if you're using a coroutine. Half the reason coroutines exist is to avoid this messy nested juggling of callbacks. There are also a few errors I see in your coroutine:
It runs on Dispatchers.IO, even though it never does anything blocking.
It runs on a throwaway CoroutineScope that is never cancelled. You should never use the CoroutineScope() constructor if you're not immediately assigning it to a variable or property through which you can cancel it at the appropriate time. You should be using viewLifeCycleOwner.lifecycleScope instead, since it's already set up to automatically cancel at the appropriate time to avoid crashes.
Your runnables you're posting are not canceled at the appropriate time, so even if you use the appropriate scope, they can still cause crashes. But as mentioned above, you don't need them in a coroutine.
(mAdapter?.itemCount != 0) will evaluate to true even if mAdapter is null, which will promptly cause a crash when you use mAdapter!!.
Fixed code looks like this. If this still doesn't do anything, you'll want to check to make sure your adapter is not null at the time this is called.
private fun initFields() {
val progress: ProgressBar = ev_progress_bar
val text: TextView = count
getTodayEvents()
viewLifeCycleOwner.lifecycleScope.launch {
val adapter = mAdapter ?: return
while (true) {
if (adapter.itemCount != 0) {
todayEventsCount = adapter.itemCount
for (i in 0 until todayEventsCount) {
if (adapter.getItem(i).checked == "1") {
todayCheckedEventsCount++
progress.progress = todayCheckedEventsCount
text.text = progress.progress.toString() + "/" + progress.max.toString()
}
}
todayCheckedEventsCount = 0
}
delay(30)
}
}
initChart()
}
I didn't try to follow your logic of what these todayEventsCount and todayCheckedEventsCount properties are. They probably should be locally defined variables in the coroutine.
You're also going to need some condition in the while loop that breaks you out of it. I didn't look closely enough to see what that condition should be. Your original code doesn't break the loop of reposting the runnable forever.
I am currently trying to learn Kotlin with the help of the book "Kotlin Programming The Big Nerd Ranch Guide" and so far everything worked.
But now I am struggling with the "lazy" initialization which throws a NullPointerException which says
Cannot invoke "kotlin.Lazy.getValue()" because "< local1>" is null
The corresponding lines are:
val hometown by lazy { selectHometown() }
private fun selectHometown(): String = File("data/towns.txt").readText().split("\n").shuffled().first()
In case you want to compile it yourself or need more code for a better understanding I provide the Game.kt and Player.kt down below. If "lazy" is dropped for a "normal" initialization the hometown gets assigned as intended.
Any tips for solving the problem and understanding the cause of it is welcome.
// Game.kt
package com.bignerdranch.nyethack
fun main(args: Array<String>) {
val player = Player("Madrigal")
player.castFireball()
}
private fun printPlayerStatus(player: Player) {
println("(Aura: ${player.auraColor()}) " + "(Blessed: ${if (player.isBlessed) "YES" else "NO"})")
println("${player.name} ${player.formatHealthStatus()}")
}
// Player.kt
package com.bignerdranch.nyethack
import java.io.File
class Player(_name: String, var healthPoints: Int = 100, val isBlessed: Boolean, private val isImmortal: Boolean) {
var name = _name
get() = "${field.capitalize()} of $hometown"
private set(value) {
field = value.trim()
}
constructor(name: String) : this(name, isBlessed = true, isImmortal = false) {
if (name.toLowerCase() == "kar") healthPoints = 40
}
init {
require(healthPoints > 0, { "healthPoints must be greater than zero." })
require(name.isNotBlank(), { "Player must have a name" })
}
val hometown by lazy { selectHometown() }
private fun selectHometown(): String = File("data/towns.txt").readText().split("\n").shuffled().first()
fun castFireball(numFireballs: Int = 2) =
println("A glass of Fireball springs into existence. (x$numFireballs)")
fun auraColor(): String {
val auraVisible = isBlessed && healthPoints > 60 || isImmortal
return if (auraVisible) "GREEN" else "NONE"
}
fun formatHealthStatus() =
when (healthPoints) {
100 -> "is an excellent condition!"
in 90..99 -> "has a few scratches."
in 75..89 -> if (isBlessed) {
"has some minor wounds but is healing quite quickly"
} else {
"has some minor wounds"
}
in 15..74 -> "looks pretty hurt"
else -> "is in awful condition!"
}
}
I forgot the towns.txt so here it is (not that it matters much)
Neversummer
Abelhaven
Phandoril
Tampa
Sanorith
Trell
Zan'tro
Hermi Hermi
Curlthistle Forest
When something like this happens, it's usually due to bad ordering of initialization.
The initialization of the Player class goes this way:
the name property has its backing field initialized with the _name value
the init block is run, and tries to access name
the getter of name tries to read the hometown property, but fails because hometown is still not initialized
...if things had gone right, the hometown property would be initialized now with the lazy delegate
So basically you're trying to access hometown before the lazy delegate is configured.
If you move hometown's declaration above the init block, you should be fine.
You can see the fix in action on the playground
In the new project that I'm currently working on I have no RxJava dependency at all, because until now I didn't need that - coroutines solve threading problem pretty gracefully.
At this point I stumbled upon on a requirement to have a BehaviorSubject-alike behavior, where one can subscribe to a stream of data and receive the latest value upon subscription. As I've learned, Channels provide very similar behavior in Kotlin, so I decided to give them a try.
From this article I've learned, that ConflatedBroadcastChannel is the type of channel that mimics BehaviorSubject, so I declared following:
class ChannelSender {
val channel = ConflatedBroadcastChannel<String>()
fun sendToChannel(someString: String) {
GlobalScope.launch(Dispatchers.Main) { channel.send(someString) }
}
}
For listening to the channel I do this:
class ChannelListener(val channelSender: ChannelSender) {
fun listenToChannel() {
channelSender.channel.consumeEach { someString ->
if (someString == "A") foo.perform()
else bar.perform()
}
}
}
This works as expected, but at this point I'm having difficulties understanding how to unit test ChannelListener.
I've tried to find something related here, but none of example-channel-**.kt classes were helpful.
Any help, suggestion or correction related to my incorrect assumptions is appreciated. Thanks.
With the help of Alexey I could manage to end up having following code, which answers the question:
class ChannelListenerTest {
private val val channelSender: ChannelSender = mock()
private val sut = ChannelListener(channelSender)
private val broadcastChannel = ConflatedBroadcastChannel<String>()
private val timeLimit = 1_000L
private val endMarker = "end"
#Test
fun `some description here`() = runBlocking {
whenever(channelSender.channel).thenReturn(broadcastChannel)
val sender = launch(Dispatchers.Default) {
broadcastChannel.offer("A")
yield()
}
val receiver = launch(Dispatchers.Default) {
while (isActive) {
val i = waitForEvent()
if (i == endMarker) break
yield()
}
}
try {
withTimeout(timeLimit) {
sut.listenToChannel()
sender.join()
broadcastChannel.offer(endMarker) // last event to signal receivers termination
receiver.join()
}
verify(foo).perform()
} catch (e: CancellationException) {
println("Test timed out $e")
}
}
private suspend fun waitForEvent(): String =
with(broadcastChannel.openSubscription()) {
val value = receive()
cancel()
value
}
}
I have a code like this:
enum class Player { PLAYER, COMPUTER }
interface BoardCell {
val x: Int
val y: Int
var player: Player?
}
data class Cell(val x: Int, val y: Int, var player: Player?, var value: Int)
data class BoardCellClass(override val x: Int, override val y: Int, override var player: Player?) : BoardCell
data class Request(val board: MutableList<MutableList<BoardCellClass>>? = null, val occupied: MutableList<BoardCellClass>? = null)
class AI(board: MutableList<MutableList<BoardCell>>, private var occupied: MutableList<BoardCell>) {
private var board: MutableList<MutableList<Cell>> = board.map { it.map { Cell(it.x, it.y, it.player, 0) } .toMutableList() } .toMutableList()
}
// in main
val request = call.receive<Request>()
if (request.board == null || request.occupied == null) {
// respond with 403
} else {
val ai = AI(request.board, request.occupied) // Kotlin: Type mismatch: inferred type is MutableList<MutableList<BoardCellClass>>? but MutableList<MutableList<BoardCell>> was expected
// Kotlin: Type mismatch: inferred type is MutableList<BoardCellClass>? but MutableList<BoardCell> was expected
}
But it errors with what is in the comment in the bottom. What am I doing wrong? Clearly, there is an if statement, that catches nullity, so it shouldn't be of type MutableList<MutableList<BoardCellClass>>?, but MutableList<MutableList<BoardCellClass>>, no?
Also, MutableList<MutableList<BoardCellClass>> is compatible with MutableList<MutableList<BoardCell>>, because it implements that interface, right?
MutableList> is compatible with
MutableList>, because it implements that
interface, right?
No. In your case you can use out keyword
class AI(board: MutableList<MutableList<BoardCell>>, private var occupied: MutableList<out BoardCell>) {
private var board: MutableList<MutableList<Cell>> = board.map { it.map { Cell(it.x, it.y, it.player, 0) } .toMutableList() } .toMutableList()
}
Clearly, there is an if statement, that catches nullity, so it
shouldn't be of type
You can write this code in if (not else) part to compiler understand you in wright way
if (request.board != null && request.occupied != null){
val ai = AI(request.board, request.occupied)
} else {
// respond with 403
}
Also, MutableList<MutableList<BoardCellClass>> is compatible with MutableList<MutableList<BoardCell>>, because it implements that interface, right?
No, it isn't. That's an issue of variance. Searching for "Kotlin variance" will give you many explanations, but the simplest way to see why MutableList<BoardCellClass> isn't a subtype of MutableList<BoardCell> is
val list: MutableList<BoardCellClass> = ...
val list1: MutableList<BoardCell> = list // illegal in actual Kotlin!
list1.add(object : BoardCell { ... }) // would add a BoardCell to a MutableList<BoardCellClass>
Then the same logic can be lifted up a level to see that MutableList<MutableList<BoardCellClass>> isn't a subtype of MutableList<MutableList<BoardCell>>.
Clearly, there is an if statement, that catches nullity, so it shouldn't be of type MutableList<MutableList<BoardCellClass>>?, but MutableList<MutableList<BoardCellClass>>, no?
That's not quite how Kotlin smart casts work (though the difference isn't relevant most of the time). It's still of type MutableList<MutableList<BoardCellClass>>?, but if it's used where the required type is MutableList<MutableList<BoardCellClass>>, it's automatically cast. Here the required type is MutableList<MutableList<BoardCell>> instead, and so there's no cast inserted and the nullable type shows up in the error message.