diff options
Diffstat (limited to 'server/src')
| -rw-r--r-- | server/src/Thermoprint/Server.hs | 24 | ||||
| -rw-r--r-- | server/src/Thermoprint/Server/QMConfig.hs | 129 | 
2 files changed, 17 insertions, 136 deletions
| diff --git a/server/src/Thermoprint/Server.hs b/server/src/Thermoprint/Server.hs index c2a4972..446c63e 100644 --- a/server/src/Thermoprint/Server.hs +++ b/server/src/Thermoprint/Server.hs | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | {-# LANGUAGE TypeOperators #-} | 4 | {-# LANGUAGE TypeOperators #-} | 
| 5 | {-# LANGUAGE FlexibleContexts #-} | 5 | {-# LANGUAGE FlexibleContexts #-} | 
| 6 | {-# LANGUAGE ImpredicativeTypes #-} | 6 | {-# LANGUAGE ImpredicativeTypes #-} | 
| 7 | {-# LANGUAGE ExistentialQuantification #-} | ||
| 7 | {-# LANGUAGE ViewPatterns #-} | 8 | {-# LANGUAGE ViewPatterns #-} | 
| 8 | {-# LANGUAGE DataKinds #-} | 9 | {-# LANGUAGE DataKinds #-} | 
| 9 | 10 | ||
| @@ -14,8 +15,7 @@ module Thermoprint.Server | |||
| 14 | , module Data.Default.Class | 15 | , module Data.Default.Class | 
| 15 | , module Servant.Server.Internal.Enter | 16 | , module Servant.Server.Internal.Enter | 
| 16 | , module Thermoprint.Server.Printer | 17 | , module Thermoprint.Server.Printer | 
| 17 | , module Thermoprint.Server.QMConfig | 18 | , module Thermoprint.Server.Queue | 
| 18 | , Queue(..), QueueEntry(..) | ||
| 19 | ) where | 19 | ) where | 
| 20 | 20 | ||
| 21 | import Data.Default.Class | 21 | import Data.Default.Class | 
| @@ -78,8 +78,7 @@ import Thermoprint.Server.Push | |||
| 78 | 78 | ||
| 79 | import Thermoprint.Server.Database | 79 | import Thermoprint.Server.Database | 
| 80 | import Thermoprint.Server.Printer | 80 | import Thermoprint.Server.Printer | 
| 81 | import Thermoprint.Server.Queue hiding (intersection, idQM, union, nullQM, runQM) | 81 | import Thermoprint.Server.Queue | 
| 82 | import Thermoprint.Server.QMConfig | ||
| 83 | import qualified Thermoprint.Server.API as API (thermoprintServer) | 82 | import qualified Thermoprint.Server.API as API (thermoprintServer) | 
| 84 | import Thermoprint.Server.API hiding (thermoprintServer) | 83 | import Thermoprint.Server.API hiding (thermoprintServer) | 
| 85 | 84 | ||
| @@ -89,9 +88,17 @@ import Debug.Trace | |||
| 89 | data Config m = Config { dyreError :: Maybe String -- ^ Set by 'Dyre' -- sent to log as an error | 88 | data Config m = Config { dyreError :: Maybe String -- ^ Set by 'Dyre' -- sent to log as an error | 
| 90 | , warpSettings :: Warp.Settings -- ^ Configure 'Warp's behaviour | 89 | , warpSettings :: Warp.Settings -- ^ Configure 'Warp's behaviour | 
| 91 | , printers :: Map API.PrinterId Printer | 90 | , printers :: Map API.PrinterId Printer | 
| 92 | , queueManagers :: API.PrinterId -> QMConfig | 91 | , queueManagers :: API.PrinterId -> QMConfig m | 
| 93 | } | 92 | } | 
| 94 | 93 | ||
| 94 | data QMConfig m = forall t. ( MonadTrans t | ||
| 95 | , MFunctor t | ||
| 96 | , Monad (t STM) | ||
| 97 | , MonadIO (t IO) | ||
| 98 | ) => QMConfig { manager :: QueueManager t | ||
| 99 | , collapse :: (t IO) :~> m | ||
| 100 | } | ||
| 101 | |||
| 95 | instance MonadIO m => Default (Config m) where | 102 | instance MonadIO m => Default (Config m) where | 
| 96 | def = Config { dyreError = Nothing | 103 | def = Config { dyreError = Nothing | 
| 97 | , warpSettings = Warp.defaultSettings | 104 | , warpSettings = Warp.defaultSettings | 
| @@ -99,7 +106,10 @@ instance MonadIO m => Default (Config m) where | |||
| 99 | , queueManagers = const def | 106 | , queueManagers = const def | 
| 100 | } | 107 | } | 
| 101 | 108 | ||
| 102 | withPrinters :: MonadResource m => Config m -> [(m PrinterMethod, QMConfig)] -> m (Config m) | 109 | instance MonadIO m => Default (QMConfig m) where | 
| 110 | def = QMConfig idQM $ Nat (liftIO . runIdentityT) | ||
| 111 | |||
| 112 | withPrinters :: MonadResource m => Config m -> [(m PrinterMethod, QMConfig m)] -> m (Config m) | ||
| 103 | -- ^ Add a list of printers to a 'Config' | 113 | -- ^ Add a list of printers to a 'Config' | 
| 104 | withPrinters cfg = fmap updateCfg . foldlM mapInsert (Map.mapWithKey (\k a -> (a, queueManagers cfg k)) $ printers cfg) | 114 | withPrinters cfg = fmap updateCfg . foldlM mapInsert (Map.mapWithKey (\k a -> (a, queueManagers cfg k)) $ printers cfg) | 
| 105 | where | 115 | where | 
| @@ -140,7 +150,7 @@ thermoprintServer dyre io = Dyre.wrapMain $ Dyre.defaultParams | |||
| 140 | gcChan <- liftIO newTChanIO | 150 | gcChan <- liftIO newTChanIO | 
| 141 | fork tMgr $ jobGC gcChan | 151 | fork tMgr $ jobGC gcChan | 
| 142 | let | 152 | let | 
| 143 | runQM' = liftIO . runQM gcChan . queueManagers | 153 | runQM' (queueManagers -> QMConfig qm nat) printer = unNat nat $ runQM gcChan qm printer | 
| 144 | mapM_ (fork tMgr . uncurry runQM') $ Map.toList printers | 154 | mapM_ (fork tMgr . uncurry runQM') $ Map.toList printers | 
| 145 | nChan <- liftIO $ newBroadcastTChanIO | 155 | nChan <- liftIO $ newBroadcastTChanIO | 
| 146 | let | 156 | let | 
| diff --git a/server/src/Thermoprint/Server/QMConfig.hs b/server/src/Thermoprint/Server/QMConfig.hs deleted file mode 100644 index 7255c8c..0000000 --- a/server/src/Thermoprint/Server/QMConfig.hs +++ /dev/null | |||
| @@ -1,129 +0,0 @@ | |||
| 1 | {-# LANGUAGE GADTs #-} | ||
| 2 | {-# LANGUAGE FlexibleContexts #-} | ||
| 3 | {-# LANGUAGE ImpredicativeTypes #-} | ||
| 4 | {-# LANGUAGE TypeOperators #-} | ||
| 5 | {-# LANGUAGE RecordWildCards #-} | ||
| 6 | {-# LANGUAGE ViewPatterns #-} | ||
| 7 | {-# LANGUAGE ConstraintKinds #-} | ||
| 8 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
| 9 | {-# LANGUAGE KindSignatures #-} | ||
| 10 | {-# LANGUAGE FlexibleInstances #-} | ||
| 11 | {-# LANGUAGE UndecidableInstances #-} | ||
| 12 | {-# LANGUAGE IncoherentInstances #-} | ||
| 13 | |||
| 14 | module Thermoprint.Server.QMConfig | ||
| 15 | ( QMConfig(..) | ||
| 16 | , nullQM | ||
| 17 | , idQM | ||
| 18 | , standardSleep | ||
| 19 | , limitHistorySize | ||
| 20 | , limitHistoryAge | ||
| 21 | ) where | ||
| 22 | |||
| 23 | import Control.Monad.Trans.Resource | ||
| 24 | import Control.Monad.Trans.Control | ||
| 25 | import Control.Monad.Trans.Identity | ||
| 26 | import Control.Monad.Logger | ||
| 27 | import Control.Monad.Reader | ||
| 28 | import Control.Monad.IO.Class | ||
| 29 | import Control.Monad.Morph | ||
| 30 | import Control.Monad.Trans.Compose | ||
| 31 | import Control.Monad.State | ||
| 32 | import Control.Category | ||
| 33 | import Servant.Server.Internal.Enter | ||
| 34 | |||
| 35 | import Data.Constraint | ||
| 36 | import Data.Constraint.Forall | ||
| 37 | |||
| 38 | import Data.Default.Class | ||
| 39 | |||
| 40 | import Data.Sequence (Seq) | ||
| 41 | import qualified Data.Sequence as Seq | ||
| 42 | import Data.Time | ||
| 43 | import Data.Foldable | ||
| 44 | |||
| 45 | import Control.Concurrent.STM | ||
| 46 | |||
| 47 | import Thermoprint.Server.Queue hiding (idQM, intersection, nullQM, union, runQM) | ||
| 48 | import qualified Thermoprint.Server.Queue as Q | ||
| 49 | |||
| 50 | import Thermoprint.Server.Database (JobId) | ||
| 51 | |||
| 52 | import Prelude hiding (length, maximum, id, (.)) | ||
| 53 | |||
| 54 | type QMTrans t = ( MonadTrans t | ||
| 55 | , MFunctor t | ||
| 56 | , Monad (t STM) | ||
| 57 | , MonadIO (t IO) | ||
| 58 | , Monad (QueueManagerM t) | ||
| 59 | , MonadState Queue (QueueManagerM t) | ||
| 60 | ) | ||
| 61 | |||
| 62 | class QMTrans1 (t :: (* -> *) -> * -> *) (t' :: (* -> *) -> * -> *) | ||
| 63 | instance QMTrans (ComposeT t t') => QMTrans1 t t' | ||
| 64 | |||
| 65 | type QMTrans' t = (QMTrans t, Forall (QMTrans1 t)) | ||
| 66 | |||
| 67 | data QMConfig where | ||
| 68 | QMConfig :: QMTrans' t => QueueManager t -> (forall m. (t m) :~> m) -> QMConfig | ||
| 69 | QMConfig' :: ( forall t . QMTrans' t => QueueManager t | ||
| 70 | ) -> QMConfig | ||
| 71 | |||
| 72 | instance Default QMConfig where | ||
| 73 | def = idQM | ||
| 74 | |||
| 75 | intersection :: Foldable f => f QMConfig -> QMConfig | ||
| 76 | intersection = foldr' (qmCombine Q.intersection) idQM | ||
| 77 | |||
| 78 | idQM :: QMConfig | ||
| 79 | idQM = QMConfig' Q.idQM | ||
| 80 | |||
| 81 | union :: Foldable f => f QMConfig -> QMConfig | ||
| 82 | union = foldr' (qmCombine Q.union) idQM | ||
| 83 | |||
| 84 | nullQM :: QMConfig | ||
| 85 | nullQM = QMConfig' Q.nullQM | ||
| 86 | |||
| 87 | qmCombine :: (forall f t. (Foldable f, MonadState Queue (QueueManagerM t)) => f (QueueManager t) -> QueueManager t) -> QMConfig -> QMConfig -> QMConfig | ||
| 88 | qmCombine qmCombine' (QMConfig a natA) (QMConfig b natB) = (QMConfig (qmCombine' [a', b']) (natComp natA natB)) \\ (inst :: Forall (QMTrans1 t) :- QMTrans (ComposeT t t')) | ||
| 89 | where | ||
| 90 | a' = mapComposeT (hoist $ ComposeT . hoist lift) a | ||
| 91 | b' = mapComposeT (hoist $ ComposeT . lift) b | ||
| 92 | |||
| 93 | natComp :: (forall m. t m :~> m) | ||
| 94 | -> (forall m. t' m :~> m) | ||
| 95 | -> (forall m. (MFunctor t | ||
| 96 | , Monad (t' m) | ||
| 97 | ) => ComposeT t t' m :~> m | ||
| 98 | ) | ||
| 99 | natComp natA natB = natA . hoistNat natB . Nat getComposeT | ||
| 100 | |||
| 101 | runQM :: ( HasQueue q ) => TChan JobId -> QMConfig -> q -> IO () | ||
| 102 | runQM gcChan (QMConfig qm nat) q = unNat nat $ Q.runQM gcChan qm q | ||
| 103 | runQM gcChan (QMConfig' qm) q = runIdentityT $ Q.runQM gcChan qm q | ||
| 104 | |||
| 105 | standardSleep :: Monad (QueueManagerM t) => QueueManager t | ||
| 106 | -- ^ Instruct 'runQM' to sleep some standard amount of time | ||
| 107 | -- | ||
| 108 | -- /TODO/: Investigate implementing a smarter algorithm (PID-controller) | ||
| 109 | standardSleep = return $ 20 | ||
| 110 | |||
| 111 | limitHistorySize :: Int -> QMConfig | ||
| 112 | -- ^ Limit a 'Queue's 'history's size to some number of 'QueueEntry's | ||
| 113 | limitHistorySize max = QMConfig' $ modify' limitSize >> standardSleep | ||
| 114 | where | ||
| 115 | limitSize :: Queue -> Queue | ||
| 116 | limitSize q@Queue{..} = q { history = Seq.take max history } | ||
| 117 | |||
| 118 | limitHistoryAge :: NominalDiffTime -- ^ Maximum age relative to the youngest 'QueueEntry' | ||
| 119 | -> QMConfig | ||
| 120 | -- ^ Limit a 'Queue's 'history's contents to 'QueueEntry's below a certain age | ||
| 121 | limitHistoryAge maxAge = QMConfig' $ modify' limitAge >> standardSleep | ||
| 122 | where | ||
| 123 | limitAge :: Queue -> Queue | ||
| 124 | limitAge q@Queue{..} = q { history = Seq.filter (filterAge . created . fst) history} | ||
| 125 | where | ||
| 126 | youngest :: UTCTime | ||
| 127 | youngest = maximum $ created . fst <$> history | ||
| 128 | filterAge :: UTCTime -> Bool | ||
| 129 | filterAge time = not $ (youngest `diffUTCTime` time) > maxAge | ||
