Compare commits

19 Commits

Author SHA1 Message Date
69c22322c8 Merge pull request '삭제 버튼 위치 조정 / 프리셋 버튼 관련 수정' (#41) from feature/ISSUE-34 into develop
Reviewed-on: #41
2026-03-03 11:08:58 +00:00
areumwoo
28f0430e1d feat: 빠진 이미지 추가 2026-03-03 19:09:54 +09:00
fac756eacb Merge pull request 'Guide beam packet 오전송 수정' (#47) from feature/ISSUE-5 into develop
Reviewed-on: #47
2026-03-03 08:55:48 +00:00
StevenBuzzi
d0b8a78847 Guide beam packet 오전송 수정
1. Engineer; Min/Max에 표기된 값을 전송하도록 수정
2. Config; Guide beam Step에 맞게 전송하도록 수정
2026-03-03 17:40:25 +09:00
a817a5f14f Merge pull request 'LifeTimeView Count/Hour 분리' (#39) from feature/ISSUE-38 into develop
Reviewed-on: #39
2026-03-03 06:23:35 +00:00
StevenBuzzi
2099f4d178 1. Modified the range of applied item
2. Adjust padding @ Life Time, Temp
2026-03-03 14:44:09 +09:00
f7fca4219a Merge pull request 'Gradient 슬라이더 계산 로직 수정' (#40) from feature/ISSUE-20 into develop
Reviewed-on: #40
2026-03-03 05:01:57 +00:00
9da71a962b Merge pull request 'Enforce standby when lamp lifetime exceeded' (#37) from feature/ISSUE-11 into develop
Reviewed-on: #37
2026-03-03 04:36:08 +00:00
d8bd898442 Merge pull request '우선순위 중복 시 기존 항목을 NONE으로 초기화하도록 수정' (#36) from feature/ISSUE-10 into develop
Reviewed-on: #36
2026-03-03 03:36:58 +00:00
a97d5e4354 Merge pull request 'InfoScreen 체크박스 상태 저장' (#33) from feature/ISSUE-7 into develop
Reviewed-on: #33
2026-03-03 02:04:27 +00:00
areumwoo
6cba0674cf 프리셋 저장 버튼/로드 버튼 크기 조정 2026-03-02 16:57:10 +09:00
areumwoo
b87d911cea 삭제 버튼 위치 및 여백 조정 2026-03-02 16:41:38 +09:00
areumwoo
1fc9640e38 Gradient 슬라이더 계산 로직 수정 2026-03-02 16:27:04 +09:00
areumwoo
00f2ec73d5 LifeTimeView Count/Hour 분리 2026-03-02 15:22:47 +09:00
areumwoo
f5d7187c0f Enforce standby when lamp lifetime exceeded 2026-03-02 14:54:34 +09:00
areumwoo
3c9a07bf9a feat: 우선순위 중복 시 기존 항목을 NONE으로 초기화하도록 수정 2026-03-02 13:08:44 +09:00
areumwoo
d6efa9bcaa InfoScreen 체크박스 상태 저장 2026-03-02 12:45:05 +09:00
woo
53cd5976bb Merge pull request 'feat: #72 Read/Write 표시 위치 수정 미적용' (#31) from feature/ISSUE-6 into develop
Reviewed-on: #31
Reviewed-by: steven <jbj213@naver.com>
2026-03-02 01:59:50 +00:00
areumwoo
841d9221a6 feat: #72 Read/Write 표시 위치 수정 미적용 2026-03-02 10:50:44 +09:00
15 changed files with 255 additions and 115 deletions

View File

@@ -88,4 +88,7 @@ interface Preference {
suspend fun getPowerSupplySerialListFromPreference(): Flow<List<String>> suspend fun getPowerSupplySerialListFromPreference(): Flow<List<String>>
///// /////
suspend fun saveInfoChartLineStates(states: Map<String, Boolean>)
suspend fun getInfoChartLineStates(): Flow<Map<String, Boolean>>
} }

View File

@@ -68,6 +68,9 @@ class PreferenceRepository(private val context: Context) : Preference {
val PRODUCT_SERIAL = stringPreferencesKey("PRODUCT_SERIAL") val PRODUCT_SERIAL = stringPreferencesKey("PRODUCT_SERIAL")
val LASER_HAND_SERIAL = stringPreferencesKey("LASER_HAND_SERIAL") val LASER_HAND_SERIAL = stringPreferencesKey("LASER_HAND_SERIAL")
val POWER_SUPPLY_SERIAL = stringPreferencesKey("POWER_SUPPLY_SERIAL") val POWER_SUPPLY_SERIAL = stringPreferencesKey("POWER_SUPPLY_SERIAL")
// InfoScreen Chart Checkboxes
val INFO_CHART_LINE_STATES = stringPreferencesKey("INFO_CHART_LINE_STATES")
} }
override suspend fun clearAllPreferences() { override suspend fun clearAllPreferences() {
@@ -559,4 +562,31 @@ class PreferenceRepository(private val context: Context) : Preference {
emit(listOf("B", "U", "O", "C", "L", "D")) emit(listOf("B", "U", "O", "C", "L", "D"))
} }
} }
override suspend fun saveInfoChartLineStates(states: Map<String, Boolean>) {
try {
val stateJson = gson.toJson(states)
context.datastore.edit { preferences ->
preferences[INFO_CHART_LINE_STATES] = stateJson
}
} catch (e: Exception) {
Timber.e(e, "Failed to serialize INFO_CHART_LINE_STATES to JSON.")
}
}
override suspend fun getInfoChartLineStates(): Flow<Map<String, Boolean>> {
return context.datastore.data.map { preferences ->
preferences[INFO_CHART_LINE_STATES]
}.distinctUntilChanged().map { jsonString ->
if (!jsonString.isNullOrBlank()) {
val type = object : TypeToken<Map<String, Boolean>>() {}.type
gson.fromJson<Map<String, Boolean>>(jsonString, type) ?: emptyMap()
} else {
emptyMap()
}
}.catch { e ->
Timber.e(e, "Failed to get or parse INFO_CHART_LINE_STATES. Emitting default.")
emit(emptyMap())
}
}
} }

View File

@@ -439,15 +439,15 @@ fun ConfigScreen(
mainViewModel.saveGuideBeamToPreference() mainViewModel.saveGuideBeamToPreference()
} }
/* // Guide Beam step mapping (0~10):
val value = when(guideBeam.toInt()) { // 0 -> fixed 0
0 -> 0 // 1~10 -> min~max range in 10 steps (10 -> max)
1 -> guideBeamMin val step = guideBeam.toInt().coerceIn(0, 10)
10 -> guideBeamMax val value = if (step == 0) {
else -> (guideBeamMin + (guideBeam.toInt() - 1) * ((guideBeamMax - guideBeamMin) / 9)) 0
} else {
guideBeamMin + ((step-1) * (guideBeamMax - guideBeamMin) / 9)
} }
*/
val value = (guideBeamMin + (guideBeam.toInt() - 1) * ((guideBeamMax - guideBeamMin) / 9))
Timber.d("guideBeam: $value, guideBeamMax: $guideBeamMax, guideBeamMin: $guideBeamMin") Timber.d("guideBeam: $value, guideBeamMax: $guideBeamMax, guideBeamMin: $guideBeamMin")
mainViewModel.txPacket(READ_WRITE.WRITE, CMD.GUIDE_BEAM, GuideBeam(value = value)) mainViewModel.txPacket(READ_WRITE.WRITE, CMD.GUIDE_BEAM, GuideBeam(value = value))

View File

@@ -31,7 +31,7 @@ fun HourItemView(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
hour: Int = 0, hour: Int = 0,
onClick: () -> Unit = {}, onClick: () -> Unit = {},
title:String = "" title: String = ""
) { ) {
Row(modifier = modifier, Row(modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,

View File

@@ -32,11 +32,11 @@ fun LifeTimeView(
) { ) {
Column(modifier = Modifier Column(modifier = Modifier
//.noRippleClickable(onClick = onClick) //.noRippleClickable(onClick = onClick)
.size(388.px.dp, 276.px.dp) .size(388.px.dp, 258.px.dp)
.clip(RoundedCornerShape(12.px.dp)) .clip(RoundedCornerShape(12.px.dp))
.border(width = 1.px.dp, color = Color(209, 209, 209), shape = RoundedCornerShape(10.px.dp)) .border(width = 1.px.dp, color = Color(209, 209, 209), shape = RoundedCornerShape(10.px.dp))
.background(Color.White) .background(Color.White)
.padding(16.px.dp), .padding(3.px.dp, 16.px.dp),
verticalArrangement = Arrangement.SpaceEvenly, verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
@@ -55,11 +55,11 @@ fun LifeTimeView(
), ),
) )
Spacer(modifier = Modifier.height(10.px.dp)) Spacer(modifier = Modifier.height(2.px.dp))
// Temp 0..7 // Temp 0..7
for (i in 0..lifeTimeTypes.size -1) { for (i in 0..lifeTimeTypes.size -1) {
val hour = when (i) { val value = when (i) {
0 -> lifeTime.lamp 0 -> lifeTime.lamp
1 -> lifeTime.hp5x5 1 -> lifeTime.hp5x5
2 -> lifeTime.hp7x7 2 -> lifeTime.hp7x7
@@ -70,22 +70,35 @@ fun LifeTimeView(
7 -> lifeTime.water 7 -> lifeTime.water
else -> 0 else -> 0
} }
HourItemView( val modifier = Modifier
modifier = Modifier
.fillMaxSize() .fillMaxSize()
.weight(1f) .weight(1f)
.padding( .padding(
start = 20.px.dp, start = 20.px.dp,
end = 20.px.dp, end = 20.px.dp,
//bottom = 10.px.dp //bottom = 10.px.dp
), )
title = lifeTimeTypes[i], val title = lifeTimeTypes[i]
hour = hour, val onItemClick = {
onClick = { Timber.d("onClick > Temp $i ($title)")
Timber.d("onClick > Temp $i (${lifeTimeTypes[i]})")
onClick.invoke(i) onClick.invoke(i)
} }
if (i <= 5) {
CountItemView(
modifier = modifier,
title = title,
count = value,
onClick = onItemClick
) )
} else {
HourItemView(
modifier = modifier,
title = title,
hour = value,
onClick = onItemClick
)
}
if (i < lifeTimeTypes.size -1) { if (i < lifeTimeTypes.size -1) {
HorizontalDivider( HorizontalDivider(

View File

@@ -5,6 +5,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -13,15 +14,19 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.laseroptek.raman.const.temperatureTypes import com.laseroptek.raman.const.temperatureTypes
import com.laseroptek.raman.data.model.serial.Temperature import com.laseroptek.raman.data.model.serial.Temperature
import com.laseroptek.raman.ui.theme.RobotoTypography
import com.laseroptek.raman.utils.ext.px import com.laseroptek.raman.utils.ext.px
import timber.log.Timber import timber.log.Timber
@@ -37,7 +42,7 @@ fun TemperatureView(
.clip(RoundedCornerShape(12.px.dp)) .clip(RoundedCornerShape(12.px.dp))
.border(width = 1.px.dp, color = Color(209, 209, 209), shape = RoundedCornerShape(10.px.dp)) .border(width = 1.px.dp, color = Color(209, 209, 209), shape = RoundedCornerShape(10.px.dp))
.background(Color.White) .background(Color.White)
.padding(16.px.dp), .padding(3.px.dp, 16.px.dp),
verticalArrangement = Arrangement.SpaceEvenly, verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
@@ -56,7 +61,33 @@ fun TemperatureView(
), ),
) )
Spacer(modifier = Modifier.height(10.px.dp)) Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 20.px.dp, end = 20.px.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.weight(1.5f))
Text(
modifier = Modifier.weight(1f),
text = "Read",
style = RobotoTypography.labelMedium,
fontSize = 12.px.sp,
color = Color.Black,
textAlign = TextAlign.End
)
Text(
modifier = Modifier.weight(1f),
text = "Write",
style = RobotoTypography.labelMedium,
fontSize = 12.px.sp,
color = Color.Black,
textAlign = TextAlign.End
)
Spacer(modifier = Modifier.weight(1f))
}
Spacer(modifier = Modifier.height(2.px.dp))
// Tempearture // Tempearture
for (i in 0..temperatureTypes.size -1) { for (i in 0..temperatureTypes.size -1) {

View File

@@ -75,25 +75,6 @@ fun TwoCountItemView(
) )
} }
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.height(30.px.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.Start
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "Read",
style = RobotoTypography.bodyMedium,
fontWeight = FontWeight.Normal,
fontSize = 12.px.sp,
color = Color.Black,
textAlign = TextAlign.End
)
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -113,25 +94,6 @@ fun TwoCountItemView(
) )
} }
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.height(30.px.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.Start
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "Write",
style = RobotoTypography.bodyMedium,
fontWeight = FontWeight.Normal,
fontSize = 12.px.sp,
color = Color.Black,
textAlign = TextAlign.End
)
}
Column( Column(
modifier = Modifier modifier = Modifier
//.noRippleClickable(onClick = onDeviceOpTimeClick) //.noRippleClickable(onClick = onDeviceOpTimeClick)

View File

@@ -35,6 +35,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.laseroptek.raman.const.LaserParameter import com.laseroptek.raman.const.LaserParameter
import com.laseroptek.raman.const.LASER_STATUS
import com.laseroptek.raman.const.LaserStatusType import com.laseroptek.raman.const.LaserStatusType
import com.laseroptek.raman.const.MAX_LASER_COUNT import com.laseroptek.raman.const.MAX_LASER_COUNT
import com.laseroptek.raman.const.PresetButtonType import com.laseroptek.raman.const.PresetButtonType
@@ -92,10 +93,18 @@ fun HomeScreen(
val presetList by mainViewModel.presetList.collectAsState() val presetList by mainViewModel.presetList.collectAsState()
LaunchedEffect(Unit) { LaunchedEffect(lampCount, lifeTime.lamp, laserStatus.laserStatus) {
Timber.d("LaunchedEffect - HomeScreen") Timber.d("LaunchedEffect - HomeScreen")
focusManager.clearFocus(force = true) // Hide the keyboard focusManager.clearFocus(force = true) // Hide the keyboard
// Ensure the system returns to StandBy when lamp thresholds are exceeded
val lampLifetimeLimit = lifeTime.lamp
val reachedLifetimeLimit = lampLifetimeLimit > 0 && lampCount >= lampLifetimeLimit
if (reachedLifetimeLimit && laserStatus.laserStatus != LASER_STATUS.STAND_BY) {
Timber.d("HomeScreen load - forcing StandBy state due to lamp count limit")
mainViewModel.txLaserStatusEntry(LASER_STATUS.STAND_BY)
}
Timber.d("Attempted to hide keyboard on EngineerScreen launch") Timber.d("Attempted to hide keyboard on EngineerScreen launch")
} }
@@ -482,6 +491,18 @@ fun HomeScreen(
return@StandByButton return@StandByButton
} }
val lampLifetimeLimit = lifeTime.lamp
if (lampLifetimeLimit > 0 && lampCount >= lampLifetimeLimit) {
Toast.makeText(
context,
"Lamp lifetime limit reached",
Toast.LENGTH_SHORT
).show()
return@StandByButton
}
val hpCount = mainViewModel.getHPCount() val hpCount = mainViewModel.getHPCount()
if (hpCount < 1) { if (hpCount < 1) {
Toast.makeText( Toast.makeText(

View File

@@ -334,7 +334,7 @@ fun DcdSettingPopup(
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.size(40.px.dp, 210.px.dp), .size(40.px.dp, 210.px.dp),
chargeRate = gasChargeRate.toInt() chargeRate = gasChargeRate
) )
// Icon // Icon

View File

@@ -28,14 +28,26 @@ import com.laseroptek.raman.ui.screens.main.MainViewModel
import com.laseroptek.raman.utils.DefaultDispatcherProvider import com.laseroptek.raman.utils.DefaultDispatcherProvider
import com.laseroptek.raman.utils.ext.px import com.laseroptek.raman.utils.ext.px
import timber.log.Timber import timber.log.Timber
import kotlin.math.abs
import kotlin.math.ceil
@Composable @Composable
fun GradientSlider( fun GradientSlider(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
chargeRate: Int = 0, // Value from (0 .. 100) chargeRate: Float = 0f,
) { ) {
val chargeIndex = ((chargeRate.coerceIn(0, 100) + 4) / 5).toInt() // 0..19 val normalizedRate = chargeRate.coerceIn(0f, 100f)
val bucket = normalizedRate / 5f
val remainder = normalizedRate % 5f
val isExactMultiple = abs(remainder) < 0.0001f
val chargeIndex = when {
normalizedRate == 0f -> -1
isExactMultiple -> (bucket - 1f).toInt().coerceAtLeast(-1)
else -> (ceil(bucket.toDouble()).toInt() - 1)
}.coerceIn(-1, 19)
Timber.d("chargeRate: $chargeRate, chargeIndex: $chargeIndex") Timber.d("chargeRate: $chargeRate, chargeIndex: $chargeIndex")
Box( Box(
@@ -103,6 +115,6 @@ fun GradientSliderPreview(
//mainViewModel = mainViewModel //mainViewModel = mainViewModel
modifier = Modifier modifier = Modifier
.size(40.px.dp, 210.px.dp), .size(40.px.dp, 210.px.dp),
chargeRate = 20 chargeRate = 20f
) )
} }

View File

@@ -108,15 +108,15 @@ fun PresetIconButton(
Image( Image(
painter = painterResource(id = painter = painterResource(id =
if (type == PresetButtonType.SAVE) { if (type == PresetButtonType.SAVE) {
R.drawable.ic_preset_save R.drawable.ic_preset_save2
} else { } else {
R.drawable.ic_preset_load R.drawable.ic_preset_load
} }
), ),
contentDescription = "", contentDescription = "",
modifier = Modifier modifier = Modifier
.size(20.px.dp), .size(30.px.dp),
contentScale = ContentScale.Crop contentScale = ContentScale.Fit
) )
} }
} }

View File

@@ -552,12 +552,12 @@ fun PresetLoadPopup(
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Spacer(Modifier.weight(1f))
if (isEditMode) { if (isEditMode) {
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
// Edit Mode // Edit Mode
Spacer(modifier = Modifier.width(10.px.dp))
// Preset Delete (Delete confirm popup) // Preset Delete (Delete confirm popup)
Box( Box(
modifier = Modifier modifier = Modifier
@@ -578,7 +578,7 @@ fun PresetLoadPopup(
) )
} }
Spacer(Modifier.width(10.px.dp)) Spacer(Modifier.weight(1f))
// Preset Cancel (Reload selected item from mainViewModel) // Preset Cancel (Reload selected item from mainViewModel)
Box( Box(
@@ -646,7 +646,7 @@ fun PresetLoadPopup(
Timber.d("onClick - Confirm Save") Timber.d("onClick - Confirm Save")
// Check empty names and conflict priority exist in the preset viewmodel's presetList // Check empty names and conflict priority exist in the preset viewmodel's presetList
val listToValidate = presetViewModel.presetList.value var listToValidate = presetViewModel.presetList.value
// Check for any presets with an empty name // Check for any presets with an empty name
val hasEmptyName = val hasEmptyName =
@@ -678,18 +678,32 @@ fun PresetLoadPopup(
} }
// Check for duplicate priorities (ignoring priority 0) // Check for duplicate priorities (ignoring priority 0)
val priorityConflicts = listToValidate val duplicatePriorityGroups = listToValidate
.filter { it.priority > 0 } // Only consider prioritized items .filter { it.priority > 0 }
.groupBy { it.priority } // Group them by priority .groupBy { it.priority }
.any { it.value.size > 1 } // Check if any group is larger than 1 .filter { it.value.size > 1 }
if (priorityConflicts) { if (duplicatePriorityGroups.isNotEmpty()) {
Toast.makeText( val resolvedList = listToValidate.map { it.copy() }.toMutableList()
context, val selectedPreset = resolvedList.getOrNull(selectedPresetIndex)
"There are duplicate priorities. Please ensure each priority is unique.",
Toast.LENGTH_LONG duplicatePriorityGroups.forEach { (priorityValue, presets) ->
).show() val keeperId = presets
return@noRippleClickable // Stop the process .firstOrNull { preset ->
selectedPreset != null && preset.id == selectedPreset.id
}
?.id
?: presets.first().id
resolvedList.forEachIndexed { index, preset ->
if (preset.priority == priorityValue && preset.id != keeperId) {
resolvedList[index] = preset.copy(priority = 0)
}
}
}
presetViewModel.setPresetList(resolvedList)
listToValidate = resolvedList
} }
Timber.d("Validation successful. Saving list to MainViewModel.") Timber.d("Validation successful. Saving list to MainViewModel.")
@@ -729,7 +743,11 @@ fun PresetLoadPopup(
contentScale = ContentScale.Crop contentScale = ContentScale.Crop
) )
} }
Spacer(modifier = Modifier.width(10.px.dp))
} else { } else {
Spacer(Modifier.weight(1f))
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
// Select Mode - hide Keyboard // Select Mode - hide Keyboard
focusManager.clearFocus(force = true) // Hide the keyboard focusManager.clearFocus(force = true) // Hide the keyboard
@@ -790,6 +808,8 @@ fun PresetLoadPopup(
contentScale = ContentScale.Crop contentScale = ContentScale.Crop
) )
} }
Spacer(modifier = Modifier.width(10.px.dp))
} }
} }

