{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE FlexibleInstances #-}

module PID where

import Ivory.Compile.C.CmdlineFrontend
import Ivory.Language

[ivory|

struct PID
  { pid_mv  :: Stored IFloat
  ; pid_i   :: Stored IFloat
  ; pid_err :: Stored IFloat
  }

|]

type SP   = IFloat -- Set point
type PV   = IFloat -- Process (measured) value
type Time = IFloat

{-
void pid_update(struct PID * pid,
                sp_t sp,
                pv_t pv,
                timeinc_t dt
                )
{
  float err = sp - pv;
  float i   = pid->i + err*dt;
  float d   = (err - pid->err) / dt;
  pid->i    = ki*i;
  pid->mv   = kp*err + pid->i + kd*d;
  pid->err  = err;
  return;
}
-}

kp, ki, kd :: IFloat
kp = 1.0
ki = 0.1
kd = 0.1

pidUpdate :: Def ('[ Ref s (Struct "PID")
                   , SP
                   , PV
                   , Time ]
                  :-> IFloat)
pidUpdate = proc "pid_update" $
  \ pid sp pv dt ->
  -- These are made up requires/ensures for testing purposes.
    requires (checkStored (pid ~> pid_err) (\err -> err <? 1))
  $ ensures  (\res -> checkStored (pid ~> pid_err) (\err -> err <? res))
  $ body
  $ do
    err     <- assign (sp - pv)
    i       <- deref $ pid ~> pid_i
    i'      <- assign  $ ki * (i + err*dt)
    prevErr <- deref $ pid ~> pid_err
    d       <- assign  $ (err - prevErr) / dt
    store (pid ~> pid_i)   i'
    store (pid ~> pid_mv)  (kp*err + i' + kd*d)
    store (pid ~> pid_err) err
    ret err

foo :: Def ('[ Ref s (Array 3 (Stored Uint32))
             , Ref s (Array 3 (Stored Uint32)) ] :-> ())
foo = proc "foo" $ \a b ->
--  requires (*a!0 < *b!0)
  requires (checkStored (a ! 0)
              (\v -> (checkStored (b ! 0)
                     (\v1 -> v <? v1))))
  $ body $ do
    retVoid

runPID :: IO ()
runPID = runCompiler [cmodule] []
  initialOpts { outDir = Nothing, bitShiftCheck = True, divZero = True }

cmodule :: Module
cmodule = package "PID" $ do
  incl foobar
  -- defStruct (Proxy :: Proxy "PID")
  -- incl pidUpdate
  -- incl alloc_test

foobar :: Def ('[Uint8] :-> Uint8)
foobar = proc "foobar" $ \x -> body $ do
  ret (x `iShiftR` (3 `iDiv` 2))

alloc_test :: Def ('[] :-> IFloat)
alloc_test  = proc "alloc_test" $ body $ do
  pid <- local (istruct [pid_i .= ival 1])
  ret =<< deref (pid ~> pid_i)
