@ -1,10 +1,15 @@
package bitmask
import (
"encoding/binary"
"math"
"regexp"
"strconv"
"strings"
"time"
"unsafe"
"github.com/albenik/bcd"
)
type DataTypeType string
@ -23,33 +28,37 @@ const (
DTTdword DataTypeType = "dword"
)
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
// 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 ) :
@ -116,3 +125,396 @@ 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
}