123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- package voltloggerParser
- import (
- "os"
- "io"
- "fmt"
- "strings"
- "encoding/binary"
- "devel.mephi.ru/dyokunev/voltlogger_parser/models"
- )
- const (
- WRITEBLOCK_SIZE int64 = 512
- WINDOW_SIZE int = 1 // Should be essentially less than 32768 (localTimestamp size/2)
- )
- type VoltloggerDumpRawHeader struct {
- Version byte
- Magic [11]byte
- Modificators byte // Bit: 1 — NoClock; 2 — Bitness8ADC; 4 — ZeroClockOnNewBlock; the rest is reserved
- ChannelsNum byte
- BlockWriteClockDelay byte
- Reserved0 [1]byte
- DeviceName [16]byte
- Reserved1 [480]byte
- }
- type VoltloggerDumpHeader struct {
- DeviceName [16]byte
- NoClock uint8
- Bitness8ADC uint8
- ZeroClockOnNewBlock uint8
- BlockWriteClockDelay int64
- ChannelsNum uint8
- }
- func makeWindow(r VoltloggerDumpHeader) (ret []models.VoltloggerRow) {
- ret = make([]models.VoltloggerRow, WINDOW_SIZE)
- /*
- for i:=0; i < WINDOW_SIZE; i++ {
- ret[i].Values = make([]int32, r.ChannelsNum)
- }
- */
- return ret
- }
- func pushPopWindow(window []models.VoltloggerRow, newRow models.VoltloggerRow) (oldRow models.VoltloggerRow) {
- oldestNum := 0
- for i:=0; i < WINDOW_SIZE; i++ {
- if (window[i].TimestampGlobal == 0) {
- window[i] = newRow
- continue
- }
- if (window[i].TimestampGlobal < window[oldestNum].TimestampGlobal) {
- oldestNum = i
- }
- }
- oldRow = window[oldestNum]
- window[oldestNum].TimestampGlobal = 0
- return oldRow
- }
- func get16(dumpFile *os.File) (r uint16, err error) {
- err = binary.Read(dumpFile, binary.LittleEndian, &r)
- return r, err
- }
- func get8(dumpFile *os.File) (r uint8, err error) {
- err = binary.Read(dumpFile, binary.LittleEndian, &r)
- return r, err
- }
- func ParseVoltloggerDump(dumpPath string, noHeaders bool, channelsNum uint8, adc8Bit bool, headerHandler func(VoltloggerDumpHeader, interface{})(error), rowHandler func(models.VoltloggerRow, VoltloggerDumpHeader, interface{})(error), arg interface{}) (err error) {
- var r VoltloggerDumpHeader
- // Openning the "dumpPath" as a file
- if (dumpPath == "-") {
- dumpPath = "/dev/stdin"
- }
- dumpFile, err := os.Open(dumpPath)
- if (err != nil) {
- return err
- }
- defer dumpFile.Close()
- if (noHeaders) {
- r.ChannelsNum = 1
- r.NoClock = 0
- } else {
- // Reading binary header to a VoltloggerDumpRawHeader
- var raw VoltloggerDumpRawHeader
- err = binary.Read(dumpFile, binary.LittleEndian, &raw)
- if (err != nil) {
- return fmt.Errorf("Cannot read dump: %v", err.Error())
- }
- // Checking if the data of a known type
- magicString := string(raw.Magic[:])
- if (magicString != "voltlogger\000") {
- return fmt.Errorf("The source is not a voltlogger dump (magic doesn't match: \"%v\" != \"voltlogger\")", magicString)
- }
- if (raw.Version != 0) {
- return fmt.Errorf("Unsupported dump version: %v", raw.Version)
- }
- if ((raw.Modificators & (0xff ^ 0x07)) != 0) {
- return fmt.Errorf("Unsupported modificators bitmask: %o %o", raw.Modificators)
- }
- if (raw.ChannelsNum == 0) {
- return fmt.Errorf("Channels number is zero")
- }
- // Filling the VoltloggerDump struct
- DeviceName := strings.Trim(string(raw.DeviceName[:]), "\000")
- copy(r.DeviceName[:], DeviceName)
- r.NoClock = raw.Modificators & 0x01
- r.Bitness8ADC = raw.Modificators & 0x02
- r.ZeroClockOnNewBlock = raw.Modificators & 0x04
- r.BlockWriteClockDelay = int64(raw.BlockWriteClockDelay)
- err = headerHandler(r, arg);
- if (err != nil) {
- return fmt.Errorf("Got an error from headerHandler: %v", err);
- }
- r.ChannelsNum = uint8(raw.ChannelsNum)
- }
- if (channelsNum > 0) {
- r.ChannelsNum = channelsNum
- }
- if (adc8Bit) {
- r.Bitness8ADC = 1;
- }
- // Parsing the Data
- var pos int64
- var timestampLocalOld uint16
- var row models.VoltloggerRow
- row.TimestampGlobal = -1
- window := makeWindow(r)
- for err = nil; err == nil; pos++ {
- row.TimestampLocal = 0;
- if (r.NoClock != 0) {
- row.TimestampGlobal++
- } else {
- row.TimestampLocal, err = get16(dumpFile)
- if (err != nil) {
- break
- }
- if (row.TimestampGlobal < 0) {
- row.TimestampGlobal = int64(row.TimestampLocal)
- } else {
- var timestampLocalDiff int
- timestampLocalDiff = int(row.TimestampLocal) - int(timestampLocalOld)
- /*if (timestampLocalDiff*timestampLocalDiff > TIMESTAMP_WINDOW*TIMESTAMP_WINDOW) {
- break
- }*/
- timestampLocalOld = uint16(row.TimestampLocal)
- if (timestampLocalDiff < 0) {
- if (r.ZeroClockOnNewBlock != 0) {
- timestampLocalDiff = 0
- } else {
- timestampLocalDiff += (1 << 16)
- }
- row.TimestampGlobal += r.BlockWriteClockDelay
- }
- row.TimestampGlobal += int64(timestampLocalDiff)
- }
- }
- row.Values = make([]int32, r.ChannelsNum)
- for i:=uint8(0); i < r.ChannelsNum; i++ {
- var value uint16
- if (r.Bitness8ADC != 0) {
- var value8 uint8
- value8, err = get8(dumpFile)
- value = uint16(value8)
- } else {
- value, err = get16(dumpFile)
- }
- if (err != nil) {
- break
- }
- row.Values[i] = int32(value)
- }
- row = pushPopWindow(window, row)
- if (row.TimestampGlobal == 0) { // If the window is not filled, yet
- continue
- }
- timestampLocalOld = row.TimestampLocal
- err = rowHandler(row, r, arg);
- if (err != nil) {
- return fmt.Errorf("Got an error from rowHandler: %v", err);
- }
- }
- if (err == io.EOF) {
- err = nil
- }
- return err
- }
|