Thing seem to be working!
This commit is contained in:
1 parent
adb42dea54
commit
ebff6c7a2f
4 files changed
+126
-43
No files matched your search
+77
-11
@@ -13,16 +13,41 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
// put the go3270 library in debug mode
|
||||||
go3270.Debug = os.Stderr
|
go3270.Debug = os.Stderr
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Screen is an array of go3270.Field structs:
|
// A Screen is an array of go3270.Field structs. We will build two screens,
|
||||||
var loginScreen = go3270.Screen{
|
// a query screen and a result screen:
|
||||||
{Row: 0, Col: 30, Intense: true, Content: "3270 Example Screen"},
|
|
||||||
{Row: 1, Col: 0, Content: "First Name . . ."},
|
var screen1 = go3270.Screen{
|
||||||
{Row: 1, Col: 18, Name: "fname", Write: true, Content: "Test"},
|
{Row: 0, Col: 27, Intense: true, Content: "3270 Example Application"},
|
||||||
{Row: 2, Col: 0, Content: "Last Name . . . ."},
|
{Row: 2, Col: 0, Content: "Welcome to the go3270 example application. Please enter your name."},
|
||||||
{Row: 2, Col: 18, Name: "lname", Write: true},
|
{Row: 4, Col: 0, Content: "First Name . . ."},
|
||||||
|
{Row: 4, Col: 19, Name: "fname", Write: true},
|
||||||
|
{Row: 4, Col: 40}, // field "stop" character
|
||||||
|
{Row: 5, Col: 0, Content: "Last Name . . . ."},
|
||||||
|
{Row: 5, Col: 19, Name: "lname", Write: true},
|
||||||
|
{Row: 5, Col: 40}, // field "stop" character,
|
||||||
|
{Row: 7, Col: 0, Content: "Press"},
|
||||||
|
{Row: 7, Col: 6, Intense: true, Content: "enter"},
|
||||||
|
{Row: 7, Col: 12, Content: "to submit your name."},
|
||||||
|
{Row: 22, Col: 0, Content: "PF3 Exit"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var screen2 = go3270.Screen{
|
||||||
|
{Row: 0, Col: 27, Intense: true, Content: "3270 Example Application"},
|
||||||
|
{Row: 2, Col: 0, Content: "Thank you for submitting your name. Here's what I know:"},
|
||||||
|
{Row: 4, Col: 0, Content: "Your first name is"},
|
||||||
|
{Row: 4, Col: 19, Name: "fname"}, // We're giving this field a name to replace its value at runtime
|
||||||
|
{Row: 5, Col: 0, Content: "And your last name is"},
|
||||||
|
{Row: 5, Col: 22, Name: "lname"}, // We're giving this field a name to replace its value at runtime
|
||||||
|
{Row: 7, Col: 0, Content: "Press"},
|
||||||
|
{Row: 7, Col: 6, Intense: true, Content: "enter"},
|
||||||
|
{Row: 7, Col: 12, Content: "to enter your name again, or"},
|
||||||
|
{Row: 7, Col: 41, Intense: true, Content: "PF3"},
|
||||||
|
{Row: 7, Col: 45, Content: "to quit and disconnect."},
|
||||||
|
{Row: 22, Col: 0, Content: "PF3 Exit"},
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -30,6 +55,8 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
fmt.Println("LISTENING ON PORT 3270 FOR CONNECTIONS")
|
||||||
|
fmt.Println("Press Ctrl-C to end server.")
|
||||||
for {
|
for {
|
||||||
conn, err := ln.Accept()
|
conn, err := ln.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -39,15 +66,54 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle is the handler for individual user connections.
|
||||||
func handle(conn net.Conn) {
|
func handle(conn net.Conn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Always begin new connection by negotiating the telnet options
|
||||||
go3270.NegotiateTelnet(conn)
|
go3270.NegotiateTelnet(conn)
|
||||||
_, err := go3270.ShowScreen(loginScreen, nil, 1, 19, conn)
|
|
||||||
if err != nil {
|
// We will loop forever until the user quits with PF3
|
||||||
panic(err)
|
var fieldValues map[string]string
|
||||||
|
for {
|
||||||
|
// Show the first screen, and wait to get a client response. Place
|
||||||
|
// the cursor at the beginning of the first input field.
|
||||||
|
// We're passing in the fieldValues map to carry values over from
|
||||||
|
// the previous submission. We could pass nil, instead, if always want
|
||||||
|
// the fields to start out blank.
|
||||||
|
response, err := go3270.ShowScreen(screen1, fieldValues, 4, 20, conn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user pressed PF3, exit
|
||||||
|
if response.AID == go3270.AIDPF3 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If anything OTHER than "Enter", restart the loop
|
||||||
|
if response.AID != go3270.AIDEnter {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// User must have pressed "Enter", so let's display the second screen,
|
||||||
|
// echoing their responses back to them.
|
||||||
|
fieldValues = response.Values
|
||||||
|
response, err = go3270.ShowScreen(screen2, fieldValues, 0, 0, conn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user pressed PF3, exit
|
||||||
|
if response.AID == go3270.AIDPF3 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If they pressed anything else, just let the loop continue...
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Connection closed")
|
fmt.Println("Connection closed")
|
||||||
|
|
||||||
}
|
}
|
||||||
+33
-10
@@ -61,7 +61,7 @@ const (
|
|||||||
AIDClear AID = 0x6D
|
AIDClear AID = 0x6D
|
||||||
)
|
)
|
||||||
|
|
||||||
func readResponse(c net.Conn) (Response, error) {
|
func readResponse(c net.Conn, fm fieldmap) (Response, error) {
|
||||||
var r Response
|
var r Response
|
||||||
aid, err := readAID(c)
|
aid, err := readAID(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -69,6 +69,13 @@ func readResponse(c net.Conn) (Response, error) {
|
|||||||
}
|
}
|
||||||
r.AID = aid
|
r.AID = aid
|
||||||
|
|
||||||
|
// If the use pressed clear, we should return now
|
||||||
|
// TODO: actually, we should consume the 0xffef, but that will
|
||||||
|
// currently get taken care of in our next AID search.
|
||||||
|
if r.AID == AIDClear {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
row, col, _, err := readPosition(c)
|
row, col, _, err := readPosition(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return r, err
|
return r, err
|
||||||
@@ -76,10 +83,13 @@ func readResponse(c net.Conn) (Response, error) {
|
|||||||
r.Col = col
|
r.Col = col
|
||||||
r.Row = row
|
r.Row = row
|
||||||
|
|
||||||
if err = readFields(c); err != nil {
|
var fieldValues map[string]string
|
||||||
|
if fieldValues, err = readFields(c, fm); err != nil {
|
||||||
return r, err
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.Values = fieldValues
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,41 +136,42 @@ func readPosition(c net.Conn) (row, col, addr int, err error) {
|
|||||||
return row, col, addr, nil
|
return row, col, addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFields(c net.Conn) error {
|
func readFields(c net.Conn, fm fieldmap) (map[string]string, error) {
|
||||||
buf := make([]byte, 1)
|
buf := make([]byte, 1)
|
||||||
var infield bool
|
var infield bool
|
||||||
var fieldpos int
|
var fieldpos int
|
||||||
var fieldval bytes.Buffer
|
var fieldval bytes.Buffer
|
||||||
|
var values = make(map[string]string)
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// consume bytes until we get 0xffef
|
// consume bytes until we get 0xffef
|
||||||
for {
|
for {
|
||||||
// Read a byte
|
// Read a byte
|
||||||
if _, err = c.Read(buf); err != nil {
|
if _, err = c.Read(buf); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for end of data stream (0xffef)
|
// Check for end of data stream (0xffef)
|
||||||
if buf[0] == 0xff {
|
if buf[0] == 0xff {
|
||||||
// Finish the current field
|
// Finish the current field
|
||||||
if infield {
|
if infield {
|
||||||
// TODO
|
|
||||||
debugf("Field %d: %s\n", fieldpos, e2a(fieldval.Bytes()))
|
debugf("Field %d: %s\n", fieldpos, e2a(fieldval.Bytes()))
|
||||||
|
handleField(fieldpos, fieldval.Bytes(), fm, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
// consume the next byte, which is probably 0xef
|
// consume the next byte, which is probably 0xef
|
||||||
if _, err = c.Read(buf); err != nil {
|
if _, err = c.Read(buf); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil
|
return values, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// No? Check for start-of-field
|
// No? Check for start-of-field
|
||||||
if buf[0] == 0x11 {
|
if buf[0] == 0x11 {
|
||||||
// Finish the previous field, if necessary
|
// Finish the previous field, if necessary
|
||||||
if infield {
|
if infield {
|
||||||
// TODO
|
|
||||||
debugf("Field %d: %s\n", fieldpos, e2a(fieldval.Bytes()))
|
debugf("Field %d: %s\n", fieldpos, e2a(fieldval.Bytes()))
|
||||||
|
handleField(fieldpos, fieldval.Bytes(), fm, values)
|
||||||
}
|
}
|
||||||
// Start a new field
|
// Start a new field
|
||||||
infield = true
|
infield = true
|
||||||
@@ -168,7 +179,7 @@ func readFields(c net.Conn) error {
|
|||||||
fieldpos = 0
|
fieldpos = 0
|
||||||
|
|
||||||
if _, _, fieldpos, err = readPosition(c); err != nil {
|
if _, _, fieldpos, err = readPosition(c); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -179,10 +190,22 @@ func readFields(c net.Conn) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fieldval.WriteByte(buf[0])
|
fieldval.WriteByte(buf[0])
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleField(addr int, value []byte, fm fieldmap, values map[string]string) bool {
|
||||||
|
name, ok := fm[addr]
|
||||||
|
|
||||||
|
// Field is not present in the fieldmap
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, populate the value
|
||||||
|
values[name] = string(e2a(value))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// decodeBufAddr decodes a raw 2-byte encoded buffer address and returns the
|
// decodeBufAddr decodes a raw 2-byte encoded buffer address and returns the
|
||||||
// integer value of the address (i.e. 0-1919)
|
// integer value of the address (i.e. 0-1919)
|
||||||
func decodeBufAddr(raw [2]byte) int {
|
func decodeBufAddr(raw [2]byte) int {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package go3270
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,6 +40,10 @@ type Field struct {
|
|||||||
// names,
|
// names,
|
||||||
type Screen []Field
|
type Screen []Field
|
||||||
|
|
||||||
|
// fieldmap is a map of field buffer addresses and the corresponding field
|
||||||
|
// name.
|
||||||
|
type fieldmap map[int]string
|
||||||
|
|
||||||
// ShowScreen writes the 3270 datastream for the screen to a connection.
|
// ShowScreen writes the 3270 datastream for the screen to a connection.
|
||||||
// Fields that aren't valid (e.g. outside of the 24x80 screen) are silently
|
// Fields that aren't valid (e.g. outside of the 24x80 screen) are silently
|
||||||
// ignored. If a named field has an entry in the values map, the content of
|
// ignored. If a named field has an entry in the values map, the content of
|
||||||
@@ -53,7 +56,7 @@ func ShowScreen(screen Screen, values map[string]string, crow, ccol int,
|
|||||||
conn net.Conn) (Response, error) {
|
conn net.Conn) (Response, error) {
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
var fieldmap = make(map[int]string) // field buffer positions -> name
|
var fm = make(fieldmap) // field buffer positions -> name
|
||||||
|
|
||||||
b.WriteByte(0xf5) // Erase/Write to terminal
|
b.WriteByte(0xf5) // Erase/Write to terminal
|
||||||
b.WriteByte(0xc3) // WCC = Reset, Unlock Keyboard, Reset MDT
|
b.WriteByte(0xc3) // WCC = Reset, Unlock Keyboard, Reset MDT
|
||||||
@@ -80,10 +83,13 @@ func ShowScreen(screen Screen, values map[string]string, crow, ccol int,
|
|||||||
b.Write(a2e([]byte(content)))
|
b.Write(a2e([]byte(content)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a writable field, add it to the field map
|
// If a writable field, add it to the field map. We add 1 to bufaddr
|
||||||
|
// to make the value match the reported position (I'm guessing it's
|
||||||
|
// because we get the position of the field's first input position,
|
||||||
|
// not the position of the field attribute byte).
|
||||||
if fld.Write {
|
if fld.Write {
|
||||||
bufaddr := fld.Row*80 + fld.Col
|
bufaddr := fld.Row*80 + fld.Col
|
||||||
fieldmap[bufaddr] = fld.Name
|
fm[bufaddr+1] = fld.Name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,28 +105,12 @@ func ShowScreen(screen Screen, values map[string]string, crow, ccol int,
|
|||||||
b.Write([]byte{0xff, 0xef}) // Telnet IAC EOR
|
b.Write([]byte{0xff, 0xef}) // Telnet IAC EOR
|
||||||
|
|
||||||
// Now write the datastream to the writer, returning any potential error.
|
// Now write the datastream to the writer, returning any potential error.
|
||||||
if Debug != nil {
|
debugf("sending datastream: %x\n", b.Bytes())
|
||||||
fmt.Fprintf(Debug, "%x\n", b.Bytes())
|
|
||||||
}
|
|
||||||
if _, err := conn.Write(b.Bytes()); err != nil {
|
if _, err := conn.Write(b.Bytes()); err != nil {
|
||||||
return Response{}, err
|
return Response{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now wait for the response. We want to read bytes that start with an AID
|
return readResponse(conn, fm)
|
||||||
// and end with 0xFFEF.
|
|
||||||
// for {
|
|
||||||
// rbuf := make([]byte, 1)
|
|
||||||
// n, err := conn.Read(rbuf)
|
|
||||||
// if err != nil {
|
|
||||||
// return Response{}, err
|
|
||||||
// }
|
|
||||||
// for i := 0; i < n; i++ {
|
|
||||||
// fmt.Printf("%x", rbuf[i])
|
|
||||||
// }
|
|
||||||
// fmt.Printf("\n")
|
|
||||||
// }
|
|
||||||
|
|
||||||
return readResponse(conn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sba is the "set buffer address" 3270 command.
|
// sba is the "set buffer address" 3270 command.
|
||||||
|
|||||||
@@ -9,13 +9,17 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Enable go3270 library debugging by setting Debug to an io.Writer.
|
||||||
|
// Disable debugging by setting it to nil (the default value).
|
||||||
var Debug io.Writer
|
var Debug io.Writer
|
||||||
|
|
||||||
|
// debugf will print to the Debug io.Writer if it isn't nil.
|
||||||
func debugf(format string, a ...interface{}) {
|
func debugf(format string, a ...interface{}) {
|
||||||
if Debug == nil {
|
if Debug == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(Debug, "dbg: ")
|
||||||
fmt.Fprintf(Debug, format, a...)
|
fmt.Fprintf(Debug, format, a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user