go-hep.org/x/hep@v0.38.1/fwk/hbooksvc/hsvc.go (about)

     1  // Copyright ©2017 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package hbooksvc // import "go-hep.org/x/hep/fwk/hbooksvc"
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"reflect"
    11  	"strings"
    12  	"sync"
    13  
    14  	"go-hep.org/x/hep/fwk"
    15  	"go-hep.org/x/hep/fwk/fsm"
    16  	"go-hep.org/x/hep/hbook"
    17  	"go-hep.org/x/hep/rio"
    18  )
    19  
    20  type h1d struct {
    21  	fwk.H1D
    22  	mu sync.RWMutex
    23  }
    24  
    25  type h2d struct {
    26  	fwk.H2D
    27  	mu sync.RWMutex
    28  }
    29  
    30  type p1d struct {
    31  	fwk.P1D
    32  	mu sync.RWMutex
    33  }
    34  
    35  type s2d struct {
    36  	fwk.S2D
    37  	mu sync.RWMutex
    38  }
    39  
    40  type hsvc struct {
    41  	fwk.SvcBase
    42  
    43  	h1ds map[fwk.HID]*h1d
    44  	h2ds map[fwk.HID]*h2d
    45  	p1ds map[fwk.HID]*p1d
    46  	s2ds map[fwk.HID]*s2d
    47  
    48  	streams map[string]Stream
    49  	w       map[string]ostream
    50  	r       map[string]istream
    51  }
    52  
    53  func (svc *hsvc) Configure(ctx fwk.Context) error {
    54  	var err error
    55  
    56  	return err
    57  }
    58  
    59  func (svc *hsvc) StartSvc(ctx fwk.Context) error {
    60  	var err error
    61  
    62  	for name, stream := range svc.streams {
    63  		switch stream.Mode {
    64  		case Read:
    65  			_, dup := svc.r[name]
    66  			if dup {
    67  				return fmt.Errorf("%s: duplicate read-stream %q", svc.Name(), name)
    68  			}
    69  			// FIXME(sbinet): handle remote/local files + protocols
    70  			f, err := os.Open(stream.Name)
    71  			if err != nil {
    72  				return fmt.Errorf("error opening file [%s]: %w", stream.Name, err)
    73  			}
    74  			r, err := rio.NewReader(f)
    75  			if err != nil {
    76  				return fmt.Errorf("error opening rio-stream [%s]: %w", stream.Name, err)
    77  			}
    78  
    79  			svc.r[name] = istream{
    80  				name:  name,
    81  				fname: stream.Name,
    82  				f:     f,
    83  				r:     r,
    84  			}
    85  
    86  		case Write:
    87  			_, dup := svc.w[name]
    88  			if dup {
    89  				return fmt.Errorf("%s: duplicate write-stream %q", svc.Name(), name)
    90  			}
    91  			// FIXME(sbinet): handle remote/local files + protocols
    92  			f, err := os.Create(stream.Name)
    93  			if err != nil {
    94  				return fmt.Errorf("error creating file [%s]: %w", stream.Name, err)
    95  			}
    96  			w, err := rio.NewWriter(f)
    97  			if err != nil {
    98  				return fmt.Errorf("error creating rio-stream [%s]: %w", stream.Name, err)
    99  			}
   100  
   101  			svc.w[name] = ostream{
   102  				name:  name,
   103  				fname: stream.Name,
   104  				f:     f,
   105  				w:     w,
   106  			}
   107  
   108  		default:
   109  			return fmt.Errorf("%s: invalid stream mode (%d)", svc.Name(), stream.Mode)
   110  		}
   111  	}
   112  	return err
   113  }
   114  
   115  func (svc *hsvc) StopSvc(ctx fwk.Context) error {
   116  	var err error
   117  
   118  	errs := make([]error, 0, len(svc.r)+len(svc.w))
   119  
   120  	// closing write-streams
   121  	for n, w := range svc.w {
   122  
   123  		werr := w.write()
   124  		if werr != nil {
   125  			errs = append(errs, fmt.Errorf("error flushing %q: %w", n, werr))
   126  		}
   127  
   128  		werr = w.close()
   129  		if werr != nil {
   130  			errs = append(errs, fmt.Errorf("error closing %q: %w", n, werr))
   131  		}
   132  	}
   133  
   134  	// closing read-streams
   135  	for n, r := range svc.r {
   136  
   137  		rerr := r.close()
   138  		if rerr != nil {
   139  			errs = append(errs, fmt.Errorf("error closing %q: %w", n, rerr))
   140  		}
   141  	}
   142  
   143  	if len(errs) > 0 {
   144  		// FIXME(sbinet): return the complete list instead of the first one.
   145  		//                use an errlist.Error ?
   146  		return errs[0]
   147  	}
   148  	return err
   149  }
   150  
   151  func (svc *hsvc) BookH1D(name string, nbins int, low, high float64) (fwk.H1D, error) {
   152  	var err error
   153  	var h fwk.H1D
   154  
   155  	if !(fsm.Configured < svc.FSMState() && svc.FSMState() < fsm.Running) {
   156  		return h, fmt.Errorf("fwk: can not book histograms during FSM-state %v", svc.FSMState())
   157  	}
   158  
   159  	stream, hid := svc.split(name)
   160  	h = fwk.H1D{
   161  		ID:   fwk.HID(hid),
   162  		Hist: hbook.NewH1D(nbins, low, high),
   163  	}
   164  	h.Hist.Annotation()["name"] = svc.fullname(stream, hid)
   165  
   166  	switch stream {
   167  	case "":
   168  		// ok, temporary histo.
   169  	default:
   170  		sname := "/" + stream
   171  		str, ok := svc.streams[sname]
   172  		if !ok {
   173  			return h, fmt.Errorf("fwk: no stream [%s] declared", sname)
   174  		}
   175  		switch str.Mode {
   176  		case Read:
   177  			r, ok := svc.r[sname]
   178  			if !ok {
   179  				return h, fmt.Errorf("fwk: no read-stream [%s] declared", sname)
   180  			}
   181  			err = r.read(hid, h.Hist)
   182  			if err != nil {
   183  				return h, err
   184  			}
   185  
   186  			r.objs = append(r.objs, h)
   187  			svc.r[sname] = r
   188  
   189  		case Write:
   190  			w, ok := svc.w[sname]
   191  			if !ok {
   192  				return h, fmt.Errorf("fwk: no write-stream [%s] declared: %v", sname, svc.w)
   193  			}
   194  			w.objs = append(w.objs, h)
   195  			svc.w[sname] = w
   196  		default:
   197  			return h, fmt.Errorf("%s: invalid stream mode (%d)", svc.Name(), str.Mode)
   198  		}
   199  	}
   200  
   201  	hh := &h1d{H1D: h}
   202  	svc.h1ds[h.ID] = hh
   203  	return hh.H1D, err
   204  }
   205  
   206  func (svc *hsvc) BookH2D(name string, nx int, xmin, xmax float64, ny int, ymin, ymax float64) (fwk.H2D, error) {
   207  	var err error
   208  	var h fwk.H2D
   209  
   210  	if !(fsm.Configured < svc.FSMState() && svc.FSMState() < fsm.Running) {
   211  		return h, fmt.Errorf("fwk: can not book histograms during FSM-state %v", svc.FSMState())
   212  	}
   213  
   214  	stream, hid := svc.split(name)
   215  	h = fwk.H2D{
   216  		ID:   fwk.HID(hid),
   217  		Hist: hbook.NewH2D(nx, xmin, xmax, ny, ymin, ymax),
   218  	}
   219  	h.Hist.Annotation()["name"] = svc.fullname(stream, hid)
   220  
   221  	switch stream {
   222  	case "":
   223  		// ok, temporary histo.
   224  	default:
   225  		sname := "/" + stream
   226  		str, ok := svc.streams[sname]
   227  		if !ok {
   228  			return h, fmt.Errorf("fwk: no stream [%s] declared", sname)
   229  		}
   230  		switch str.Mode {
   231  		case Read:
   232  			r, ok := svc.r[sname]
   233  			if !ok {
   234  				return h, fmt.Errorf("fwk: no read-stream [%s] declared", sname)
   235  			}
   236  			err = r.read(hid, h.Hist)
   237  			if err != nil {
   238  				return h, err
   239  			}
   240  
   241  			r.objs = append(r.objs, h)
   242  			svc.r[sname] = r
   243  
   244  		case Write:
   245  			w, ok := svc.w[sname]
   246  			if !ok {
   247  				return h, fmt.Errorf("fwk: no write-stream [%s] declared: %v", sname, svc.w)
   248  			}
   249  			w.objs = append(w.objs, h)
   250  			svc.w[sname] = w
   251  		default:
   252  			return h, fmt.Errorf("%s: invalid stream mode (%d)", svc.Name(), str.Mode)
   253  		}
   254  	}
   255  
   256  	hh := &h2d{H2D: h}
   257  	svc.h2ds[h.ID] = hh
   258  	return hh.H2D, err
   259  }
   260  
   261  func (svc *hsvc) BookP1D(name string, nbins int, low, high float64) (fwk.P1D, error) {
   262  	var err error
   263  	var h fwk.P1D
   264  
   265  	if !(fsm.Configured < svc.FSMState() && svc.FSMState() < fsm.Running) {
   266  		return h, fmt.Errorf("fwk: can not book histograms during FSM-state %v", svc.FSMState())
   267  	}
   268  
   269  	stream, hid := svc.split(name)
   270  	h = fwk.P1D{
   271  		ID:      fwk.HID(hid),
   272  		Profile: hbook.NewP1D(nbins, low, high),
   273  	}
   274  	h.Profile.Annotation()["name"] = svc.fullname(stream, hid)
   275  
   276  	switch stream {
   277  	case "":
   278  		// ok, temporary histo.
   279  	default:
   280  		sname := "/" + stream
   281  		str, ok := svc.streams[sname]
   282  		if !ok {
   283  			return h, fmt.Errorf("fwk: no stream [%s] declared", sname)
   284  		}
   285  		switch str.Mode {
   286  		case Read:
   287  			r, ok := svc.r[sname]
   288  			if !ok {
   289  				return h, fmt.Errorf("fwk: no read-stream [%s] declared", sname)
   290  			}
   291  			err = r.read(hid, h.Profile)
   292  			if err != nil {
   293  				return h, err
   294  			}
   295  
   296  			r.objs = append(r.objs, h)
   297  			svc.r[sname] = r
   298  
   299  		case Write:
   300  			w, ok := svc.w[sname]
   301  			if !ok {
   302  				return h, fmt.Errorf("fwk: no write-stream [%s] declared: %v", sname, svc.w)
   303  			}
   304  			w.objs = append(w.objs, h)
   305  			svc.w[sname] = w
   306  		default:
   307  			return h, fmt.Errorf("%s: invalid stream mode (%d)", svc.Name(), str.Mode)
   308  		}
   309  	}
   310  
   311  	hh := &p1d{P1D: h}
   312  	svc.p1ds[h.ID] = hh
   313  	return hh.P1D, err
   314  }
   315  
   316  func (svc *hsvc) BookS2D(name string) (fwk.S2D, error) {
   317  	var err error
   318  	var h fwk.S2D
   319  
   320  	if !(fsm.Configured < svc.FSMState() && svc.FSMState() < fsm.Running) {
   321  		return h, fmt.Errorf("fwk: can not book histograms during FSM-state %v", svc.FSMState())
   322  	}
   323  
   324  	stream, hid := svc.split(name)
   325  	h = fwk.S2D{
   326  		ID:      fwk.HID(hid),
   327  		Scatter: hbook.NewS2D(),
   328  	}
   329  	h.Scatter.Annotation()["name"] = svc.fullname(stream, hid)
   330  
   331  	switch stream {
   332  	case "":
   333  		// ok, temporary histo.
   334  	default:
   335  		sname := "/" + stream
   336  		str, ok := svc.streams[sname]
   337  		if !ok {
   338  			return h, fmt.Errorf("fwk: no stream [%s] declared", sname)
   339  		}
   340  		switch str.Mode {
   341  		case Read:
   342  			r, ok := svc.r[sname]
   343  			if !ok {
   344  				return h, fmt.Errorf("fwk: no read-stream [%s] declared", sname)
   345  			}
   346  			err = r.read(hid, h.Scatter)
   347  			if err != nil {
   348  				return h, err
   349  			}
   350  
   351  			r.objs = append(r.objs, h)
   352  			svc.r[sname] = r
   353  
   354  		case Write:
   355  			w, ok := svc.w[sname]
   356  			if !ok {
   357  				return h, fmt.Errorf("fwk: no write-stream [%s] declared: %v", sname, svc.w)
   358  			}
   359  			w.objs = append(w.objs, h)
   360  			svc.w[sname] = w
   361  		default:
   362  			return h, fmt.Errorf("%s: invalid stream mode (%d)", svc.Name(), str.Mode)
   363  		}
   364  	}
   365  
   366  	hh := &s2d{S2D: h}
   367  	svc.s2ds[h.ID] = hh
   368  	return hh.S2D, err
   369  }
   370  
   371  func (svc *hsvc) fullname(stream, hid string) string {
   372  	if stream == "" {
   373  		return hid
   374  	}
   375  	return stream + "/" + hid
   376  }
   377  
   378  // split splits a booking histo name into (stream-name, histo-name).
   379  //
   380  // eg: "/my-stream/histo" -> ("my-stream", "histo")
   381  //
   382  //	"my-stream/histo"  -> ("my-stream", "histo")
   383  //	"my-stream/histo/" -> ("my-stream", "histo")
   384  //	"/histo"           -> ("",          "histo")
   385  //	"histo"            -> ("",          "histo")
   386  func (svc *hsvc) split(n string) (string, string) {
   387  
   388  	n = strings.TrimPrefix(n, "/")
   389  	n = strings.TrimSuffix(n, "/")
   390  
   391  	o := strings.Split(n, "/")
   392  	switch len(o) {
   393  	case 0:
   394  		panic("impossible")
   395  	case 1:
   396  		return "", o[0]
   397  	case 2:
   398  		return o[0], o[1]
   399  	default:
   400  		return o[0], strings.Join(o[1:], "/")
   401  	}
   402  }
   403  
   404  func (svc *hsvc) FillH1D(id fwk.HID, x, w float64) {
   405  	h := svc.h1ds[id]
   406  	h.mu.Lock()
   407  	h.Hist.Fill(x, w)
   408  	h.mu.Unlock()
   409  }
   410  
   411  func (svc *hsvc) FillH2D(id fwk.HID, x, y, w float64) {
   412  	h := svc.h2ds[id]
   413  	h.mu.Lock()
   414  	h.Hist.Fill(x, y, w)
   415  	h.mu.Unlock()
   416  }
   417  
   418  func (svc *hsvc) FillP1D(id fwk.HID, x, y, w float64) {
   419  	h := svc.p1ds[id]
   420  	h.mu.Lock()
   421  	h.Profile.Fill(x, y, w)
   422  	h.mu.Unlock()
   423  }
   424  
   425  func (svc *hsvc) FillS2D(id fwk.HID, x, y float64) {
   426  	h := svc.s2ds[id]
   427  	h.mu.Lock()
   428  	// FIXME(sbinet): weight?
   429  	h.Scatter.Fill(hbook.Point2D{X: x, Y: y})
   430  	h.mu.Unlock()
   431  }
   432  
   433  func newhsvc(typ, name string, mgr fwk.App) (fwk.Component, error) {
   434  	var err error
   435  	svc := &hsvc{
   436  		SvcBase: fwk.NewSvc(typ, name, mgr),
   437  		streams: map[string]Stream{},
   438  		w:       map[string]ostream{},
   439  		r:       map[string]istream{},
   440  		h1ds:    make(map[fwk.HID]*h1d),
   441  		h2ds:    make(map[fwk.HID]*h2d),
   442  		p1ds:    make(map[fwk.HID]*p1d),
   443  		s2ds:    make(map[fwk.HID]*s2d),
   444  	}
   445  
   446  	err = svc.DeclProp("Streams", &svc.streams)
   447  	if err != nil {
   448  		return nil, err
   449  	}
   450  	return svc, err
   451  }
   452  
   453  func init() {
   454  	fwk.Register(reflect.TypeOf(hsvc{}), newhsvc)
   455  }
   456  
   457  var _ fwk.HistSvc = (*hsvc)(nil)