View File

@@ -1,10 +1,14 @@
package com.laseroptek.raman.ui.screens.info package com.laseroptek.raman.ui.screens.info
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import com.laseroptek.raman.repository.PreferenceRepository
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@@ -25,10 +29,13 @@ data class ChartUiState(
val chamber2State: Boolean = true, val chamber2State: Boolean = true,
val basePlateState: Boolean = true, val basePlateState: Boolean = true,
val waterState: Boolean = true, val waterState: Boolean = true,
) ) {
companion object
}
@HiltViewModel @HiltViewModel
class InfoViewModel @Inject constructor( class InfoViewModel @Inject constructor(
private val preferenceRepository: PreferenceRepository,
) : ViewModel() { ) : ViewModel() {
// This is the single source of truth for the checkbox states. // This is the single source of truth for the checkbox states.
@@ -39,7 +46,7 @@ class InfoViewModel @Inject constructor(
fun toggleLine(line: ChartLine) { fun toggleLine(line: ChartLine) {
// .update is a thread-safe way to update the state. // .update is a thread-safe way to update the state.
_chartUiState.update { currentState -> _chartUiState.update { currentState ->
when (line) { val updatedState = when (line) {
ChartLine.INT_TEMP -> currentState.copy(intTempState = !currentState.intTempState) ChartLine.INT_TEMP -> currentState.copy(intTempState = !currentState.intTempState)
ChartLine.EXT_TEMP -> currentState.copy(extTempState = !currentState.extTempState) ChartLine.EXT_TEMP -> currentState.copy(extTempState = !currentState.extTempState)
ChartLine.INT_HUMIDITY -> currentState.copy(intHumidityState = !currentState.intHumidityState) ChartLine.INT_HUMIDITY -> currentState.copy(intHumidityState = !currentState.intHumidityState)
@@ -50,10 +57,51 @@ class InfoViewModel @Inject constructor(
ChartLine.BASE_PLATE -> currentState.copy(basePlateState = !currentState.basePlateState) ChartLine.BASE_PLATE -> currentState.copy(basePlateState = !currentState.basePlateState)
ChartLine.WATER -> currentState.copy(waterState = !currentState.waterState) ChartLine.WATER -> currentState.copy(waterState = !currentState.waterState)
} }
persistChartUiState(updatedState)
updatedState
} }
} }
init { init {
Timber.d("InfoViewModel init") Timber.d("InfoViewModel init")
viewModelScope.launch {
preferenceRepository.getInfoChartLineStates().collectLatest { savedStates ->
if (savedStates.isEmpty()) return@collectLatest
_chartUiState.update { ChartUiState.fromPreference(savedStates) }
}
}
}
private fun persistChartUiState(state: ChartUiState) {
viewModelScope.launch {
preferenceRepository.saveInfoChartLineStates(state.toPreferenceMap())
}
} }
} }
private fun ChartUiState.toPreferenceMap(): Map<String, Boolean> = mapOf(
ChartLine.INT_TEMP.name to intTempState,
ChartLine.EXT_TEMP.name to extTempState,
ChartLine.INT_HUMIDITY.name to intHumidityState,
ChartLine.EXT_HUMIDITY.name to extHumidityState,
ChartLine.KTP.name to ktpState,
ChartLine.CHAMBER1.name to chamber1State,
ChartLine.CHAMBER2.name to chamber2State,
ChartLine.BASE_PLATE.name to basePlateState,
ChartLine.WATER.name to waterState,
)
private fun ChartUiState.Companion.fromPreference(savedStates: Map<String, Boolean>): ChartUiState {
val defaults = ChartUiState()
return ChartUiState(
intTempState = savedStates[ChartLine.INT_TEMP.name] ?: defaults.intTempState,
extTempState = savedStates[ChartLine.EXT_TEMP.name] ?: defaults.extTempState,
intHumidityState = savedStates[ChartLine.INT_HUMIDITY.name] ?: defaults.intHumidityState,
extHumidityState = savedStates[ChartLine.EXT_HUMIDITY.name] ?: defaults.extHumidityState,
ktpState = savedStates[ChartLine.KTP.name] ?: defaults.ktpState,
chamber1State = savedStates[ChartLine.CHAMBER1.name] ?: defaults.chamber1State,
chamber2State = savedStates[ChartLine.CHAMBER2.name] ?: defaults.chamber2State,
basePlateState = savedStates[ChartLine.BASE_PLATE.name] ?: defaults.basePlateState,
waterState = savedStates[ChartLine.WATER.name] ?: defaults.waterState,
)
}

View File

@@ -583,22 +583,22 @@ class MainViewModel @Inject constructor(
saveGuideBeamMinToPreference() saveGuideBeamMinToPreference()
saveGuideBeamMaxToPreference() saveGuideBeamMaxToPreference()
// After updating the state, send the packet // Engineer 화면에서는 Min/Max 버튼에 따라 표시된 Min 또는 Max 값을 그대로 송신
val newMin = guideBeamMin.value // get the potentially updated value val newMin = guideBeamMin.value
val newMax = guideBeamMax.value // get the potentially updated value val newMax = guideBeamMax.value
val guideBeam = guideBeam.value.toInt() val value = when (state) {
MinMaxUpDownState.MinDown,
MinMaxUpDownState.MinUp,
MinMaxUpDownState.MinLongDown,
MinMaxUpDownState.MinLongUp -> newMin
/* MinMaxUpDownState.MaxDown,
val value = when(guideBeamValue) { MinMaxUpDownState.MaxUp,
0 -> 0 MinMaxUpDownState.MaxLongDown,
1 -> newMin MinMaxUpDownState.MaxLongUp -> newMax
10 -> newMax
else -> (newMin + (guideBeamValue - 1) * ((newMax - newMin) / 9))
} }
*/
val value = (newMin + (guideBeam - 1) * ((newMax - newMin) / 9))
Timber.d("guideBeam: $value, guideBeam: $guideBeam, guideBeamMax: $newMax, guideBeamMin: $newMin") Timber.d("Engineer guideBeam tx value: $value, guideBeamMax: $newMax, guideBeamMin: $newMin")
txPacket(READ_WRITE.WRITE, CMD.GUIDE_BEAM, GuideBeam(value = value)) txPacket(READ_WRITE.WRITE, CMD.GUIDE_BEAM, GuideBeam(value = value))
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB