kaosjr

reactor_base.lua

May 4th, 2020
34
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.83 KB | None | 0 0
  1. local computer = require("computer")
  2. local component = require("component")
  3. local oop = require("oop")
  4. local config_api = require("brgc/config")
  5. local reactorState = require("brgc/reactor_state")
  6. local regulationState = require("brgc/regulation_state")
  7. local calibration_ringbuffer = require("brgc/calibration_ringbuffer")
  8. local polynomial = require("polynomial")
  9.  
  10. local reactorCalibrationMaxOutput = {0.01,0.02,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.5,0.6,0.7,0.75,0.8,0.9,1}
  11.  
  12. local reactor_base = {
  13. mAddress = nil,
  14. mComponent = nil,
  15. mPassive = nil,
  16. mRodNum = 0,
  17. mRodLevelState = nil,
  18. mRodLevel = 0,
  19. mRodTarget = 0,
  20. mRodOffset = 0,
  21. mOutputRateOpt = 0,
  22. mOutputRateMax = 0,
  23. mState = reactorState.OFFLINE,
  24. mRegulationState = regulationState.NONE,
  25. mAPI2IsBugged = false,
  26.  
  27. mReactorConfig = {
  28. rodLevelMin = 0,
  29. outputOpt = 0,
  30. outputPoly = nil,
  31. outputReversePoly = polynomial.make({0, 1}),
  32. regulationBehaviour = regulationState.GRID,
  33. disabled = false,
  34. PWMLevelOnline = 0.15,
  35. PWMLevelOffline = 0.8,
  36. },
  37.  
  38. mReactorStats = {
  39. outputRateCurrent = 0,
  40. outputExtractionRate = 0,
  41.  
  42. outputStoredCurrent = 0,
  43. outputStoredLast = 0,
  44. outputStoredRate = 0,
  45. outputCapacity = 0,
  46.  
  47. temperatureFuelCurrent = 0,
  48. temperatureFuelLast = 0,
  49. temperatureFuelRate = 0,
  50.  
  51. fuelLevel = 0,
  52. fuelRate = 0,
  53.  
  54. tickLast = 0,
  55. timeDiff = 0
  56. },
  57.  
  58. -- calibration stuff
  59. mCalibrationData = nil,
  60. mCalibrationStep = nil,
  61. mCalibrationTemperatureRingbuffer = nil,
  62. mCalibrationValueRingbuffer = nil,
  63. mCalibrationTemperatureDeviationRingbuffer = nil,
  64. mCalibrationValueDeviationRingbuffer = nil
  65. }
  66. oop.make(reactor_base)
  67.  
  68. function reactor_base:construct(address, config)
  69. checkArg(1, address, "string")
  70. checkArg(2, config, "table", "nil")
  71.  
  72. self.mAddress = address
  73. self.mReactorConfig = config or { }
  74. self.mReactorStats = { }
  75.  
  76. setmetatable(self.mReactorConfig, {__index = reactor_base.mReactorConfig})
  77. setmetatable(self.mReactorStats, {__index = reactor_base.mReactorStats})
  78.  
  79. if config ~= nil then
  80. if type(config.outputPoly) == "table" and type(config.outputPoly.coefs) == "table" then
  81. self.mReactorConfig.outputPoly = polynomial.make(config.outputPoly.coefs)
  82. else
  83. self.mReactorConfig.outputPoly = nil
  84. end
  85. if type(config.outputReversePoly) == "table" and type(config.outputReversePoly.coefs) == "table" then
  86. self.mReactorConfig.outputReversePoly = polynomial.make(config.outputReversePoly.coefs)
  87. else
  88. self.mReactorConfig.outputReversePoly = nil
  89. end
  90. end
  91. end
  92.  
  93. function reactor_base:connect()
  94. self.mComponent = component.proxy(self.mAddress)
  95. if self.mComponent == nil then
  96. return false
  97. end
  98. self.mPassive = not self.mComponent.isActivelyCooled()
  99. self.mRodNum = self.mComponent.getNumberOfControlRods()
  100. self:updateStats()
  101.  
  102. if self.init and not self:isDisabled() then
  103. self:init()
  104. end
  105.  
  106. return true
  107. end
  108.  
  109. function reactor_base:isConnected()
  110. if not self.mComponent then
  111. return false
  112. elseif self.mComponent.mbIsConnected then
  113. -- New API
  114. local success, state = pcall(self.mComponent.mbIsConnected)
  115.  
  116. return success and state
  117. elseif self.mComponent.getConnected then
  118. -- Old API
  119. local success, state = pcall(self.mComponent.getConnected)
  120.  
  121. return success and state
  122. end
  123.  
  124. -- Nothing of the above is true? Welp... we don't seem to be connected then!
  125. return false
  126. end
  127.  
  128. function reactor_base:isReady()
  129. local isAssembled = true
  130.  
  131. if self.mComponent.mbIsAssembled then
  132. local success, state = pcall(self.mComponent.mbIsAssembled)
  133.  
  134. isAssembled = success and state
  135. end
  136.  
  137. return isAssembled
  138. end
  139.  
  140. function reactor_base:isActivelyCooled()
  141. return not self.mPassive
  142. end
  143.  
  144. function reactor_base:isDisabled()
  145. return not not self.mReactorConfig.disabled
  146. end
  147.  
  148. function reactor_base:getState()
  149. return self.mState
  150. end
  151.  
  152. function reactor_base:getAddress()
  153. return self.mAddress
  154. end
  155.  
  156. function reactor_base:getAddressShort()
  157. return string.sub(self.mAddress, 1, 3)
  158. end
  159.  
  160. function reactor_base:getOutputOpt()
  161. return self.mReactorConfig.outputOpt
  162. end
  163.  
  164. function reactor_base:getRegulationBehaviour()
  165. return self.mReactorConfig.regulationBehaviour
  166. end
  167.  
  168. function reactor_base:setRegulationBehaviour(behaviour)
  169. assert(behaviour == regulationState.AUTO or behaviour == regulationState.PWM or behaviour == regulationState.LOAD or behaviour == regulationState.GRID, "Invalid behaviour")
  170. self.mReactorConfig.regulationBehaviour = behaviour
  171. config_api.setReactor(self:getAddress(), self.mReactorConfig)
  172. end
  173.  
  174. function reactor_base:getRegulationBehaviour()
  175. return self.mReactorConfig.regulationBehaviour
  176. end
  177.  
  178. function reactor_base:setDisabled(disabled)
  179. -- Don't update if nothing changed. Saves some i/o.
  180. if self.mReactorConfig.disabled ~= not not disabled then
  181. self.mReactorConfig.disabled = not not disabled
  182. config_api.setReactor(self:getAddress(), self.mReactorConfig)
  183. end
  184. end
  185.  
  186. function reactor_base:getFuelStats()
  187. if self.mComponent.getFuelStats then
  188. return self.mComponent.getFuelStats()
  189. else
  190. return {
  191. fuelTemperature = self.mComponent.getFuelTemperature(),
  192. fuelAmount = self.mComponent.getFuelAmount(),
  193. wasteAmount = self.mComponent.getWasteAmount(),
  194. fuelCapacity = self.mComponent.getFuelAmountMax(),
  195. fuelConsumedLastTick = self.mComponent.getFuelConsumedLastTick()
  196. }
  197. end
  198. end
  199.  
  200. function reactor_base:updateStats()
  201. -- If we're not connected, we can't do anything really.
  202. -- In order to avoid computing wierd stuff as soon as the reactor reconnects,
  203. -- we're going to reset the stats instead.
  204. if not self:isConnected() then
  205. self.mReactorStats = { }
  206. setmetatable(self.mReactorStats, {__index = reactor_base.mReactorStats})
  207. return false
  208. end
  209.  
  210. local now = computer.uptime() * 20
  211. local timediff = now - self.mReactorStats.tickLast
  212.  
  213. -- Did we advance at least one tick?
  214. if timediff <= 0 then
  215. -- We've already been called this tick. No need to do anything.
  216. -- This it NOT an optimization. Stuff will break if we go any further.
  217. return false
  218. end
  219.  
  220. local outputStats = self:getOutputStats()
  221. local fuelStats = self:getFuelStats()
  222.  
  223. -- Shift current stats to last stats
  224. self.mReactorStats.outputStoredLast = self.mReactorStats.outputStoredCurrent
  225. self.mReactorStats.temperatureFuelLast = self.mReactorStats.temperatureFuelCurrent
  226.  
  227. -- Update output stats
  228. self.mReactorStats.outputRateCurrent = outputStats.outputProducedLastTick
  229. self.mReactorStats.outputStoredCurrent = outputStats.outputStored
  230. self.mReactorStats.outputCapacity = outputStats.outputCapacity
  231.  
  232. -- Update fuel stats
  233. self.mReactorStats.temperatureFuelCurrent = fuelStats.fuelTemperature
  234. -- TODO: Once waste is returned by this, add that as well
  235. self.mReactorStats.fuelLevel = (fuelStats.fuelAmount + fuelStats.wasteAmount / 100) / fuelStats.fuelCapacity
  236. self.mReactorStats.fuelRate = fuelStats.fuelConsumedLastTick
  237.  
  238. -- Update rates
  239. -- If tickLast is 0 then this is the first iteration and we can't compute any rates.
  240. if self.mReactorStats.tickLast > 0 then
  241. self.mReactorStats.outputStoredRate = ( self.mReactorStats.outputStoredCurrent - self.mReactorStats.outputStoredLast ) / timediff
  242. self.mReactorStats.outputExtractionRate = math.max( 0, self.mReactorStats.outputRateCurrent - self.mReactorStats.outputStoredRate )
  243. self.mReactorStats.temperatureFuelRate = ( self.mReactorStats.temperatureFuelCurrent - self.mReactorStats.temperatureFuelLast ) / timediff
  244. end
  245.  
  246. self.mReactorStats.tickLast = now
  247. self.mReactorStats.timeDiff = timediff
  248. -- Fail for the first time so nobody can accidentally run the statemachine and crash.
  249. return (timediff ~= now)
  250. end
  251.  
  252. -- Getters related to fuel temperature
  253.  
  254. function reactor_base:getFuelTemperature()
  255. return self.mReactorStats.temperatureFuelCurrent
  256. end
  257.  
  258. function reactor_base:getNormalizedFuelTemperature()
  259. local fuelLevel = self:getFuelLevel()
  260. if fuelLevel == 0 then
  261. return 0
  262. else
  263. return self:getFuelTemperature() / fuelLevel
  264. end
  265. end
  266.  
  267. function reactor_base:getFuelTemperatureRate()
  268. return self.mReactorStats.temperatureFuelRate
  269. end
  270.  
  271. -- Getters related to fuel consumption
  272.  
  273. function reactor_base:getFuelConsumedLastTick()
  274. return self.mReactorStats.fuelRate
  275. end
  276.  
  277. -- Getters related to the fuel tank
  278.  
  279. function reactor_base:getFuelLevel()
  280. return self.mReactorStats.fuelLevel
  281. end
  282.  
  283. -- Getters related to the reactors output
  284.  
  285. function reactor_base:getOutputGenerationRate()
  286. return self.mReactorStats.outputRateCurrent
  287. end
  288.  
  289. function reactor_base:getOutputGenerationRateMax()
  290. return self.mOutputRateMax
  291. end
  292.  
  293. function reactor_base:getNormalizedOutputGenerationRate()
  294. local fuelLevel = self:getFuelLevel()
  295. if fuelLevel == 0 then
  296. return 0
  297. else
  298. return self:getOutputGenerationRate() / fuelLevel
  299. end
  300. end
  301.  
  302. function reactor_base:getOutputExtractionRate()
  303. return self.mReactorStats.outputExtractionRate
  304. end
  305.  
  306. function reactor_base:getOptimalOutputGenerationRate()
  307. return self.mOutputRateOpt
  308. end
  309.  
  310. function reactor_base:getCurrentOptimalOutputGenerationRate()
  311. -- This is only an approximation. The correct value requires the fuel level
  312. -- to be multiplied with the rod level and then put through the polynome
  313. return self:getOptimalOutputGenerationRate() * self:getFuelLevel()
  314. end
  315.  
  316. function reactor_base:getOutputStored()
  317. return self.mReactorStats.outputStoredCurrent
  318. end
  319.  
  320. function reactor_base:getOutputStoredMax()
  321. return self.mReactorStats.outputCapacity
  322. end
  323.  
  324. function reactor_base:getOutputStoredRate()
  325. return self.mReactorStats.outputStoredRate
  326. end
  327.  
  328. function reactor_base:isCalibrated()
  329. return self:getOptimalOutputGenerationRate() ~= nil and self.mReactorConfig.outputPoly ~= nil
  330. end
  331.  
  332. -- Rod logic
  333.  
  334. function reactor_base:setRodLevelRaw(rawlevel)
  335. if self.mComponent.setControlRodsLevels and not reactor_base.mAPI2IsBugged then
  336. self:setRodLevelRawAPI2(rawlevel)
  337. elseif self.mComponent.setAllControlRodLevels and self.mComponent.setControlRodLevel then
  338. self:setRodLevelRawAPI1(rawlevel)
  339. else
  340. error('Unable to find appropriate API function for setting control rods on reactor ' .. self.mAddress)
  341. end
  342. end
  343.  
  344. function reactor_base:getRodIdxFromCenter(idx)
  345. checkArg(1, idx, "number")
  346.  
  347. local centerRodIdx = math.floor(self.mRodNum / 2)
  348. local rodIdxOffset = math.ceil(idx / 2)
  349.  
  350. if idx % 2 == 0 then
  351. rodIdxOffset = 0 - rodIdxOffset
  352. end
  353.  
  354. local actualIdx = centerRodIdx + rodIdxOffset
  355.  
  356. if actualIdx == self.mRodNum then
  357. actualIdx = 0
  358. elseif actualIdx == -1 then
  359. actualIdx = self.mRodNum - 1
  360. end
  361.  
  362. assert(actualIdx >= 0 and actualIdx < self.mRodNum, "Detected bug in rod idx calculation")
  363.  
  364. return actualIdx
  365. end
  366.  
  367. function reactor_base:calculateRodLevelInfo(rawlevel)
  368. checkArg(1, rawlevel, "number")
  369.  
  370. local rodLevel
  371. local rodLevelMin = self.mReactorConfig.rodLevelMin or 0
  372. local rodExtraLevel
  373. local rodLevelRounded
  374.  
  375. if rawlevel > self.mRodNum * (100-rodLevelMin) then
  376. rawlevel = self.mRodNum * (100-rodLevelMin)
  377. elseif rawlevel < 0 then
  378. rawlevel = 0
  379. end
  380.  
  381. rodLevelRounded = math.floor(rawlevel+0.5)
  382. local roadLevelf = rodLevelMin + rawlevel / self.mRodNum
  383. rodLevel = math.floor(roadLevelf)
  384. rodExtraLevel = math.floor((roadLevelf - rodLevel) * self.mRodNum + 0.5)
  385.  
  386. if rodLevel > 100 then
  387. rodLevel = 100
  388. rodExtraLevel = 0
  389. end
  390.  
  391. return rodLevelRounded, rodLevel, rodExtraLevel
  392. end
  393.  
  394. function reactor_base:setRodLevelRawAPI1(rawlevel)
  395. checkArg(1, rawlevel, "number")
  396.  
  397. local rodLevelRounded, rodLevel, rodExtraLevel = self:calculateRodLevelInfo(rawlevel)
  398. local rodLevelState = rodLevel * self.mRodNum + rodExtraLevel
  399.  
  400. -- If the combined rod level did not change, there is no need to call into
  401. -- the reactor API. We can leave things as they were.
  402. if self.mRodLevelState ~= rodLevelState then
  403. -- In order to minimize the API calls required to set all rods, we
  404. -- first set all rods to the rod level the majority of the rods have to
  405. -- be set to and afterwards correct the other rod leves.
  406. if rodExtraLevel <= self.mRodNum / 2 then
  407. self.mComponent.setAllControlRodLevels(rodLevel)
  408. for i=0, rodExtraLevel-1 do
  409. self:setRodLevelDirectFromCenterAPI1(i, rodLevel+1)
  410. end
  411. else
  412. self.mComponent.setAllControlRodLevels(rodLevel+1)
  413. for i=rodExtraLevel, self.mRodNum-1 do
  414. self:setRodLevelDirectFromCenterAPI1(i, rodLevel)
  415. end
  416. end
  417.  
  418. self.mRodLevel = rodLevelRounded
  419. self.mRodLevelState = rodLevelState
  420. end
  421. end
  422.  
  423. function reactor_base:setRodLevelDirectFromCenterAPI1(idx, level)
  424. checkArg(1, idx, "number")
  425. checkArg(2, level, "number")
  426.  
  427. if level < 0 or level > 100 then
  428. return
  429. end
  430.  
  431. self.mComponent.setControlRodLevel(self:getRodIdxFromCenter(idx), level)
  432. end
  433.  
  434. function reactor_base:setRodLevelRawAPI2(rawlevel)
  435. checkArg(1, rawlevel, "number")
  436.  
  437. local rodLevelRounded, rodLevel, rodExtraLevel = self:calculateRodLevelInfo(rawlevel)
  438. local rodLevelState = rodLevel * self.mRodNum + rodExtraLevel
  439.  
  440. -- If the combined rod level did not change, there is no need to call into
  441. -- the reactor API. We can leave things as they were.
  442. if self.mRodLevelState ~= rodLevelState then
  443. local rodLevelTable = {}
  444.  
  445. for i=0, rodExtraLevel-1 do
  446. rodLevelTable[self:getRodIdxFromCenter(i)] = rodLevel+1
  447. end
  448. for i=rodExtraLevel, self.mRodNum-1 do
  449. rodLevelTable[self:getRodIdxFromCenter(i)] = rodLevel
  450. end
  451.  
  452. local retval, error = self.mComponent.setControlRodsLevels(rodLevelTable)
  453. if retval == nil and error ~= nil then
  454. reactor_base.mAPI2IsBugged = true
  455. return self:setRodLevelRawAPI1(rawlevel)
  456. end
  457.  
  458. self.mRodLevel = rodLevelRounded
  459. self.mRodLevelState = rodLevelState
  460. end
  461. end
  462.  
  463. function reactor_base:translateFromLinearOutput(level)
  464. checkArg(1, level, "number")
  465. return self.mReactorConfig.outputReversePoly:eval(self.mOutputRateMax * level)
  466. end
  467.  
  468. function reactor_base:translateToLinearOutput(level)
  469. checkArg(1, level, "number")
  470. return self.mReactorConfig.outputPoly:eval(level) / self.mOutputRateMax
  471. end
  472.  
  473. function reactor_base:setOutput(levelpercent, offset)
  474. checkArg(1, levelpercent, "number")
  475. checkArg(2, offset, "number", "nil")
  476.  
  477. local rodLevelMin = self.mReactorConfig.rodLevelMin or 0
  478. if offset ~= nil then
  479. self.mRodOffset = 0-self.mRodNum*(100-rodLevelMin)*offset
  480. end
  481. self.mRodTarget = self.mRodNum*(100-rodLevelMin)*(1-levelpercent)
  482. self:setRodLevelRaw( self.mRodTarget + self.mRodOffset )
  483. end
  484.  
  485. function reactor_base:setOutputOffset(offsetpercent)
  486. checkArg(1, offsetpercent, "number")
  487.  
  488. local rodLevelMin = self.mReactorConfig.rodLevelMin or 0
  489. self.mRodOffset = 0-self.mRodNum*(100-rodLevelMin)*offsetpercent
  490. self:setRodLevelRaw( self.mRodTarget + self.mRodOffset )
  491. end
  492.  
  493. function reactor_base:getOutputOffset()
  494. if self.mReactorConfig.rodLevelMin == 100 then
  495. return 0
  496. else
  497. return 0-self.mRodOffset/(100-self.mReactorConfig.rodLevelMin)/self.mRodNum
  498. end
  499. end
  500.  
  501. function reactor_base:getOutput()
  502. return math.max(0, math.min(1, 1 - (self.mRodTarget + self.mRodOffset) / (100 - self.mReactorConfig.rodLevelMin) / self.mRodNum))
  503. end
  504.  
  505. function reactor_base:setActive(active)
  506. checkArg(1, active, "boolean")
  507.  
  508. self.mComponent.setActive(active)
  509. end
  510.  
  511. function reactor_base:setOutputPolynomialCoefs(coefs)
  512. checkArg(1, coefs, "table", "nil")
  513.  
  514. if coefs ~= nil then
  515. self.mReactorConfig.outputPoly = polynomial.make(coefs.coefs or coefs)
  516. end
  517. end
  518.  
  519. function reactor_base:setOutputReversePolynomialCoefs(coefs)
  520. checkArg(1, coefs, "table", "nil")
  521.  
  522. if coefs ~= nil then
  523. self.mReactorConfig.outputReversePoly = polynomial.make(coefs.coefs or coefs)
  524. end
  525. end
  526.  
  527. function reactor_base:recalculateOpts()
  528. if self.mReactorConfig.outputPoly ~= nil and self.mReactorConfig.outputReversePoly ~= nil then
  529. self.mOutputRateMax = self.mReactorConfig.outputPoly:eval(1)
  530. self.mOutputRateOpt = self.mReactorConfig.outputPoly:eval(self.mReactorConfig.outputOpt)
  531. end
  532. end
  533.  
  534. function reactor_base:setState(state)
  535. self.mState = state
  536.  
  537. if state == reactorState.ERROR or state == reactorState.OFFLINE then
  538. self.mComponent.setActive(false)
  539. elseif state == reactorState.CALIBRATING then
  540. self.mCalibrationData = {}
  541. self.mCalibrationStep = 1
  542. self:setOutput(reactorCalibrationMaxOutput[1])
  543. self.mComponent.setActive(true)
  544. elseif state == reactorState.ONLINE then
  545. if not self:isCalibrated() then
  546. self:setState(reactorState.CALIBRATING)
  547. else
  548. self.mComponent.setActive(true)
  549. end
  550. else
  551. error("Invalid reactor state supplied: " + tostring(state))
  552. end
  553. end
  554.  
  555. function reactor_base:recalibrate()
  556. self:setState(reactorState.OFFLINE)
  557.  
  558. self.mReactorConfig = {
  559. regulationBehaviour = self.mReactorConfig.regulationBehaviour,
  560. disabled = self.mReactorConfig.disabled,
  561. PWMLevelOnline = self.mReactorConfig.PWMLevelOnline,
  562. PWMLevelOffline = self.mReactorConfig.PWMLevelOffline
  563. }
  564. self.mReactorStats = { }
  565.  
  566. setmetatable(self.mReactorConfig, {__index = reactor_base.mReactorConfig})
  567. setmetatable(self.mReactorStats, {__index = reactor_base.mReactorStats})
  568.  
  569. config_api.setReactor(self:getAddress(), self.mReactorConfig)
  570. self:setState(reactorState.ONLINE)
  571. end
  572.  
  573. function reactor_base:initCalibrationBuffers()
  574. self.mCalibrationValueRingbuffer = calibration_ringbuffer(10)
  575. self.mCalibrationTemperatureRingbuffer = calibration_ringbuffer(10)
  576. self.mCalibrationValueDeviationRingbuffer = calibration_ringbuffer(10)
  577. self.mCalibrationTemperatureDeviationRingbuffer = calibration_ringbuffer(10)
  578. end
  579.  
  580. function reactor_base:clearCalibrationBuffers()
  581. self.mCalibrationTemperatureRingbuffer = nil
  582. self.mCalibrationValueRingbuffer = nil
  583. self.mCalibrationTemperatureDeviationRingbuffer = nil
  584. self.mCalibrationValueDeviationRingbuffer = nil
  585. end
  586.  
  587. function reactor_base:runCalibration()
  588. local calibValue = self:getNormalizedOutputGenerationRate()
  589. local calibTemp = self:getNormalizedFuelTemperature()
  590.  
  591. if self.mCalibrationTemperatureRingbuffer == nil or self.mCalibrationValueRingbuffer == nil or self.mCalibrationTemperatureDeviationRingbuffer == nil or self.mCalibrationValueDeviationRingbuffer == nil then
  592. self:initCalibrationBuffers()
  593. end
  594.  
  595. self.mCalibrationValueRingbuffer:push(calibValue)
  596. self.mCalibrationValueDeviationRingbuffer:push(self.mCalibrationValueRingbuffer:getStandardDeviation())
  597.  
  598. self.mCalibrationTemperatureRingbuffer:push(calibTemp)
  599. self.mCalibrationTemperatureDeviationRingbuffer:push(self.mCalibrationTemperatureRingbuffer:getStandardDeviation())
  600.  
  601. -- Scientific sure fire methods to find out if the reactor is in a stable
  602. -- condition. Both of these conditions need to be true for that to be the
  603. -- case.
  604. local nonMonotonic = not self.mCalibrationTemperatureRingbuffer:isMonotonic() and not self.mCalibrationValueRingbuffer:isMonotonic()
  605. local stable = not self.mCalibrationValueDeviationRingbuffer:isMonotonic() and not self.mCalibrationTemperatureDeviationRingbuffer:isMonotonic()
  606.  
  607. -- Dirty hacks to allow faster calibration. Either one of these can replace
  608. -- the respective scientific calculation.
  609. local isCloseEnough = (math.abs(self.mCalibrationTemperatureRingbuffer:getAverage() - calibTemp) < self.mCalibrationTemperatureRingbuffer:getAverage() / 1000) and (self.mCalibrationTemperatureRingbuffer:count() == 10)
  610. or (math.abs(self.mCalibrationValueRingbuffer:getAverage() - calibValue) < self.mCalibrationValueRingbuffer:getAverage() / 1000) and (self.mCalibrationValueRingbuffer:count() == 10)
  611. local lowDeviation = self.mCalibrationTemperatureRingbuffer:getStandardDeviation() <= self.mCalibrationTemperatureRingbuffer:getAverage() / 1000
  612. and self.mCalibrationValueRingbuffer:getStandardDeviation() <= self.mCalibrationValueRingbuffer:getAverage() / 1000
  613.  
  614. -- If you found this, congratulations ;)
  615. -- Here are some leftover debug prints that allow you to get an idea what's
  616. -- going on during reactor calibration.
  617.  
  618. --[[
  619. print(string.format( "temp avg: %.03f std: %.03f stable: %s",
  620. self.mCalibrationTemperatureRingbuffer:getAverage(),
  621. self.mCalibrationTemperatureRingbuffer:getStandardDeviation(),
  622. tostring(not self.mCalibrationTemperatureDeviationRingbuffer:isMonotonic())
  623. ))
  624. print(string.format( "val avg: %.03f std: %.03f stable: %s",
  625. self.mCalibrationValueRingbuffer:getAverage(),
  626. self.mCalibrationValueRingbuffer:getStandardDeviation(),
  627. tostring(not self.mCalibrationValueDeviationRingbuffer:isMonotonic())
  628. ))
  629. print("nonMonotonic: " .. tostring(nonMonotonic) ..
  630. " lowDeviation: " .. tostring(lowDeviation) ..
  631. " stable: " .. tostring(stable) ..
  632. " isClose: " .. tostring(isCloseEnough)
  633. )
  634. --]]
  635. if not self:isGood() then
  636. self:finalizeCalibration()
  637. elseif (nonMonotonic or isCloseEnough) and (lowDeviation or stable) then
  638. if calibValue ~= nil and reactorCalibrationMaxOutput[self.mCalibrationStep] ~= nil then
  639. if type(self.mCalibrationData) ~= "table" then
  640. self.mCalibrationData = { }
  641. end
  642. table.insert(self.mCalibrationData, {
  643. step = self.mCalibrationStep,
  644. load = reactorCalibrationMaxOutput[self.mCalibrationStep],
  645. value = self.mCalibrationValueRingbuffer:getAverage(),
  646. efficiency = self.mCalibrationValueRingbuffer:getAverage() / self:getFuelConsumedLastTick()
  647. })
  648.  
  649. -- print("----------")
  650.  
  651. self.mCalibrationStep = self.mCalibrationStep + 1
  652. if reactorCalibrationMaxOutput[self.mCalibrationStep] ~= nil then
  653. self:initCalibrationBuffers()
  654. self:setOutput(reactorCalibrationMaxOutput[self.mCalibrationStep], 0)
  655. else
  656. self:clearCalibrationBuffers()
  657. self:finalizeCalibration()
  658. end
  659. else
  660. self:clearCalibrationBuffers()
  661. self:finalizeCalibration()
  662. end
  663. end
  664. self.mCalibrationValueLast = calibValue
  665. end
  666.  
  667. function reactor_base:runStateMachine()
  668. -- Before we run anything we try to update the reactors stats.
  669. -- If this function fails the stats have either been cleared
  670. -- or they're incomplete so we return early.
  671. if not self:updateStats() then
  672. return
  673. end
  674.  
  675. if self.mState == reactorState.CALIBRATING then
  676. self:runCalibration()
  677. elseif self.mState == reactorState.ONLINE then
  678. self:regulate()
  679. end
  680. end
  681.  
  682. return reactor_base
Add Comment
Please, Sign In to add comment