RosettaCodeData/Task/Active-object/Nim/active-object.nim

114 lines
3.5 KiB
Nim

# Active object.
# Compile with "nim c --threads:on".
import locks
import os
import std/monotimes
type
# Function to use for integration.
TimeFunction = proc (t: float): float {.gcsafe.}
# Integrator object.
Integrator = ptr TIntegrator
TIntegrator = object
k: TimeFunction # The function to integrate.
dt: int # Time interval in milliseconds.
thread: Thread[Integrator] # Thread which does the computation.
s: float # Computed value.
lock: Lock # Lock to manage concurrent accesses.
isRunning: bool # True if integrator is running.
#---------------------------------------------------------------------------------------------------
proc newIntegrator(f: TimeFunction; dt: int): Integrator =
## Create an integrator.
result = cast[Integrator](allocShared(sizeof(TIntegrator)))
result.k = f
result.dt = dt
result.s = 0
result.lock.initLock()
result.isRunning = false
#---------------------------------------------------------------------------------------------------
proc process(integrator: Integrator) {.thread, gcsafe.} =
## Do the integration.
integrator.isRunning = true
let start = getMonotime().ticks
var t0: float = 0
var k0 = integrator.k(0)
while true:
sleep(integrator.dt)
withLock integrator.lock:
if not integrator.isRunning:
break
let t1 = float(getMonoTime().ticks - start) / 1e9
let k1 = integrator.k(t1)
integrator.s += (k1 + k0) * (t1 - t0) / 2
t0 = t1
k0 = k1
#---------------------------------------------------------------------------------------------------
proc start(integrator: Integrator) =
## Start the integrator by launching a thread to do the computation.
integrator.thread.createThread(process, integrator)
#---------------------------------------------------------------------------------------------------
proc stop(integrator: Integrator) =
## Stop the integrator.
withLock integrator.lock:
integrator.isRunning = false
integrator.thread.joinThread()
#---------------------------------------------------------------------------------------------------
proc setInput(integrator: Integrator; f: TimeFunction) =
## Set the function.
withLock integrator.lock:
integrator.k = f
#---------------------------------------------------------------------------------------------------
proc output(integrator: Integrator): float =
## Return the current output.
withLock integrator.lock:
result = integrator.s
#---------------------------------------------------------------------------------------------------
proc destroy(integrator: Integrator) =
## Destroy an integrator, freing the resources.
if integrator.isRunning:
integrator.stop()
integrator.lock.deinitLock()
integrator.deallocShared()
#---------------------------------------------------------------------------------------------------
from math import PI, sin
# Create the integrator and start it.
let integrator = newIntegrator(proc (t: float): float {.gcsafe.} = sin(PI * t), 1)
integrator.start()
echo "Integrator started."
sleep(2000)
echo "Value after 2 seconds: ", integrator.output()
# Change the function to use.
integrator.setInput(proc (t: float): float {.gcsafe.} = 0)
echo "K function changed."
sleep(500)
# Stop the integrator and display the computed value.
integrator.stop()
echo "Value after 0.5 more second: ", integrator.output()
integrator.destroy()