updated lock server

This commit is contained in:
yurii 2025-06-18 11:54:29 +01:00
parent d9871e6a1c
commit b002c80b01
6 changed files with 104 additions and 23 deletions

View File

@ -10,11 +10,11 @@ import (
) )
// Build key encoding request command for the Assa Abloy lock server. // Build key encoding request command for the Assa Abloy lock server.
func (lock *AssaLockServer) BuildCommand(encoderAddr, lockId string, checkIn, checkOut time.Time) error { func (lock *AssaLockServer) BuildCommand(lockId string, checkIn, checkOut time.Time) error {
ci := checkIn.Format("200601021504") ci := checkIn.Format("200601021504")
co := checkOut.Format("200601021504") co := checkOut.Format("200601021504")
lock.command = fmt.Sprintf("CCA;EA%s;GR%s;CO%s;CI%s;AM1;\r\n", encoderAddr, lockId, co, ci) lock.command = fmt.Sprintf("CCA;EA%s;GR%s;CO%s;CI%s;AM1;\r\n", lock.encoderAddr, lockId, co, ci)
return nil return nil
} }

View File

@ -19,19 +19,33 @@ const (
type ( type (
LockServer interface { LockServer interface {
BuildCommand(lockId string, checkIn, checkOut time.Time) error
LockSequence(conn net.Conn) error LockSequence(conn net.Conn) error
BuildCommand(encoderAddr, lockId string, checkIn, checkOut time.Time) error
} }
AssaLockServer struct { AssaLockServer struct {
encoderAddr string
command string command string
} }
OmniLockServer struct { OmniLockServer struct {
encoderAddr string // Encoder address for the lock server
command []byte // Command to be sent to the lock server command []byte // Command to be sent to the lock server
} }
) )
func NewLockServer(lockType, encoderAddr string, fatalError func(error)) LockServer {
switch lockType {
case AssaAbloy:
return &AssaLockServer{encoderAddr: encoderAddr}
case Omnitec:
return &OmniLockServer{encoderAddr: encoderAddr}
default:
fatalError(fmt.Errorf("unsupported LockType: %s; must be 'assaabloy' or 'omnitec'", lockType))
return nil // This line will never be reached, but is needed to satisfy the compiler
}
}
func InitializeServerConnection(LockserverUrl string) (net.Conn, error) { func InitializeServerConnection(LockserverUrl string) (net.Conn, error) {
const funcName = "InitializeServerConnection" const funcName = "InitializeServerConnection"
// Parse the URL to extract host and port // Parse the URL to extract host and port
@ -46,7 +60,7 @@ func InitializeServerConnection(LockserverUrl string) (net.Conn, error) {
// Establish a TCP connection to the Visionline server // Establish a TCP connection to the Visionline server
conn, err := net.Dial("tcp", address) conn, err := net.Dial("tcp", address)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to connect to Visionline server: %v", err) return nil, fmt.Errorf("failed to connect to lock server: %v", err)
} }
return conn, nil return conn, nil
} }

View File

