564 lines
16 KiB
Go
564 lines
16 KiB
Go
// wrap7 project wrap7.go
|
|
package wrap7
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/albenik/bcd"
|
|
)
|
|
|
|
type DataTypeType string
|
|
|
|
const (
|
|
DTTint DataTypeType = "int"
|
|
DTTdint DataTypeType = "dint"
|
|
DTTreal DataTypeType = "real"
|
|
DTTdatetime DataTypeType = "date_and_time"
|
|
DTTdate DataTypeType = "date"
|
|
DTTtime DataTypeType = "time"
|
|
DTTbool DataTypeType = "bool"
|
|
DTTbyte DataTypeType = "byte"
|
|
DTTchar DataTypeType = "char"
|
|
DTTword DataTypeType = "word"
|
|
DTTdword DataTypeType = "dword"
|
|
)
|
|
|
|
// Gets Wordlength of S7-Type
|
|
/*
|
|
func MeasureSPSTypeLength(s string) int8 {
|
|
switch strings.ToLower(s) {
|
|
case "int":
|
|
return 2
|
|
case "dint":
|
|
return 4
|
|
case "real":
|
|
return 4
|
|
case "date_and_time":
|
|
return 8
|
|
case "date": // Nicht verifiziert
|
|
return 2
|
|
case "time": // Nicht verifiziert
|
|
return 4
|
|
case "bool":
|
|
return 1
|
|
case "char":
|
|
return 1
|
|
case "byte":
|
|
return 1
|
|
case "word": // Nicht verifiziert
|
|
return 2
|
|
case "dword": // Nicht verifiziert
|
|
return 4
|
|
default: // Nur im Fehlerfall
|
|
return -1
|
|
}
|
|
}
|
|
*/
|
|
func MeasureSPSTypeLength(s DataTypeType) int8 {
|
|
switch strings.ToLower(string(s)) {
|
|
case string(DTTint):
|
|
return 2
|
|
case string(DTTdint):
|
|
return 4
|
|
case string(DTTreal):
|
|
return 4
|
|
case string(DTTdatetime):
|
|
return 8
|
|
case string(DTTdate): // Nicht verifiziert
|
|
return 2
|
|
case string(DTTtime): // Nicht verifiziert
|
|
return 4
|
|
case string(DTTbool):
|
|
return 1
|
|
case string(DTTchar):
|
|
return 1
|
|
case string(DTTbyte):
|
|
return 1
|
|
case string(DTTword): // Nicht verifiziert
|
|
return 2
|
|
case string(DTTdword): // Nicht verifiziert
|
|
return 4
|
|
default: // Nur im Fehlerfall
|
|
return -1
|
|
}
|
|
}
|
|
|
|
// Sets the bit at pos in the integer n. pos = 0 is first.
|
|
func SetBit(n int, pos uint) int {
|
|
n |= (1 << pos)
|
|
return n
|
|
}
|
|
|
|
// Clears the bit at pos in n. pos = 0 is first.
|
|
func ClearBit(n int, pos uint) int {
|
|
mask := ^(1 << pos)
|
|
n &= mask
|
|
return n
|
|
}
|
|
|
|
// Check if bit at pos n is set. pos = 0 is first.
|
|
func HasBit(n int, pos uint) bool {
|
|
val := n & (1 << pos)
|
|
return (val > 0)
|
|
}
|
|
|
|
// Sets the bit at pos in the integer n. pos = 0 is first.
|
|
func Bit2Pos(n int) (pos uint) {
|
|
poss := uint(unsafe.Sizeof(^int(0)) * 8) //Number of Bytes
|
|
|
|
for p := uint(0); p < poss; p++ {
|
|
if n == 1<<p {
|
|
return p
|
|
}
|
|
}
|
|
|
|
panic("Bit-position not found")
|
|
}
|
|
|
|
// ... and reverse
|
|
func Pos2Bit(pos uint) (n int) {
|
|
n = 1 << pos
|
|
return
|
|
}
|
|
|
|
/*
|
|
* Datenkonversion zwischen SPS und PC
|
|
*
|
|
*/
|
|
|
|
func DBufferString(b *[]byte) string {
|
|
return strings.Trim(string(*b), " ")
|
|
}
|
|
func DBufferString2(b []byte) string {
|
|
return strings.Trim(string(b), " ")
|
|
}
|
|
func StringDBuffer(s *string) []byte {
|
|
return []byte(*s)
|
|
}
|
|
func StringDBuffer2(ß string) []byte {
|
|
return StringDBuffer(&ß)
|
|
}
|
|
func StringDBuffer3(ß string) *[]byte {
|
|
buf := StringDBuffer(&ß)
|
|
return &buf
|
|
}
|
|
|
|
func DBufferChar(b *[]byte) rune {
|
|
return rune((*b)[0])
|
|
}
|
|
func DBufferChar2(b []byte) rune {
|
|
return rune(b[0])
|
|
}
|
|
func DBufferChar3(b byte) rune {
|
|
return rune(b)
|
|
}
|
|
func CharDBuffer(c *rune) []byte {
|
|
return []byte{byte(*c)}
|
|
}
|
|
func CharDBuffer2(z rune) []byte {
|
|
return CharDBuffer(&z)
|
|
}
|
|
func CharDBuffer3(z rune) *[]byte {
|
|
buf := CharDBuffer(&z)
|
|
return &buf
|
|
}
|
|
|
|
func DBufferInt(b *[]byte) int16 {
|
|
return int16(binary.BigEndian.Uint16(*b))
|
|
}
|
|
func DBufferInt2(b []byte) int16 {
|
|
return int16(binary.BigEndian.Uint16(b))
|
|
}
|
|
func IntDBuffer(i *int16) []byte {
|
|
b := make([]byte, 2, 2)
|
|
binary.BigEndian.PutUint16(b, uint16(*i)) // Das Memory Layout ist bei uint32 und int32 identisch
|
|
return b
|
|
}
|
|
func IntDBuffer2(j int16) []byte {
|
|
return IntDBuffer(&j)
|
|
}
|
|
func IntDBuffer3(j int16) *[]byte {
|
|
buf := IntDBuffer(&j)
|
|
return &buf
|
|
}
|
|
|
|
func DBufferDInt(b *[]byte) int32 {
|
|
return int32(binary.BigEndian.Uint32(*b)) // Das Memory Layout ist bei uint32 und int32 identisch
|
|
}
|
|
func DBufferDInt2(b []byte) int32 {
|
|
return int32(binary.BigEndian.Uint32(b)) // Das Memory Layout ist bei uint32 und int32 identisch
|
|
}
|
|
func DIntDBuffer(i *int32) []byte {
|
|
b := make([]byte, 4, 4)
|
|
binary.BigEndian.PutUint32(b, uint32(*i)) // Das Memory Layout ist bei uint32 und int32 identisch
|
|
return b
|
|
}
|
|
func DIntDBuffer2(j int32) []byte {
|
|
return DIntDBuffer(&j)
|
|
}
|
|
func DIntDBuffer3(j int32) *[]byte {
|
|
buf := DIntDBuffer(&j)
|
|
return &buf
|
|
}
|
|
|
|
func DBufferReal(b *[]byte) float32 {
|
|
return float32(math.Float32frombits(binary.BigEndian.Uint32(*b)))
|
|
}
|
|
func DBufferReal2(b []byte) float32 {
|
|
return float32(math.Float32frombits(binary.BigEndian.Uint32(b)))
|
|
}
|
|
func RealDBuffer(r *float32) []byte {
|
|
b := make([]byte, 4, 4)
|
|
binary.BigEndian.PutUint32(b, math.Float32bits(*r))
|
|
return b
|
|
}
|
|
func RealDBuffer2(f float32) []byte {
|
|
return RealDBuffer(&f)
|
|
}
|
|
func RealDBuffer3(f float32) *[]byte {
|
|
buf := RealDBuffer(&f)
|
|
return &buf
|
|
}
|
|
|
|
func DBufferByte(b *[]byte) uint8 {
|
|
return uint8((*b)[0])
|
|
}
|
|
func DBufferByte2(b []byte) uint8 {
|
|
return uint8(b[0])
|
|
}
|
|
func DBufferByte3(b byte) uint8 {
|
|
return uint8(b)
|
|
}
|
|
func ByteDBuffer(i *uint8) []byte {
|
|
return []byte{*i}
|
|
}
|
|
func ByteDBuffer2(j uint8) []byte {
|
|
return ByteDBuffer(&j)
|
|
}
|
|
func ByteDBuffer3(j uint8) *[]byte {
|
|
buf := ByteDBuffer(&j)
|
|
return &buf
|
|
}
|
|
|
|
func DBufferWord(b *[]byte) uint16 {
|
|
return uint16(binary.BigEndian.Uint16(*b))
|
|
}
|
|
func DBufferWord2(b []byte) uint16 {
|
|
return uint16(binary.BigEndian.Uint16(b))
|
|
}
|
|
func WordDBuffer(i *uint16) []byte {
|
|
b := make([]byte, 2, 2)
|
|
binary.BigEndian.PutUint16(b, uint16(*i)) // Das Memory Layout ist bei uint32 und int32 identisch
|
|
return b
|
|
}
|
|
func WordDBuffer2(j uint16) []byte {
|
|
return WordDBuffer(&j)
|
|
}
|
|
func WordDBuffer3(j uint16) *[]byte {
|
|
buf := WordDBuffer(&j)
|
|
return &buf
|
|
}
|
|
|
|
func DBufferDWord(b *[]byte) uint32 {
|
|
return uint32(binary.BigEndian.Uint32(*b)) // Das Memory Layout ist bei uint32 und int32 identisch
|
|
}
|
|
func DBufferDWord2(b []byte) uint32 {
|
|
return uint32(binary.BigEndian.Uint32(b)) // Das Memory Layout ist bei uint32 und int32 identisch
|
|
}
|
|
func DWordDBuffer(i *uint32) []byte {
|
|
b := make([]byte, 4, 4)
|
|
binary.BigEndian.PutUint32(b, uint32(*i)) // Das Memory Layout ist bei uint32 und int32 identisch
|
|
return b
|
|
}
|
|
func DWordDBuffer2(j uint32) []byte {
|
|
return DWordDBuffer(&j)
|
|
}
|
|
func DWordDBuffer3(j uint32) *[]byte {
|
|
buf := DWordDBuffer(&j)
|
|
return &buf
|
|
}
|
|
|
|
/*
|
|
//Siehe https://en.wikipedia.org/wiki/Binary-coded_decimal und https://stackoverflow.com/questions/4494664/binary-coded-decimal-bcd-to-hexadecimal-conversion
|
|
func Bcd2int(bcd byte) int {
|
|
//return int((bcd & 0x0F) + ((bcd >> 4) * 10)) // gos7
|
|
return int((bcd & 0xF) + ((bcd&0xF0)>>4)*10)
|
|
|
|
}*/
|
|
/*
|
|
const (
|
|
NILDATE = "1999-11-30"
|
|
NILTIME = "00:00:00 +0000 UTC"
|
|
)
|
|
*/
|
|
|
|
// DBufferDateNTime wird für die drei Datentypen Date_and_Time, Date und Time benötigt
|
|
func DBufferDateNTime(b *[]byte) time.Time {
|
|
var ohYear, ohMonth, ohDay, ohHour, ohMinutes, ohSeconds, ohMilliseconds int
|
|
|
|
switch len(*b) {
|
|
default:
|
|
panic("Wrong length of byte-array in function DBufferDateNTime")
|
|
case 8: //Date und Time gesetzt
|
|
if Year := int(bcd.ToUint8((*b)[0])); Year < 90 {
|
|
ohYear = Year + 2000
|
|
} else {
|
|
ohYear = Year + 1900
|
|
}
|
|
//ohYear = int(int16(binary.BigEndian.Uint16((*b)[0:2]))) //int((*b)[0])*256 + int((*b)[1])
|
|
ohMonth = int(bcd.ToUint8((*b)[1]))
|
|
ohDay = int(bcd.ToUint8((*b)[2]))
|
|
ohHour = int(bcd.ToUint8((*b)[3]))
|
|
ohMinutes = int(bcd.ToUint8((*b)[4]))
|
|
ohSeconds = int(bcd.ToUint8((*b)[5]))
|
|
ohMilliseconds = 1e6 * ((int(bcd.ToUint8((*b)[6])) * 10) + (int(bcd.ToUint8((*b)[7])) / 10)) // Besten Dank an https://github.com/robinson/gos7/blob/master/helper.go, aber Achtung!!! Dort ist die Umrechnung der Millisekunden fehlerhaft, da die Multiplikation mit 1e6 fehlt.
|
|
//fmt.Println("DATENTIME")
|
|
case 5: // Time gesetzt, Date wird auf 0000-01-01 gesetzt
|
|
ohYear = 0
|
|
ohMonth = 1
|
|
ohDay = 1
|
|
ohHour = int(bcd.ToUint8((*b)[0]))
|
|
ohMinutes = int(bcd.ToUint8((*b)[1]))
|
|
ohSeconds = int(bcd.ToUint8((*b)[2]))
|
|
ohMilliseconds = 1e6 * ((int(bcd.ToUint8((*b)[3])) * 10) + (int(bcd.ToUint8((*b)[4])) / 10))
|
|
//fmt.Println("TIME")
|
|
case 3: // Date gesetzt, Time wird auf 00:00:00.000 gesetzt
|
|
if Year := int(bcd.ToUint8((*b)[0])); Year < 90 {
|
|
ohYear = Year + 2000
|
|
} else {
|
|
ohYear = Year + 1900
|
|
}
|
|
//ohYear = int(int16(binary.BigEndian.Uint16((*b)[0:2]))) //int((*b)[0])*256 + int((*b)[1])
|
|
ohMonth = int(bcd.ToUint8((*b)[1]))
|
|
ohDay = int(bcd.ToUint8((*b)[2]))
|
|
ohHour = 0
|
|
ohMinutes = 0
|
|
ohSeconds = 0
|
|
ohMilliseconds = 0 // Besten Dank an https://github.com/robinson/gos7/blob/master/helper.go, aber Achtung!!! Dort ist die Umrechnung der Millisekunden fehlerhaft, da die Multiplikation mit 1e6 fehlt.
|
|
//fmt.Println("DATE")
|
|
}
|
|
|
|
return time.Date(ohYear, time.Month(ohMonth), ohDay, ohHour, ohMinutes, ohSeconds, ohMilliseconds, time.UTC)
|
|
}
|
|
func DBufferDateNTime2(b []byte) time.Time {
|
|
var ohYear, ohMonth, ohDay, ohHour, ohMinutes, ohSeconds, ohMilliseconds int
|
|
|
|
switch len(b) {
|
|
default:
|
|
panic("Wrong length of byte-array in function DBufferDateNTime")
|
|
case 8: //Date und Time gesetzt
|
|
if Year := int(bcd.ToUint8(b[0])); Year < 90 {
|
|
ohYear = Year + 2000
|
|
} else {
|
|
ohYear = Year + 1900
|
|
}
|
|
//ohYear = int(int16(binary.BigEndian.Uint16((*b)[0:2]))) //int((*b)[0])*256 + int((*b)[1])
|
|
ohMonth = int(bcd.ToUint8(b[1]))
|
|
ohDay = int(bcd.ToUint8(b[2]))
|
|
ohHour = int(bcd.ToUint8(b[3]))
|
|
ohMinutes = int(bcd.ToUint8(b[4]))
|
|
ohSeconds = int(bcd.ToUint8(b[5]))
|
|
ohMilliseconds = 1e6 * ((int(bcd.ToUint8(b[6])) * 10) + (int(bcd.ToUint8(b[7])) / 10)) // Besten Dank an https://github.com/robinson/gos7/blob/master/helper.go, aber Achtung!!! Dort ist die Umrechnung der Millisekunden fehlerhaft, da die Multiplikation mit 1e6 fehlt.
|
|
//fmt.Println("DATENTIME")
|
|
case 5: // Time gesetzt, Date wird auf 0000-01-01 gesetzt
|
|
ohYear = 0
|
|
ohMonth = 1
|
|
ohDay = 1
|
|
ohHour = int(bcd.ToUint8(b[0]))
|
|
ohMinutes = int(bcd.ToUint8(b[1]))
|
|
ohSeconds = int(bcd.ToUint8(b[2]))
|
|
ohMilliseconds = 1e6 * ((int(bcd.ToUint8(b[3])) * 10) + (int(bcd.ToUint8(b[4])) / 10))
|
|
//fmt.Println("TIME")
|
|
case 3: // Date gesetzt, Time wird auf 00:00:00.000 gesetzt
|
|
if Year := int(bcd.ToUint8(b[0])); Year < 90 {
|
|
ohYear = Year + 2000
|
|
} else {
|
|
ohYear = Year + 1900
|
|
}
|
|
//ohYear = int(int16(binary.BigEndian.Uint16((*b)[0:2]))) //int((*b)[0])*256 + int((*b)[1])
|
|
ohMonth = int(bcd.ToUint8(b[1]))
|
|
ohDay = int(bcd.ToUint8(b[2]))
|
|
ohHour = 0
|
|
ohMinutes = 0
|
|
ohSeconds = 0
|
|
ohMilliseconds = 0 // Besten Dank an https://github.com/robinson/gos7/blob/master/helper.go, aber Achtung!!! Dort ist die Umrechnung der Millisekunden fehlerhaft, da die Multiplikation mit 1e6 fehlt.
|
|
//fmt.Println("DATE")
|
|
}
|
|
|
|
return time.Date(ohYear, time.Month(ohMonth), ohDay, ohHour, ohMinutes, ohSeconds, ohMilliseconds, time.UTC)
|
|
}
|
|
|
|
// DateNTimeDBuffer wird für die drei Datentypen Date_and_Time, Date und Time benötigt
|
|
func DateNTimeDBuffer(t *time.Time, strictZeroTime bool) (b []byte) {
|
|
|
|
ohYear := t.Year()
|
|
ohMonth := int(t.Month())
|
|
ohDay := t.Day()
|
|
ohHour := t.Hour()
|
|
ohMinutes := t.Minute()
|
|
ohSeconds := t.Second()
|
|
ohWeekday := int(t.Weekday()) + 1
|
|
ohNanoseconds := t.Nanosecond()
|
|
ohMillisecondsAA := int(int64(ohNanoseconds/1e6) / 10) // msh = First two digits of miliseconds
|
|
ohMillisecondsB := int(int64(ohNanoseconds/1e6) % 10) // msl = Last digit of miliseconds
|
|
|
|
if ohYear == 0 && ohMonth == 1 && ohDay == 1 {
|
|
b = make([]byte, 5, 5) // Nur Zeit gesetzt
|
|
//fmt.Println("Keine Jahresangabe")
|
|
b[0] = bcd.FromUint8(uint8(ohHour))
|
|
b[1] = bcd.FromUint8(uint8(ohMinutes))
|
|
b[2] = bcd.FromUint8(uint8(ohSeconds))
|
|
b[3] = bcd.FromUint8(uint8(ohMillisecondsAA))
|
|
b[4] = bcd.FromUint8(uint8(ohMillisecondsB*10 + ohWeekday))
|
|
} else if ohHour == 0 && ohMinutes == 0 && ohSeconds == 0 && ohNanoseconds == 0 && strictZeroTime == false { //wird strictZeroTime auf true gesetzt, dann sind wird die Zeit selbst dann als Zeit interpretiert, wenn die Uhrzeit 0:0:0.000 beträgt. Das ist prinzipiell möglich und würde gegebenfalls zu einer Fehlinterpretation des Zeitstempels führen.
|
|
b = make([]byte, 3, 3) // Nur Datum gesetzt
|
|
//fmt.Println("Keine Zeitangabe")
|
|
if ohYear < 2000 {
|
|
ohYear -= 1900
|
|
} else {
|
|
ohYear -= 2000
|
|
}
|
|
b[0] = bcd.FromUint8(uint8(ohYear))
|
|
b[1] = bcd.FromUint8(uint8(ohMonth))
|
|
b[2] = bcd.FromUint8(uint8(ohDay))
|
|
} else { // Datum und Zeit gesetzt
|
|
b = make([]byte, 8, 8)
|
|
if ohYear < 2000 { //Achtung! if y > 1999 {y -= 2000} bei https://github.com/robinson/gos7/blob/master/helper.go ist falsch, da die Subtraktion mit 1900 fehlt.
|
|
ohYear -= 1900
|
|
} else {
|
|
ohYear -= 2000
|
|
}
|
|
b[0] = bcd.FromUint8(uint8(ohYear))
|
|
b[1] = bcd.FromUint8(uint8(ohMonth))
|
|
b[2] = bcd.FromUint8(uint8(ohDay))
|
|
b[3] = bcd.FromUint8(uint8(ohHour))
|
|
b[4] = bcd.FromUint8(uint8(ohMinutes))
|
|
b[5] = bcd.FromUint8(uint8(ohSeconds))
|
|
b[6] = bcd.FromUint8(uint8(ohMillisecondsAA))
|
|
b[7] = bcd.FromUint8(uint8(ohMillisecondsB*10 + ohWeekday))
|
|
}
|
|
|
|
return b
|
|
}
|
|
func DateNTimeDBuffer2(d time.Time, strictZeroTime bool) []byte {
|
|
return DateNTimeDBuffer(&d, strictZeroTime)
|
|
}
|
|
func DateNTimeDBuffer3(d time.Time, strictZeroTime bool) *[]byte {
|
|
buf := DateNTimeDBuffer(&d, strictZeroTime)
|
|
return &buf
|
|
}
|
|
|
|
// ParseDateTime kann der Funktion DateNTimeDBuffer vorgeschaltet werden
|
|
func ParseDateTime(dts string) (time.Time, error) {
|
|
return time.Parse("DT#2006-1-2-15:04:05.000", dts)
|
|
}
|
|
|
|
// ParseDate kann der Funktion DateNTimeDBuffer vorgeschaltet werden
|
|
func ParseDate(ds string) (time.Time, error) {
|
|
return time.Parse("D#2006-1-2", strings.ReplaceAll(strings.ToUpper(ds), "DATE", "D"))
|
|
}
|
|
|
|
// ParseTime kann der Funktion DateNTimeDBuffer vorgeschaltet werden
|
|
func ParseTime(dt string) (time.Time, error) {
|
|
dt = strings.Replace(dt, ",", ".", 1) //TIME_OF_DAY#23:59:59,9 => TIME_OF_DAY#23:59:59.9
|
|
dt = strings.Replace(dt, "TIME_OF_DAY", "T", 1) //TIME_OF_DAY#23:59:59.9 => T#23:59:59.9
|
|
|
|
/*
|
|
re, err := regexp.Compile(`^(\w{1,}#)(\d{2})D_(\d{2})H_(\d{2})M_(\d{2})S_(\d{3})MS$`)
|
|
dt2 := re.FindStringSubmatch(dt)
|
|
//dt = dt2[1] + dt2[2] + ":" + dt2[3] + ":" + dt2[4] + ":" + dt2[5] + "." + dt2[6] //T#24D_20H_31M_23S_647MS => T#24:20:31:23.647
|
|
fmt.Println(dt2, err)
|
|
*/
|
|
dt = strings.Replace(dt, "MS", "", 1) //T#24D_20H_31M_23S_647MS => T#24D_20H_31M_23S_647
|
|
dt = strings.Replace(dt, "D_", ":", 1) //T#24D_20H_31M_23S_647 => T#24:20H_31M_23S_647
|
|
dt = strings.Replace(dt, "H_", ":", 1) //T#24:20H_31M_23S_647 => T#24:20:31M_23S_647
|
|
dt = strings.Replace(dt, "M_", ":", 1) //T#24:20:31M_23S_647 => T#24:20:31:23S_647
|
|
dt = strings.Replace(dt, "S_", ".", 1) //T#24:20:31:23S_647 => T#24:20:31:23.647
|
|
|
|
dt = strings.Replace(dt, "TOD", "T", 1) //TOD#23:59:59.999 => T#23:59:59.999
|
|
|
|
// Sorge dafür, daß am Ende immer ein Punkt, gefolgt von drei Stellen für die Millisekunden steht, da sonst der Parser ein falsches Format hat
|
|
if missingZeroes := 4 - (len(dt) - strings.Index(dt, ".")); missingZeroes > 0 {
|
|
dt += strings.Repeat("0", missingZeroes)
|
|
} else if missingZeroes < 0 {
|
|
dt += ".000"
|
|
}
|
|
|
|
return time.Parse("T#15:04:05.000", dt)
|
|
}
|
|
|
|
func ParseHexForByteWordAndDWord(hs string) (int, error) {
|
|
var a int64
|
|
|
|
// B#16#FF
|
|
// W#16#FFFF
|
|
// DW#16#FFFF FFFF oder DW#16#FFFFFFFF
|
|
// auch gültig: 16#89ABCDEF
|
|
|
|
hs = strings.ToLower( //2. bitte nur in Kleinbuchstaben, das vermindert die Komplexität des Regulären Ausdrucks
|
|
strings.ReplaceAll(hs, " ", "")) //1. entferne alle Leerzeichen
|
|
|
|
re, err := regexp.Compile(`^((b|w|dw)#)?(((16#)([0-9a-f]{1,8})|([0-1]{1,8})){1})$`)
|
|
|
|
d := re.FindStringSubmatch(hs)
|
|
|
|
if d[6] != "" {
|
|
a, _ = strconv.ParseInt(d[6], 16, 64)
|
|
} else if d[7] != "" {
|
|
a, _ = strconv.ParseInt(d[7], 2, 64)
|
|
} /*else {
|
|
panic("I should not happen")
|
|
}*/
|
|
//fmt.Println(a, d[6], d[7])
|
|
|
|
return int(a), err
|
|
}
|
|
|
|
func BfDBList(MyClient S7Object, startDB, endDB int) []int { //Bf = brute force, max-value enforced to envoid infinite loop
|
|
var dblist []int
|
|
for db := startDB; db <= endDB; db++ {
|
|
status, _ := DBRead(MyClient, db, 0, 1)
|
|
if status == 0 { //Valid DB
|
|
dblist = append(dblist, db)
|
|
}
|
|
}
|
|
return dblist
|
|
}
|
|
|
|
func BfDBgetSize(MyClient S7Object, dbnr int) int {
|
|
var interval = 1000
|
|
var lastvalidoffset = -1
|
|
|
|
for offset := 0; offset != lastvalidoffset; offset = offset + interval {
|
|
status, _ := DBRead(MyClient, dbnr, offset, interval)
|
|
if status == 0 {
|
|
lastvalidoffset = offset
|
|
} else {
|
|
//fmt.Println(offset, lastvalidoffset)
|
|
offset = lastvalidoffset
|
|
interval = interval / 2
|
|
}
|
|
}
|
|
|
|
return (lastvalidoffset + 1) // Achtung! +1 !!
|
|
}
|
|
|
|
func BfDBInfo(MyClient S7Object, maxdbnr int) {
|
|
dblist := BfDBList(MyClient, 1, maxdbnr)
|
|
|
|
for _, myDB := range dblist {
|
|
length := BfDBgetSize(MyClient, myDB)
|
|
|
|
fmt.Print("DB ", myDB, " (", length, " Bytes)", ": ")
|
|
fmt.Println(DBRead(MyClient, myDB, 0, length))
|
|
}
|
|
|
|
}
|