updated the grain describers to use the new enum in the pond for the moisture calculation, changed the extract moisture function to account for custom grain types, and if there is no equation set it will return the humidity, updated instances where the bin uses that function to pass in the grain settings in its inventory

This commit is contained in:
csawatzky 2025-12-12 15:17:37 -06:00
parent bdddcfc103
commit d6c670fb78
16 changed files with 596 additions and 140 deletions

View file

@ -0,0 +1,286 @@
import { Box, Button, MenuItem, TextField, Typography } from "@mui/material"
import SearchSelect, { Option } from "common/SearchSelect"
import { cloneDeep } from "lodash"
import { pond } from "protobuf-ts/pond"
import { useGlobalState } from "providers"
import { useGrainAPI } from "providers/pond/grainAPI"
import React, { useEffect, useState } from "react"
interface Props {
grainSettings?: pond.GrainSettings
onGrainSettingsChange: (settings: pond.GrainSettings) => void
}
export default function CustomGrainForm(props: Props) {
const {grainSettings, onGrainSettingsChange} = props
const grainAPI = useGrainAPI()
const [{as}] = useGlobalState()
const [name, setName] = useState("")
const [group, setGroup] = useState("")
const [equation, setEquation] = useState<pond.MoistureEquation>(pond.MoistureEquation.MOISTURE_EQUATION_NONE)
const [constantA, setConstantA] = useState("0")
const [constantB, setConstantB] = useState("0")
const [constantC, setConstantC] = useState("0")
const [kgPerBushel, setKgPerBushel] = useState("0")
const [bushelsPerTonne, setBushelsPerTonne] = useState("0")
const [newGrainSettings, setNewGrainSettings] = useState(pond.GrainSettings.create())
const [grainMap, setGrainMap] = useState<Map<string, pond.GrainObject>>(new Map())
const [grainOptions, setGrainOptions] = useState<Option[]>([])
const [isNew, setIsNew] = useState(false)
const [selectedOption, setSelectedOption] = useState<Option | null>(null)
useEffect(()=>{
if(grainSettings){
setNewGrainSettings(grainSettings)
setName(grainSettings.name)
setGroup(grainSettings.group)
setEquation(grainSettings.equation)
setConstantA(grainSettings.a.toString())
setConstantB(grainSettings.b.toString())
setConstantC(grainSettings.c.toString())
setKgPerBushel(grainSettings.kgPerBushel.toString())
setBushelsPerTonne(grainSettings.bushelsPerTonne.toString())
}
},[grainSettings])
useEffect(()=>{
grainAPI.listGrains(0, 0, "asc", "name", undefined, undefined, undefined, as).then(resp => {
if (resp.data.grains){
let map: Map<string, pond.GrainObject> = new Map()
let options: Option[] = []
resp.data.grains.forEach(grain => {
map.set(grain.key, grain)
options.push({
label: grain.name,
value: grain.key,
group: grain.settings?.group,
})
})
setGrainOptions(options)
setGrainMap(map)
}
})
},[grainAPI])
const invalid = () => {
if (name === "") return true
if (group === "") return true
if (isNaN(parseFloat(constantA))) return true
if (isNaN(parseFloat(constantB))) return true
if (isNaN(parseFloat(constantA))) return true
if (isNaN(parseFloat(kgPerBushel))) return true
if (isNaN(parseFloat(bushelsPerTonne))) return true
return false
}
const saveGrain = () => {
// console.log(newGrainSettings)
grainAPI.addGrain(newGrainSettings, as).then(resp => {
console.log("grain added")
}).catch(err => {
console.log("err")
})
}
const settingsChanged = (newSettings: pond.GrainSettings) => {
setSelectedOption(null)
setNewGrainSettings(newSettings)
onGrainSettingsChange(newSettings)
}
return (
<React.Fragment>
<Box sx={{marginBottom: 2}}>
<SearchSelect
label="My Grains"
selected={selectedOption}
changeSelection={option => {
setIsNew(false)
setSelectedOption(option)
//when an option is selected set all of the state variables controlling the form entries
let grain = grainMap.get(option?.value)
if(grain && grain.settings){
//set the form values
setName(grain.name)
setGroup(grain.settings.group)
setEquation(grain.settings.equation)
setConstantA(grain.settings.a.toString())
setConstantB(grain.settings.b.toString())
setConstantC(grain.settings.c.toString())
setKgPerBushel(grain.settings.kgPerBushel.toString())
setBushelsPerTonne(grain.settings.bushelsPerTonne.toString())
//the the new grain settings object
setNewGrainSettings(grain.settings)
//pass that settings object back up
settingsChanged(grain.settings)
}
}}
group
options={grainOptions}
/>
</Box>
<Typography sx={{marginBottom: 1}}>Custom Grain Properties</Typography>
<TextField
sx={{marginBottom: 2}}
fullWidth
label="Name*"
value={name}
onChange={(e) => {
let name = e.target.value
setName(name)
setIsNew(true)
let settings = cloneDeep(newGrainSettings)
settings.name = name
settingsChanged(settings)
}}/>
<TextField
sx={{marginBottom: 2}}
fullWidth
label="Group*"
value={group}
onChange={(e) => {
let group = e.target.value
setGroup(group)
setIsNew(true)
let settings = cloneDeep(newGrainSettings)
settings.group = group
settingsChanged(settings)
}}/>
<TextField
sx={{marginBottom: 2}}
fullWidth
value={equation}
helperText={"Not selecting an EMC equation to use will return the humidity instead of EMC"}
label="EMC Equation"
onChange={(e) => {
let enumVal = parseFloat(e.target.value)
setEquation(enumVal)
setIsNew(true)
let settings = cloneDeep(newGrainSettings)
settings.equation = enumVal
settingsChanged(settings)
}}
select>
<MenuItem key={pond.MoistureEquation.MOISTURE_EQUATION_NONE} value={pond.MoistureEquation.MOISTURE_EQUATION_NONE}>
None
</MenuItem>
<MenuItem key={pond.MoistureEquation.MOISTURE_EQUATION_CHUNG_PFOST} value={pond.MoistureEquation.MOISTURE_EQUATION_CHUNG_PFOST}>
Chung-Pfost
</MenuItem>
<MenuItem key={pond.MoistureEquation.MOISTURE_EQUATION_HENDERSON} value={pond.MoistureEquation.MOISTURE_EQUATION_HENDERSON}>
Henderson
</MenuItem>
<MenuItem key={pond.MoistureEquation.MOISTURE_EQUATION_HALSEY} value={pond.MoistureEquation.MOISTURE_EQUATION_HALSEY}>
Halsey
</MenuItem>
<MenuItem key={pond.MoistureEquation.MOISTURE_EQUATION_OSWIN} value={pond.MoistureEquation.MOISTURE_EQUATION_OSWIN}>
Oswin
</MenuItem>
</TextField>
<TextField
sx={{marginBottom: 2}}
fullWidth
type="number"
value={constantA}
error={isNaN(parseFloat(constantA))}
helperText={isNaN(parseFloat(constantA)) ? "Must be a valid number" : ""}
onChange={(e) => {
let val = e.target.value
setConstantA(val)
setIsNew(true)
if(!isNaN(parseFloat(val))){
let settings = cloneDeep(newGrainSettings)
settings.a = parseFloat(val)
settingsChanged(settings)
}
}}
label="Constant A"/>
<TextField
sx={{marginBottom: 2}}
fullWidth
type="number"
value={constantB}
error={isNaN(parseFloat(constantB))}
helperText={isNaN(parseFloat(constantB)) ? "Must be a valid number" : ""}
onChange={(e) => {
let val = e.target.value
setConstantB(val)
setIsNew(true)
if(!isNaN(parseFloat(val))){
let settings = cloneDeep(newGrainSettings)
settings.b = parseFloat(val)
settingsChanged(settings)
}
}}
label="Constant B"/>
<TextField
sx={{marginBottom: 2}}
fullWidth
type="number"
value={constantC}
error={isNaN(parseFloat(constantC))}
helperText={isNaN(parseFloat(constantC)) ? "Must be a valid number" : ""}
onChange={(e) => {
let val = e.target.value
setConstantC(val)
setIsNew(true)
if(!isNaN(parseFloat(val))){
let settings = cloneDeep(newGrainSettings)
settings.c = parseFloat(val)
settingsChanged(settings)
}
}}
label="Constant C"/>
<TextField
sx={{marginBottom: 2}}
fullWidth
type="number"
value={kgPerBushel}
error={isNaN(parseFloat(kgPerBushel))}
helperText={isNaN(parseFloat(kgPerBushel)) ? "Must be a valid number" : ""}
onChange={(e) => {
let val = e.target.value
setKgPerBushel(val)
setIsNew(true)
if(!isNaN(parseFloat(val))){
let settings = cloneDeep(newGrainSettings)
settings.kgPerBushel = parseFloat(val)
settingsChanged(settings)
}
}}
label="kg per Bushel"/>
<TextField
sx={{marginBottom: 2}}
fullWidth
type="number"
value={bushelsPerTonne}
error={isNaN(parseFloat(bushelsPerTonne))}
helperText={isNaN(parseFloat(bushelsPerTonne)) ? "Must be a valid number" : ""}
onChange={(e) => {
let val = e.target.value
setBushelsPerTonne(val)
setIsNew(true)
if(!isNaN(parseFloat(val))){
let settings = cloneDeep(newGrainSettings)
settings.bushelsPerTonne = parseFloat(val)
settingsChanged(settings)
}
}}
label="Bushels per Tonne"/>
{isNew &&
<Box display='flex' justifyContent="flex-end">
<Button
variant="contained"
disabled={invalid()}
onClick={() => {
saveGrain()
setIsNew(false)
}}
color="primary">
Save Custom Grain
</Button>
</Box>
}
</React.Fragment>
)
}