@ -13,7 +13,7 @@ import (
) )
// Build key encoding request command for the Omnitec lock server. // Build key encoding request command for the Omnitec lock server.
func (lock *OmniLockServer) BuildCommand(encoderAddr, lockId string, checkIn, checkOut time.Time) error { func (lock *OmniLockServer) BuildCommand(lockId string, checkIn, checkOut time.Time) error {
const funcName = "OmniLockServer.BuildCommand" const funcName = "OmniLockServer.BuildCommand"
hostname, err := os.Hostname() hostname, err := os.Hostname()
if err != nil { if err != nil {
@ -36,7 +36,7 @@ func (lock *OmniLockServer) BuildCommand(encoderAddr, lockId string, checkIn, ch
// Construct payload // Construct payload
payload := fmt.Sprintf( payload := fmt.Sprintf(
"KR|KC%s|KTD|RN%s|%s|DT%s|G#75|GA%s|GD%s|KO0000|DA%s|TI%s|", "KR|KC%s|KTD|RN%s|%s|DT%s|G#75|GA%s|GD%s|KO0000|DA%s|TI%s|",
encoderAddr, lock.encoderAddr,
formattedLockId, formattedLockId,
hostname, hostname,
dt, dt,

27
main.go
View File

@ -53,10 +53,17 @@ type DoorCardRequest struct {
type App struct { type App struct {
dispPort *serial.Port dispPort *serial.Port
lockConn net.Conn lockConn net.Conn
config configRec
lockserver lockserver.LockServer lockserver lockserver.LockServer
} }
func newApp(dispPort *serial.Port, lockConn net.Conn, config configRec) *App {
return &App{
dispPort: dispPort,
lockConn: lockConn,
lockserver: lockserver.NewLockServer(config.LockType, config.EncoderAddress, fatalError),
}
}
func main() { func main() {
// Load config // Load config
config := readConfig() config := readConfig()
@ -101,21 +108,7 @@ func main() {
log.Infof("Connected to lock server at %s", config.LockserverUrl) log.Infof("Connected to lock server at %s", config.LockserverUrl)
// Create App and wire routes // Create App and wire routes
app := &App{ app := newApp(dispHandle, lockConn, config)
dispPort: dispHandle,
lockConn: lockConn,
config: config,
}
switch config.LockType {
case lockserver.AssaAbloy:
app.lockserver = &lockserver.AssaLockServer{}
case lockserver.Omnitec:
app.lockserver = &lockserver.OmniLockServer{}
default:
err = fmt.Errorf("unsupported LockType: %s; must be 'assaabloy' or 'omnitec'", config.LockType)
fatalError(err)
}
mux := http.NewServeMux() mux := http.NewServeMux()
setUpRoutes(app, mux) setUpRoutes(app, mux)
@ -238,7 +231,7 @@ func (app *App) issueDoorCard(w http.ResponseWriter, r *http.Request) {
} }
// build lock server command // build lock server command
app.lockserver.BuildCommand(app.config.EncoderAddress, doorReq.RoomField, checkIn, checkOut) app.lockserver.BuildCommand(doorReq.RoomField, checkIn, checkOut)
// lock server sequence // lock server sequence
err = app.lockserver.LockSequence(app.lockConn) err = app.lockserver.LockSequence(app.lockConn)

42
payment/creditcall.go Normal file
View File

@ -0,0 +1,42 @@
package payment
import (
"fmt"
"net"
"time"
)
// StartTransaction sends a start transaction XML to ChipDNA Server
func (pc *PaymentClient) StartTransaction(amountMinorUnits int, reference string) (string, error) {
conn, err := net.Dial("tcp", pc.Addr)
if err != nil {
return "", fmt.Errorf("failed to connect: %w", err)
}
defer conn.Close()
// Set timeout
conn.SetDeadline(time.Now().Add(15 * time.Second))
// Format XML request
request := fmt.Sprintf(`
<StartTransaction>
<Amount>%d</Amount>
<AmountType>Actual</AmountType>
<Reference>%s</Reference>
<TransactionType>Sale</TransactionType>
</StartTransaction>`, amountMinorUnits, reference)
_, err = conn.Write([]byte(request))
if err != nil {
return "", fmt.Errorf("failed to write to server: %w", err)
}
// Read response
buff := make([]byte, 4096)
n, err := conn.Read(buff)
if err != nil {
return "", fmt.Errorf("failed to read from server: %w", err)
}
return string(buff[:n]), nil
}

32
payment/paymentcommon.go Normal file
View File

@ -0,0 +1,32 @@
package payment
import (
"net"
"net/url"
"fmt"
"strings"
)
// PaymentClient holds connection data
type PaymentClient struct {
Addr string // e.g., "127.0.0.1:1869"
}
func InitializeConnection(paymentrUrl string) (net.Conn, error) {
const funcName = "InitializeServerConnection"
// Parse the URL to extract host and port
parsedUrl, err := url.Parse(paymentrUrl)
if err != nil {
return nil, fmt.Errorf("[%s] failed to parse LockserverUrl: %v", funcName, err)
}
// Remove any leading/trailing slashes just in case
address := strings.Trim(parsedUrl.Host, "/")
// Establish a TCP connection to the Visionline server
conn, err := net.Dial("tcp", address)
if err != nil {
return nil, fmt.Errorf("failed to connect to payment server: %v", err)
}
return conn, nil
}