func (e *Experiment) OpenReport() (err error) {
	return e.OpenReportContext(context.Background())
}
OpenReport is an idempotent method to open a report. We assume that
you have configured the available probe services, either manually or
through using the session's MaybeLookupBackends method.
package engine
import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"os"
	"strconv"
	"time"
	"github.com/ooni/probe-engine/geolocate"
	"github.com/ooni/probe-engine/internal/platform"
	"github.com/ooni/probe-engine/model"
	"github.com/ooni/probe-engine/netx/bytecounter"
	"github.com/ooni/probe-engine/netx/dialer"
	"github.com/ooni/probe-engine/netx/httptransport"
	"github.com/ooni/probe-engine/probeservices"
	"github.com/ooni/probe-engine/resources"
	"github.com/ooni/probe-engine/version"
)
const dateFormat = "2006-01-02 15:04:05"
func formatTimeNowUTC() string {
	return time.Now().UTC().Format(dateFormat)
}
type Experiment struct {
	byteCounter   *bytecounter.Counter
	callbacks     model.ExperimentCallbacks
	measurer      model.ExperimentMeasurer
	report        probeservices.ReportChannel
	session       *Session
	testName      string
	testStartTime string
	testVersion   string
}
func NewExperiment(sess *Session, measurer model.ExperimentMeasurer) *Experiment {
	return &Experiment{
		byteCounter:   bytecounter.New(),
		callbacks:     model.NewPrinterCallbacks(sess.Logger()),
		measurer:      measurer,
		session:       sess,
		testName:      measurer.ExperimentName(),
		testStartTime: formatTimeNowUTC(),
		testVersion:   measurer.ExperimentVersion(),
	}
}
func (e *Experiment) KibiBytesReceived() float64 {
	return e.byteCounter.KibiBytesReceived()
}
func (e *Experiment) KibiBytesSent() float64 {
	return e.byteCounter.KibiBytesSent()
}
func (e *Experiment) Name() string {
	return e.testName
}
func (e *Experiment) GetSummaryKeys(m *model.Measurement) (interface{}, error) {
	return e.measurer.GetSummaryKeys(m)
}