https://github.com/pmiddend/hs-tango
Haskell bindings to the Tango controls system
https://github.com/pmiddend/hs-tango
haskell tango-controls
Last synced: 4 months ago
JSON representation
Haskell bindings to the Tango controls system
- Host: GitHub
- URL: https://github.com/pmiddend/hs-tango
- Owner: pmiddend
- License: mit
- Created: 2023-08-06T08:03:31.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2025-03-07T09:47:28.000Z (over 1 year ago)
- Last Synced: 2025-05-12T22:46:17.947Z (about 1 year ago)
- Topics: haskell, tango-controls
- Language: Haskell
- Homepage: https://hackage.haskell.org/package/hs-tango
- Size: 451 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.org
- Changelog: Changelog
- License: LICENSE
Awesome Lists containing this project
README
* hs-tango
[[https://www.gnu.org/licenses/gpl-3.0][https://img.shields.io/badge/License-GPLv3-blue.svg]]
[[Hackage][https://img.shields.io/hackage/v/hs-tango.svg]]
[[CI][https://github.com/pmiddend/hs-tango/actions/workflows/build-with-ubuntu.yaml/badge.svg]]
** How to install
*** General instructions
A simple =cabal install hs-tango= should suffice to build and install the library, as you would for any other Haskell library. Of course, you need to have the development packages for Tango (namely "cpptango") installed.
The [[https://tango-controls.readthedocs.io/en/latest/installation/tango-on-linux.html#debian-ubuntu][Tango documentation]] has instructions on how to install Tango for different operating systems. On *Debian* and *Ubuntu*, for example, you can do =apt install libtango-dev=.
*** With Nix
If you're using the Nix package manager, you have to enable flakes, and then use the accompanying =flake.nix=. To get Tango, it uses the [[https://gitlab.desy.de/cfel-sc-public/tango-flake][tango-flake]] from your truly. You can simply clone the repository, do =nix develop=, and then =cabal build=.
** Usage
*** Client, reading attributes
There is haddock documentation available. But just to illustrate how to use the library, here's a little client example:
#+begin_src haskell
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Tango.Client(
parseTangoUrl,
withDeviceProxy,
getTimeout,
AttributeName(..),
readBoolAttribute,
readBoolSpectrumAttribute
)
main :: IO ()
main =
case parseTangoUrl "sys/tg_test/1" of
Left e -> error "couldn't resolve tango URL"
Right deviceAddress -> withDeviceProxy deviceAddress $ \proxy -> do
timeout <- getTimeout proxy
putStrLn $ "proxy timeout is " <> show timeout
booleanResult <- readBoolAttribute proxy (AttributeName "boolean_scalar")
putStrLn $ "boolean_scalar is " <> show booleanResult
booleanResultBetter <- readBoolAttribute proxy (AttributeName "boolean_scalar")
putStrLn $ "boolean_scalar better is " <> show booleanResultBetter
booleanSpectrumResult <- readBoolSpectrumAttribute proxy (AttributeName "boolean_spectrum")
putStrLn $ "boolean_spectrum is " <> show booleanSpectrumResult
#+end_src
*** Client, subscribing to events
In this example, we are subscribing to changes in the attribute =boolean_scalar= and waiting for two changes (the =forM_= loop).
#+begin_src haskell
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.Concurrent (newChan, readChan, writeChan)
import Control.Monad (forM_)
import Control.Monad.IO.Class (liftIO)
import Data.Int (Int16)
import Data.Text.IO qualified as TIO
import Tango.Client
main =
case parseTangoUrl "sys/tg_test/1" of
Left e -> error "couldn't resolve tango URL"
Right deviceAddress -> withDeviceProxy deviceAddress \proxy -> do
timeout <- getTimeout proxy
chan <- newChan
let attributeName = AttributeName "boolean_scalar"
eventCallback attribute bool = liftIO do
boolValue <- readBoolAttribute proxy attributeName
putStrLn $ "got a change: " <> show boolValue
writeChan chan True
pollAttribute proxy attributeName (Milliseconds 2000)
withSubscribedEvent proxy attributeName ChangeEvent False eventCallback do
forM_ [0 .. 2] \_ -> do
putStrLn "waiting for change"
_ <- readChan chan
putStrLn "received change"
#+end_src
*** Example: Web Astor
Under =benchmark= in the =.cabal= file, you will find =web-astor=, which is a web-based implementation of the Astor starter overlay. It runs on =localhost:8081= and displays a HTML version of Astor, asking for a Tango host. The code is short and should be fairly easy to grasp. A screenshot of that should explain it well:
[[https://raw.githubusercontent.com/pmiddend/hs-tango/main/doc-assets/web-astor.png]]
The example uses Servant and Lucid to render the HTML.
** What's missing
- Server: Everything
- Client
+ Setting advanced attribute properties, specifically event-related ones like =rel_change= and the period for periodic events. This is no technical difficulty, you just have to write the code.
+ Commands returning or receiving an =UInt32=. I'm not sure how to handle this, since tango has no such data type.
+ In event callbacks, it would be good to transport more information like the list of errors, not just a boolean (see cppTango's =event.h=)
+ Dynamic attribute properties such as the "quality" (currently we're only exporting read/write value)
** Rationale for the used dependencies
- =derive-storable= so that we can just declare data types as records, and have C =Storable= instances generated
- =text= to replace =String= for textual data (=ByteString= might have been an option, we haven't decided yet)
- =unliftio= to have the opssibility to have code running in =MonadIO=
** Interesting links/documentation
- [[https://www.esrf.fr/computing/cs/tango/tango_doc/kernel_doc/cpp_doc/classTango_1_1DeviceProxy.html][DeviceProxy C++ API reference]]