diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-31 15:03:57 +0000 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-31 15:03:57 +0000 |
commit | 44a6279b86deecc865f05d2ee519f64f39ac1ccb (patch) | |
tree | e2634312eee0c99b383520e0877c33ece32102ee /server/src | |
parent | 2914fd9d66265080dbb38aed61ef8aad77b5ec2c (diff) | |
download | thermoprint-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.hs | 41 | ||||
-rw-r--r-- | server/src/Thermoprint/Server/Printer.hs | 67 |
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 | ||
7 | module Thermoprint.Server.API | 6 | module Thermoprint.Server.API |
8 | ( ProtoHandler, Handler | 7 | ( ProtoHandler, Handler |
@@ -59,6 +58,8 @@ import Data.Acquire (with) | |||
59 | 58 | ||
60 | import Control.Monad.Catch (handle, catch) | 59 | import Control.Monad.Catch (handle, catch) |
61 | 60 | ||
61 | import Data.Time | ||
62 | |||
62 | type ProtoHandler = ReaderT HandlerInput (LoggingT (ResourceT IO)) | 63 | type ProtoHandler = ReaderT HandlerInput (LoggingT (ResourceT IO)) |
63 | type Handler = EitherT ServantErr ProtoHandler | 64 | type 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 |
117 | queue' = fmap force . liftIO . readTVarIO . queue | 118 | queue' = fmap force . liftIO . readTVarIO . queue |
118 | 119 | ||
119 | extractJobs :: (PrinterId, Queue) -> Seq (API.JobId, JobStatus) | 120 | extractJobs :: (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' |
121 | extractJobs (pId, Queue pending current history) = mconcat [ fmap ((, Queued pId) . castId) pending | 122 | extractJobs (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 | ||
126 | listPrinters :: Handler (Map PrinterId PrinterStatus) | 127 | listPrinters :: Handler (Map PrinterId PrinterStatus) |
127 | listPrinters = fmap toStatus <$> (mapM (liftIO . readTVarIO . queue) . printers =<< ask) | 128 | listPrinters = 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 | ||
132 | queueJob :: Maybe PrinterId -> Printout -> Handler API.JobId | 133 | queueJob :: Maybe PrinterId -> Printout -> Handler API.JobId |
133 | queueJob pId printout = lift . fmap castId . withReaderT sqlPool . addToQueue printout . snd =<< lookupPrinter pId | 134 | queueJob pId printout = lift . fmap castId . withReaderT sqlPool . addToQueue printout . snd =<< lookupPrinter pId |
@@ -136,31 +137,39 @@ printerStatus :: PrinterId -> Handler PrinterStatus | |||
136 | printerStatus = fmap queueToStatus . queue' . snd <=< lookupPrinter . Just | 137 | printerStatus = 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 | ||
141 | listJobs :: Maybe PrinterId -> Maybe API.JobId -> Maybe API.JobId -> Handler (Seq (API.JobId, JobStatus)) | 142 | listJobs :: Maybe PrinterId |
142 | listJobs Nothing minId maxId = fmap mconcat . mapM (\pId -> listJobs (Just pId) minId maxId) =<< asks (Map.keys . printers) | 143 | -> Maybe API.JobId -> Maybe API.JobId |
143 | listJobs 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)) | ||
146 | listJobs Nothing minId maxId minTime maxTime = fmap mconcat . mapM (\pId -> listJobs (Just pId) minId maxId minTime maxTime) =<< asks (Map.keys . printers) | ||
147 | listJobs 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 | ||
147 | getJob :: API.JobId -> Handler Printout | 156 | getJob :: API.JobId -> Handler Printout |
148 | getJob jobId = fmap jobContent . maybe (left err404) return =<< runSqlPool (get $ castId jobId) =<< asks sqlPool | 157 | getJob jobId = fmap jobContent . maybe (left err404) return =<< runSqlPool (get $ castId jobId) =<< asks sqlPool |
149 | 158 | ||
150 | jobStatus :: API.JobId -> Handler JobStatus | 159 | jobStatus :: API.JobId -> Handler JobStatus |
151 | jobStatus jobId = maybe (left err404) return . lookup jobId . toList =<< listJobs Nothing Nothing Nothing | 160 | jobStatus jobId = maybe (left err404) return . lookup jobId . map (\(id, _, st) -> (id, st)) . toList =<< listJobs Nothing Nothing Nothing Nothing Nothing |
152 | 161 | ||
153 | abortJob :: API.JobId -> Handler () | 162 | abortJob :: API.JobId -> Handler () |
154 | abortJob jobId = do | 163 | abortJob 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 | ||
19 | import Thermoprint.API (PrintingError(..), Printout) | 20 | import Thermoprint.API (PrintingError(..), Printout) |
20 | import qualified Thermoprint.API as API (JobStatus(..)) | 21 | import qualified Thermoprint.API as API (JobStatus(..)) |
21 | 22 | ||
22 | import Thermoprint.Server.Database | 23 | import Thermoprint.Server.Database |
23 | 24 | ||
24 | import Database.Persist | 25 | import Database.Persist |
25 | import Database.Persist.Sql | 26 | import Database.Persist.Sql |
26 | 27 | ||
27 | import Data.Sequence (Seq, ViewL(..), viewl, (<|)) | 28 | import Data.Sequence (Seq, ViewL(..), viewl, (<|), (|>)) |
28 | import qualified Data.Sequence as Seq | 29 | import qualified Data.Sequence as Seq |
29 | import Data.Map (Map) | 30 | import Data.Map (Map) |
30 | import qualified Data.Map as Map | 31 | import qualified Data.Map as Map |
31 | 32 | ||
32 | import qualified Data.Text as T (pack) | 33 | import qualified Data.Text as T (pack) |
33 | 34 | ||
34 | import Data.Typeable (Typeable) | 35 | import Data.Typeable (Typeable) |
35 | import GHC.Generics (Generic) | 36 | import GHC.Generics (Generic) |
36 | import Control.DeepSeq | 37 | import Control.DeepSeq |
37 | import Data.Default.Class | 38 | import Data.Default.Class |
38 | 39 | ||
39 | import Control.Monad.Trans.Resource | 40 | import Control.Monad.Trans.Resource |
40 | import Control.Monad.IO.Class | 41 | import Control.Monad.IO.Class |
41 | import Control.Monad.Logger | 42 | import Control.Monad.Logger |
42 | import Control.Monad.Reader | 43 | import Control.Monad.Reader |
43 | 44 | ||
44 | import Control.Monad (forever) | 45 | import Control.Monad (forever) |
45 | 46 | ||
46 | import Control.Concurrent.STM | 47 | import Control.Concurrent.STM |
48 | |||
49 | import Data.Time.Clock | ||
47 | 50 | ||
48 | newtype PrinterMethod = PM { unPM :: forall m. (MonadResource m, MonadLogger m) => Printout -> m (Maybe PrintingError) } | 51 | newtype 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' |
56 | data Queue = Queue | 59 | data 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 | ||
73 | data QueueEntry = QueueEntry | ||
74 | { jobId :: JobId | ||
75 | , created :: UTCTime | ||
76 | } | ||
77 | deriving (Typeable, Generic, NFData) | ||
78 | |||
70 | printer :: MonadResource m => m PrinterMethod -> m Printer | 79 | printer :: MonadResource m => m PrinterMethod -> m Printer |
71 | printer p = Printer <$> p <*> liftIO (newTVarIO def) | 80 | printer 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 |
82 | runPrinter Printer{..} = forever $ do | 91 | runPrinter 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 | ||
101 | addToQueue :: ( MonadReader ConnectionPool m | 110 | addToQueue :: ( 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 |
106 | addToQueue printout Printer{..} = do | 115 | addToQueue 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 |