Skip to content

Commit

Permalink
Switch to windows user interface
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanp0 committed Jan 22, 2022
1 parent 7a27a75 commit 57ff75d
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 79 deletions.
4 changes: 4 additions & 0 deletions cmd/departureboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

## Attribution

Bulletin board icons created by catkuro - [Flaticon](https://www.flaticon.com/free-icons/bulletin-board
152 changes: 73 additions & 79 deletions cmd/departureboard/departureboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import (
"net/http"
"os"
"path/filepath"
"runtime"
"sort"

"github.com/go-stomp/stomp/v3"
"github.com/pkg/browser"
"github.com/sqweek/dialog"

"github.com/jonathanp0/go-simsig/gateway"
"github.com/jonathanp0/go-simsig/wttxml"
Expand All @@ -37,71 +36,15 @@ var pass = flag.String("pass", "", "SimSig License Password(optional)")
var helpFlag = flag.Bool("help", false, "Print help text")
var stop = make(chan bool)

func main() {
flag.Parse()
if *helpFlag {
fmt.Fprintf(os.Stderr, "Usage of %s\n", os.Args[0])
flag.PrintDefaults()
os.Exit(1)
}

//Open File Dialog for WTT
var filename string
if *wttFile != "" {
filename = *wttFile
} else {
var err error
filename, err = dialog.File().Title("Select Active SimSig WTT").SetStartDir("C:\\Users\\Public\\Documents\\SimSig\\Timetables").Filter("SimSig WTT(*.WTT)", "wtt").Load()
if err != nil {
println("No WTT specified on command line or in dialog")
os.Exit(1)
}
}

println("Reading WTT File ", filename, "...")

//Read WTT
data, err := wttxml.ReadSavedTimetable(filename)
if err != nil {
println("Error reading WTT: " + err.Error())
os.Exit(1)
}

//Build stop list from WTT
var wtt wttxml.SimSigTimetable
err = xml.Unmarshal(data, &wtt)
if err != nil {
println("WTT Parsing Error: " + err.Error())
os.Exit(1)
}

locations := buildSortedLocationList(wtt.Timetables.Timetable)
//global variables
var locations []string
var stopsAtLocations map[string]*LocationStopList

stopsAtLocations := buildLocationStopList(locations, wtt.Timetables.Timetable)

for _, locStops := range stopsAtLocations {
sort.Sort(locStops)
}

//Iniate STOMP Connection

subscribed := make(chan bool)

//Global state variables
var currentClock gateway.ClockMsg

println("Connecting to SimSig at ", *serverAddr, "...")
go recvMessages(&currentClock, stopsAtLocations, subscribed)

// wait until we know the receiver has subscribed
<-subscribed
func main() {

println("Connected. Launched web interface att http://localhost:8090/")
go webInterface(locations, stopsAtLocations, &currentClock)
browser.OpenURL("http://localhost:8090/")
runtime.LockOSThread()
runWindowsUI()

//run indefinitely
<-stop
}

//Gateway Message Processing
Expand Down Expand Up @@ -153,34 +96,53 @@ func processDelayMessage(m *gateway.TrainDelay, locations LocationStopListMap) {
}
}

func gatewayConnection(user string, password string, address string) {
//Iniate STOMP Connection
subscribed := make(chan bool)

//Global state variables
var currentClock gateway.ClockMsg

go recvMessages(&currentClock, stopsAtLocations, subscribed, user, password, address)

// wait until we know the receiver has subscribed
<-subscribed

go webInterface(locations, stopsAtLocations, &currentClock)
webInterfaceReady()

//run indefinitely
<-stop
}

//Main communication thread for Interface Gateway
func recvMessages(clock *gateway.ClockMsg, locations LocationStopListMap, subscribed chan bool) {
func recvMessages(clock *gateway.ClockMsg, locations LocationStopListMap, subscribed chan bool, user string, pass string, serverAddr string) {
defer func() {
stop <- true
}()

//login credentials
var options []func(*stomp.Conn) error = []func(*stomp.Conn) error{}

if *user != "" {
options = append(options, stomp.ConnOpt.Login(*user, *pass))
if user != "" {
options = append(options, stomp.ConnOpt.Login(user, pass))
}

conn, err := stomp.Dial("tcp", *serverAddr, options...)
conn, err := stomp.Dial("tcp", serverAddr, options...)

if err != nil {
println("cannot connect to server", err.Error())
updateStatus("cannot connect to server: " + err.Error())
return
}

subMvt, err := conn.Subscribe(movementQueueName, stomp.AckAuto)
if err != nil {
println("cannot subscribe to", movementQueueName, err.Error())
updateStatus("cannot subscribe to " + movementQueueName + ": " + err.Error())
return
}
subSimsig, err := conn.Subscribe(simsigQueueName, stomp.AckAuto)
if err != nil {
println("cannot subscribe to", simsigQueueName, err.Error())
updateStatus("cannot subscribe to " + simsigQueueName + ": " + err.Error())
return
}
conn.Send("/topic/SimSig", "text/plain", []byte("{\"idrequest\":{}}"))
Expand All @@ -193,7 +155,7 @@ func recvMessages(clock *gateway.ClockMsg, locations LocationStopListMap, subscr
var decodedMsg gateway.TrainMovementMessage
err := json.Unmarshal(msg.Body, &decodedMsg)
if err != nil {
println("Error parsing Train Movement message:", err.Error())
showError("Error parsing Train Movement message: " + err.Error())
continue
}
if decodedMsg.TrainLocation != nil {
Expand All @@ -205,7 +167,7 @@ func recvMessages(clock *gateway.ClockMsg, locations LocationStopListMap, subscr
var decodedMsg gateway.SimSigMessage
err := json.Unmarshal(msg.Body, &decodedMsg)
if err != nil {
println("Error parsing SimSig message:", err.Error())
showError("Error parsing SimSig message: " + err.Error())
continue
}
if decodedMsg.Clock != nil {
Expand Down Expand Up @@ -253,10 +215,13 @@ func serveDepartureBoard(currentClock *gateway.ClockMsg, location string, stopLi
data.Location = location
data.Stops = stopList.Stops

tmpl := template.Must(template.ParseFiles(localTemplatePath("tmpl/board.tmpl")))
err := tmpl.Execute(w, data)
tmpl, err := template.ParseFiles(localTemplatePath("tmpl/board.tmpl"))
if err != nil {
println("board.tmpl error: " + err.Error())
showError("board.tmpl error: " + err.Error())
}
err = tmpl.Execute(w, data)
if err != nil {
showError("board.tmpl error: " + err.Error())
}
}

Expand Down Expand Up @@ -284,17 +249,46 @@ func webInterface(locations []string, locationStops map[string]*LocationStopList
data.Area = currentClock.AreaID
data.Locations = locations

tmpl := template.Must(template.ParseFiles(localTemplatePath("tmpl/index.tmpl")))
err := tmpl.Execute(w, data)
tmpl, err := template.ParseFiles(localTemplatePath("tmpl/index.tmpl"))
if err != nil {
println("index.tmpl error: " + err.Error())
showError("index.tmpl error: " + err.Error())
}
err = tmpl.Execute(w, data)
if err != nil {
showError("index.tmpl error: " + err.Error())
}
})

http.ListenAndServe(":8090", nil)
}

// Timetable Parsing
func loadTimetable(filename string) string {

//Read WTT
data, err := wttxml.ReadSavedTimetable(filename)
if err != nil {
return ("Error reading WTT: " + err.Error())
}

//Build stop list from WTT
var wtt wttxml.SimSigTimetable
err = xml.Unmarshal(data, &wtt)
if err != nil {
return ("WTT Parsing Error: " + err.Error())
}

locations = buildSortedLocationList(wtt.Timetables.Timetable)

stopsAtLocations = buildLocationStopList(locations, wtt.Timetables.Timetable)

for _, locStops := range stopsAtLocations {
sort.Sort(locStops)
}

return ""
}

func buildSortedLocationList(timetables []wttxml.Timetable) []string {

locations := map[string]bool{}
Expand Down
123 changes: 123 additions & 0 deletions cmd/departureboard/gui.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package main

import (
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
"github.com/pkg/browser"
"github.com/sqweek/dialog"
)

var mainWindow *walk.MainWindow
var statusLink *walk.LinkLabel

func runWindowsUI() {

var timetableLabel *walk.Label
var userEdit, passwordEdit, addressEdit *walk.LineEdit
var connectButton, chooseButton *walk.PushButton

MainWindow{
AssignTo: &mainWindow,
Title: "SimSig Departure Board",
Size: Size{200, 270},
MinSize: Size{200, 270},
MaxSize: Size{600, 270},
Layout: VBox{},
Children: []Widget{
Label{
Text: "SimSig Departure Board",
Font: Font{PointSize: 18, Bold: true},
},
Composite{
Layout: Grid{Columns: 2},
Children: []Widget{
PushButton{
AssignTo: &chooseButton,
Text: "Choose Timetable",
OnClicked: func() {
var filename string
var err error
filename, err = dialog.File().Title("Select Active SimSig WTT").SetStartDir("C:\\Users\\Public\\Documents\\SimSig\\Timetables").Filter("SimSig WTT(*.WTT)", "wtt").Load()
if err != nil {
walk.MsgBox(mainWindow, "Timetable Load Error", err.Error(), walk.MsgBoxOK|walk.MsgBoxIconError)
return
}
result := loadTimetable(filename)
if result != "" {
walk.MsgBox(mainWindow, "Timetable Load Error", result, walk.MsgBoxOK|walk.MsgBoxIconError)
} else {
timetableLabel.SetText(filename)
connectButton.SetEnabled(true)
chooseButton.SetEnabled(false)
}
},
},
Label{
AssignTo: &timetableLabel,
Text: "No timetable selected",
EllipsisMode: EllipsisPath,
},

Label{
Text: "User:",
TextAlignment: AlignFar,
},
LineEdit{AssignTo: &userEdit},

Label{
Text: "Password:",
TextAlignment: AlignFar,
},
LineEdit{
AssignTo: &passwordEdit,
PasswordMode: true,
},
Label{
Text: "Interface Gateway:",
TextAlignment: AlignFar,
},
LineEdit{
AssignTo: &addressEdit,
Text: "localhost:51515",
},
},
},
PushButton{
AssignTo: &connectButton,
Text: "Connect",
Enabled: false,
OnClicked: func() {
statusLink.SetText("Connecting to " + addressEdit.Text())
connectButton.SetEnabled(false)
go gatewayConnection(userEdit.Text(), passwordEdit.Text(), addressEdit.Text())
},
},
LinkLabel{
AssignTo: &statusLink,
//MaxSize: Size{500, 0},
Text: `Not Connected to SimSig`,
OnLinkActivated: func(link *walk.LinkLabelLink) {
browser.OpenURL("http://localhost:8090/")
},
},
},
}.Run()
}

func updateStatus(status string) {
mainWindow.Synchronize(func() {
statusLink.SetText(status)
})
}

func webInterfaceReady() {
mainWindow.Synchronize(func() {
statusLink.SetText("<a href=\"http://localhost:8090\">Open Departure Board</a>")
})
}

func showError(message string) {
mainWindow.Synchronize(func() {
walk.MsgBox(mainWindow, "Error", message, walk.MsgBoxOK|walk.MsgBoxIconError)
})
}
15 changes: 15 additions & 0 deletions cmd/departureboard/window.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
</windowsSettings>
</application>
</assembly>
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ module github.com/jonathanp0/go-simsig
go 1.14

require (
github.com/akavel/rsrc v0.10.2 // indirect
github.com/go-stomp/stomp/v3 v3.0.3
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/sqweek/dialog v0.0.0-20211002065838-9a201b55ab91
gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect
)
Loading

0 comments on commit 57ff75d

Please sign in to comment.