aboutsummaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2016-01-31 15:03:57 +0000
committerGregor Kleen <gkleen@yggdrasil.li>2016-01-31 15:03:57 +0000
commit44a6279b86deecc865f05d2ee519f64f39ac1ccb (patch)
treee2634312eee0c99b383520e0877c33ece32102ee /server/src
parent2914fd9d66265080dbb38aed61ef8aad77b5ec2c (diff)
downloadthermoprint-44a6279b86deecc865f05d2ee519f64f39ac1ccb.tar
thermoprint-44a6279b86deecc865f05d2ee519f64f39ac1ccb.tar.gz
thermoprint-44a6279b86deecc865f05d2ee519f64f39ac1ccb.tar.bz2
thermoprint-44a6279b86deecc865f05d2ee519f64f39ac1ccb.tar.xz
thermoprint-44a6279b86deecc865f05d2ee519f64f39ac1ccb.zip
Recording job creation time in printer queues
Diffstat (limited to 'server/src')
-rw-r--r--server/src/Thermoprint/Server/API.hs41
-rw-r--r--server/src/Thermoprint/Server/Printer.hs67
2 files changed, 66 insertions, 42 deletions
diff --git a/server/src/Thermoprint/Server/API.hs b/server/src/Thermoprint/Server/API.hs
index 4d036ce..add771a 100644
--- a/server/src/Thermoprint/Server/API.hs
+++ b/server/src/Thermoprint/Server/API.hs
@@ -2,7 +2,6 @@
2{-# LANGUAGE FlexibleContexts #-} 2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE TemplateHaskell #-} 3{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE OverloadedStrings #-} 4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE TupleSections #-}
6 5
7module Thermoprint.Server.API 6module Thermoprint.Server.API
8 ( ProtoHandler, Handler 7 ( ProtoHandler, Handler
@@ -59,6 +58,8 @@ import Data.Acquire (with)
59 58
60import Control.Monad.Catch (handle, catch) 59import Control.Monad.Catch (handle, catch)
61 60
61import Data.Time
62
62type ProtoHandler = ReaderT HandlerInput (LoggingT (ResourceT IO)) 63type ProtoHandler = ReaderT HandlerInput (LoggingT (ResourceT IO))
63type Handler = EitherT ServantErr ProtoHandler 64type Handler = EitherT ServantErr ProtoHandler
64 65
@@ -116,18 +117,18 @@ queue' :: MonadIO m => Printer -> m Queue
116-- ^ Call 'queue' and handle concurrency 117-- ^ Call 'queue' and handle concurrency
117queue' = fmap force . liftIO . readTVarIO . queue 118queue' = fmap force . liftIO . readTVarIO . queue
118 119
119extractJobs :: (PrinterId, Queue) -> Seq (API.JobId, JobStatus) 120extractJobs :: (PrinterId, Queue) -> Seq (API.JobId, UTCTime, JobStatus)
120-- ^ Get an API-compatible list of all jobs from a 'Printer' 'Queue' 121-- ^ Get an API-compatible list of all jobs from a 'Printer' 'Queue'
121extractJobs (pId, Queue pending current history) = mconcat [ fmap ((, Queued pId) . castId) pending 122extractJobs (pId, Queue pending current history) = mconcat [ fmap (\e -> (castId $ jobId e, created e, Queued pId)) pending
122 , maybe Seq.empty Seq.singleton $ fmap ((, Printing pId) . castId) current 123 , maybe Seq.empty Seq.singleton $ fmap (\e -> (castId $ jobId e, created e, Printing pId)) current
123 , fmap (bimap castId $ maybe Done Failed) history 124 , fmap (\(e, s) -> (castId $ jobId e, created e, maybe Done Failed $ s)) history
124 ] 125 ]
125 126
126listPrinters :: Handler (Map PrinterId PrinterStatus) 127listPrinters :: Handler (Map PrinterId PrinterStatus)
127listPrinters = fmap toStatus <$> (mapM (liftIO . readTVarIO . queue) . printers =<< ask) 128listPrinters = fmap toStatus <$> (mapM (liftIO . readTVarIO . queue) . printers =<< ask)
128 where 129 where
129 toStatus (Queue _ Nothing _) = Available 130 toStatus (Queue _ Nothing _) = Available
130 toStatus (Queue _ (Just id) _) = Busy . castId $ fromSqlKey id 131 toStatus (Queue _ (Just id) _) = Busy . castId $ jobId id
131 132
132queueJob :: Maybe PrinterId -> Printout -> Handler API.JobId 133queueJob :: Maybe PrinterId -> Printout -> Handler API.JobId
133queueJob pId printout = lift . fmap castId . withReaderT sqlPool . addToQueue printout . snd =<< lookupPrinter pId 134queueJob pId printout = lift . fmap castId . withReaderT sqlPool . addToQueue printout . snd =<< lookupPrinter pId
@@ -136,31 +137,39 @@ printerStatus :: PrinterId -> Handler PrinterStatus
136printerStatus = fmap queueToStatus . queue' . snd <=< lookupPrinter . Just 137printerStatus = fmap queueToStatus . queue' . snd <=< lookupPrinter . Just
137 where 138 where
138 queueToStatus (Queue _ Nothing _) = Available 139 queueToStatus (Queue _ Nothing _) = Available
139 queueToStatus (Queue _ (Just id) _) = Busy $ castId id 140 queueToStatus (Queue _ (Just c) _) = Busy . castId $ jobId c
140 141
141listJobs :: Maybe PrinterId -> Maybe API.JobId -> Maybe API.JobId -> Handler (Seq (API.JobId, JobStatus)) 142listJobs :: Maybe PrinterId
142listJobs Nothing minId maxId = fmap mconcat . mapM (\pId -> listJobs (Just pId) minId maxId) =<< asks (Map.keys . printers) 143 -> Maybe API.JobId -> Maybe API.JobId
143listJobs pId minId maxId = fmap (filterJobs . extractJobs) . (\(a, b) -> (,) a <$> queue' b) =<< lookupPrinter pId 144 -> Maybe UTCTime -> Maybe UTCTime
145 -> Handler (Seq (API.JobId, UTCTime, JobStatus))
146listJobs Nothing minId maxId minTime maxTime = fmap mconcat . mapM (\pId -> listJobs (Just pId) minId maxId minTime maxTime) =<< asks (Map.keys . printers)
147listJobs pId minId maxId minTime maxTime = fmap (filterJobs . extractJobs) . (\(a, b) -> (,) a <$> queue' b) =<< lookupPrinter pId
144 where 148 where
145 filterJobs = Seq.filter (\(id, _) -> maybe True (< id) minId && maybe True (> id) maxId) 149 filterJobs = Seq.filter (\(id, time, _) -> and [ maybe True (<= id) minId
150 , maybe True (>= id) maxId
151 , maybe True (<= time) minTime
152 , maybe True (>= time) maxTime
153 ]
154 )
146 155
147getJob :: API.JobId -> Handler Printout 156getJob :: API.JobId -> Handler Printout
148getJob jobId = fmap jobContent . maybe (left err404) return =<< runSqlPool (get $ castId jobId) =<< asks sqlPool 157getJob jobId = fmap jobContent . maybe (left err404) return =<< runSqlPool (get $ castId jobId) =<< asks sqlPool
149 158
150jobStatus :: API.JobId -> Handler JobStatus 159jobStatus :: API.JobId -> Handler JobStatus
151jobStatus jobId = maybe (left err404) return . lookup jobId . toList =<< listJobs Nothing Nothing Nothing 160jobStatus jobId = maybe (left err404) return . lookup jobId . map (\(id, _, st) -> (id, st)) . toList =<< listJobs Nothing Nothing Nothing Nothing Nothing
152 161
153abortJob :: API.JobId -> Handler () 162abortJob :: API.JobId -> Handler ()
154abortJob jobId = do 163abortJob needle = do
155 printerIds <- asks (Map.keys . printers) 164 printerIds <- asks (Map.keys . printers)
156 found <- fmap or . forM printerIds $ \pId -> do 165 found <- fmap or . forM printerIds $ \pId -> do
157 (pId', p) <- lookupPrinter $ Just pId 166 (pId', p) <- lookupPrinter $ Just pId
158 found <- liftIO . atomically $ do 167 found <- liftIO . atomically $ do
159 current@(Queue pending _ _) <- readTVar $ queue p 168 current@(Queue pending _ _) <- readTVar $ queue p
160 let filtered = Seq.filter (/= castId jobId) pending 169 let filtered = Seq.filter ((/= castId needle) . jobId) pending
161 writeTVar (queue p) $ current { pending = filtered } 170 writeTVar (queue p) $ current { pending = filtered }
162 return . not $ ((==) `on` length) pending filtered 171 return . not $ ((==) `on` length) pending filtered
163 when found . $(logInfo) $ "Removed job #" <> (T.pack $ show (castId jobId :: Integer)) <> " from " <> (T.pack . show $ pId') 172 when found . $(logInfo) $ "Removed job #" <> (T.pack $ show (castId needle :: Integer)) <> " from " <> (T.pack . show $ pId')
164 return found 173 return found
165 when (not found) $ left err404 174 when (not found) $ left err404
166 175
diff --git a/server/src/Thermoprint/Server/Printer.hs b/server/src/Thermoprint/Server/Printer.hs
index 67180c4..7f41430 100644
--- a/server/src/Thermoprint/Server/Printer.hs
+++ b/server/src/Thermoprint/Server/Printer.hs
@@ -12,38 +12,41 @@ module Thermoprint.Server.Printer
12 ( PrinterMethod(..), Printer(..) 12 ( PrinterMethod(..), Printer(..)
13 , printer 13 , printer
14 , Queue(..) 14 , Queue(..)
15 , QueueEntry(..)
15 , runPrinter 16 , runPrinter
16 , addToQueue 17 , addToQueue
17 ) where 18 ) where
18 19
19import Thermoprint.API (PrintingError(..), Printout) 20import Thermoprint.API (PrintingError(..), Printout)
20import qualified Thermoprint.API as API (JobStatus(..)) 21import qualified Thermoprint.API as API (JobStatus(..))
21 22
22import Thermoprint.Server.Database 23import Thermoprint.Server.Database
23 24
24import Database.Persist 25import Database.Persist
25import Database.Persist.Sql 26import Database.Persist.Sql
26 27
27import Data.Sequence (Seq, ViewL(..), viewl, (<|)) 28import Data.Sequence (Seq, ViewL(..), viewl, (<|), (|>))
28import qualified Data.Sequence as Seq 29import qualified Data.Sequence as Seq
29import Data.Map (Map) 30import Data.Map (Map)
30import qualified Data.Map as Map 31import qualified Data.Map as Map
31 32
32import qualified Data.Text as T (pack) 33import qualified Data.Text as T (pack)
33 34
34import Data.Typeable (Typeable) 35import Data.Typeable (Typeable)
35import GHC.Generics (Generic) 36import GHC.Generics (Generic)
36import Control.DeepSeq 37import Control.DeepSeq
37import Data.Default.Class 38import Data.Default.Class
38 39
39import Control.Monad.Trans.Resource 40import Control.Monad.Trans.Resource
40import Control.Monad.IO.Class 41import Control.Monad.IO.Class
41import Control.Monad.Logger 42import Control.Monad.Logger
42import Control.Monad.Reader 43import Control.Monad.Reader
43 44
44import Control.Monad (forever) 45import Control.Monad (forever)
45 46
46import Control.Concurrent.STM 47import Control.Concurrent.STM
48
49import Data.Time.Clock
47 50
48newtype PrinterMethod = PM { unPM :: forall m. (MonadResource m, MonadLogger m) => Printout -> m (Maybe PrintingError) } 51newtype PrinterMethod = PM { unPM :: forall m. (MonadResource m, MonadLogger m) => Printout -> m (Maybe PrintingError) }
49 52
@@ -52,11 +55,11 @@ data Printer = Printer
52 , queue :: TVar Queue 55 , queue :: TVar Queue
53 } 56 }
54 57
55-- | Zipper for 'Seq JobId' 58-- | Zipper for 'Seq QueueEntry'
56data Queue = Queue 59data Queue = Queue
57 { pending :: Seq JobId -- ^ Pending jobs, closest first 60 { pending :: Seq QueueEntry -- ^ Pending jobs, closest last
58 , current :: Maybe JobId 61 , current :: Maybe QueueEntry
59 , history :: Seq (JobId, Maybe PrintingError) -- ^ Completed jobs, closest first 62 , history :: Seq (QueueEntry, Maybe PrintingError) -- ^ Completed jobs, closest first
60 } 63 }
61 deriving (Typeable, Generic, NFData) 64 deriving (Typeable, Generic, NFData)
62 65
@@ -67,6 +70,12 @@ instance Default Queue where
67 , history = Seq.empty 70 , history = Seq.empty
68 } 71 }
69 72
73data QueueEntry = QueueEntry
74 { jobId :: JobId
75 , created :: UTCTime
76 }
77 deriving (Typeable, Generic, NFData)
78
70printer :: MonadResource m => m PrinterMethod -> m Printer 79printer :: MonadResource m => m PrinterMethod -> m Printer
71printer p = Printer <$> p <*> liftIO (newTVarIO def) 80printer p = Printer <$> p <*> liftIO (newTVarIO def)
72 81
@@ -80,13 +89,13 @@ runPrinter :: ( MonadReader ConnectionPool m
80 ) => Printer -> m () 89 ) => Printer -> m ()
81-- ^ Loop 'forever' pushing entries from the 'Queue' associated to a 'Printer' into its 'print'-method 90-- ^ Loop 'forever' pushing entries from the 'Queue' associated to a 'Printer' into its 'print'-method
82runPrinter Printer{..} = forever $ do 91runPrinter Printer{..} = forever $ do
83 jobId <- atomically' $ do 92 entry@(QueueEntry{..}) <- atomically' $ do
84 (Queue queuePending Nothing history) <- readTVar queue 93 (Queue queuePending Nothing history) <- readTVar queue
85 case viewl queuePending of 94 case viewl queuePending of
86 EmptyL -> retry 95 EmptyL -> retry
87 (jobId :< remaining) -> do 96 (current :< remaining) -> do
88 writeTVar queue $!! Queue remaining (Just jobId) history 97 writeTVar queue $!! Queue remaining (Just current) history
89 return jobId 98 return current
90 job <- runSqlPool (get jobId) =<< ask 99 job <- runSqlPool (get jobId) =<< ask
91 case job of 100 case job of
92 Nothing -> do 101 Nothing -> do
@@ -96,7 +105,7 @@ runPrinter Printer{..} = forever $ do
96 $(logInfo) . T.pack $ "Printing " ++ show (unSqlBackendKey . unJobKey $ jobId) 105 $(logInfo) . T.pack $ "Printing " ++ show (unSqlBackendKey . unJobKey $ jobId)
97 printReturn <- (unPM print) (jobContent job) -- We could, at this point, do some exception handling. It was decided that this would be undesirable, because we really don't have any idea what exceptions to catch 106 printReturn <- (unPM print) (jobContent job) -- We could, at this point, do some exception handling. It was decided that this would be undesirable, because we really don't have any idea what exceptions to catch
98 maybe (return ()) ($(logWarn) . T.pack . (("Error while printing " ++ show (unSqlBackendKey . unJobKey $ jobId) ++ ": ") ++) . show) $ printReturn 107 maybe (return ()) ($(logWarn) . T.pack . (("Error while printing " ++ show (unSqlBackendKey . unJobKey $ jobId) ++ ": ") ++) . show) $ printReturn
99 atomically' $ modifyTVar' queue (\Queue{..} -> force . Queue pending Nothing $ (jobId, printReturn) <| history) 108 atomically' $ modifyTVar' queue (\Queue{..} -> force . Queue pending Nothing $ (entry, printReturn) <| history)
100 109
101addToQueue :: ( MonadReader ConnectionPool m 110addToQueue :: ( MonadReader ConnectionPool m
102 , MonadLogger m 111 , MonadLogger m
@@ -105,6 +114,12 @@ addToQueue :: ( MonadReader ConnectionPool m
105 ) => Printout -> Printer -> m JobId 114 ) => Printout -> Printer -> m JobId
106addToQueue printout Printer{..} = do 115addToQueue printout Printer{..} = do
107 jobId <- runSqlPool (insert $ Job printout) =<< ask 116 jobId <- runSqlPool (insert $ Job printout) =<< ask
117 time <- liftIO getCurrentTime
118 let
119 entry = QueueEntry
120 { jobId = jobId
121 , created = time
122 }
108 $(logInfo) . T.pack $ "Queueing " ++ show (unSqlBackendKey . unJobKey $ jobId) 123 $(logInfo) . T.pack $ "Queueing " ++ show (unSqlBackendKey . unJobKey $ jobId)
109 atomically' $ modifyTVar' queue (\Queue{..} -> force $ Queue (jobId <| pending) current history) 124 atomically' $ modifyTVar' queue (\Queue{..} -> force $ Queue (pending |> entry) current history)
110 return jobId 125 return jobId