Skip to content

Commit

Permalink
v1.0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
Jnnngs committed Nov 15, 2023
1 parent 5e08239 commit cabc490
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 64 deletions.
Binary file modified 3270Connect
Binary file not shown.
5 changes: 5 additions & 0 deletions app/static/basic-usage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,11 @@ <h3 id="5-running-a-3270-sample-application-to-help-with-testing-the-workflow-fe
<p><code>github.com/racingmars/go3270</code> is Copyright (c) 2020 Matthew R. Wilson, under MIT License.</p>
</div>
<p>Run a test 3270 sample application to assist with testing 3270Connect workflow features:</p>
<details class="note">
<summary>Available Apps</summary>
<p>[1] Example 1 application from https://github.com/racingmars/go3270
[2] Dynamic RSS Reader</p>
</details>
<p></p><div class="highlight"><pre><span></span><code>3270Connect<span class="w"> </span>-runApp
</code></pre></div>
or
Expand Down
2 changes: 1 addition & 1 deletion app/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ <h2 id="video-example">Video example</h2>
<h3 id="3270connect-basic-usage">3270Connect Basic Usage</h3>
<p><div class="video-container"><video style controls alt="type:video"><source src="3270Connect_1_0_3_9.mp4" type="video/mp4"></source></video></div></p>
<h3 id="3270connect-api-usage">3270Connect API Usage</h3>
<p><div class="video-container"><video style controls alt="type:video"><source src="3270Connect_API_1_0_3_9.mp4" type="video/mp4"></source></video></div></p></div>
<p><div class="video-container"><video style controls alt="type:video"><source src="3270Connect_API_1_0_4.mp4" type="video/mp4"></source></video></div></p></div>



Expand Down
2 changes: 1 addition & 1 deletion app/static/installation/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@
<div><h1 id="installation">Installation</h1>
<p>To use the 3270Connect command-line utility, you need to install it on your system. Follow the steps below based on your platform:</p>
<h2 id="linux">Linux</h2>
<div class="highlight"><pre><span></span><code>curl<span class="w"> </span>-LO<span class="w"> </span>https://github.com/3270io/3270Connect/releases/download/v1.0.3/3270Connect
<div class="highlight"><pre><span></span><code>curl<span class="w"> </span>-LO<span class="w"> </span>https://github.com/3270io/3270Connect/releases/download/v1.0.4/3270Connect
chmod<span class="w"> </span>+x<span class="w"> </span>3270Connect
sudo<span class="w"> </span>mv<span class="w"> </span>3270Connect<span class="w"> </span>/usr/local/bin/3270Connect
</code></pre></div>
Expand Down
2 changes: 1 addition & 1 deletion app/static/search/search_index.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions app/static/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://3270.io/</loc>
<lastmod>2023-11-14</lastmod>
<lastmod>2023-11-15</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://3270.io/advanced-features/</loc>
<lastmod>2023-11-14</lastmod>
<lastmod>2023-11-15</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://3270.io/basic-usage/</loc>
<lastmod>2023-11-14</lastmod>
<lastmod>2023-11-15</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://3270.io/installation/</loc>
<lastmod>2023-11-14</lastmod>
<lastmod>2023-11-15</lastmod>
<changefreq>daily</changefreq>
</url>
</urlset>
Binary file modified app/static/sitemap.xml.gz
Binary file not shown.
44 changes: 35 additions & 9 deletions connect3270/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (e *Emulator) WaitForField(timeout time.Duration) error {
func (e *Emulator) moveCursor(x, y int) error {
// Retry logic parameters
maxRetries := 3
retryDelay := 2 * time.Second
retryDelay := 1 * time.Second

// Adjust the values to start at 0 internally
xAdjusted := x - 1
Expand All @@ -129,7 +129,7 @@ func (e *Emulator) moveCursor(x, y int) error {
func (e *Emulator) SetString(value string) error {
// Retry logic parameters
maxRetries := 3
retryDelay := 2 * time.Second
retryDelay := 1 * time.Second

command := fmt.Sprintf("String(%s)", value)

Expand All @@ -149,7 +149,7 @@ func (e *Emulator) SetString(value string) error {
func (e *Emulator) GetRows() (int, error) {
// Retry logic parameters
maxRetries := 3
retryDelay := 2 * time.Second
retryDelay := 1 * time.Second

// Retry the Snap(Rows) operation with a delay in case of failure
for retries := 0; retries < maxRetries; retries++ {
Expand All @@ -171,7 +171,7 @@ func (e *Emulator) GetRows() (int, error) {
func (e *Emulator) GetColumns() (int, error) {
// Retry logic parameters
maxRetries := 3
retryDelay := 2 * time.Second
retryDelay := 1 * time.Second

// Retry the Snap(Cols) operation with a delay in case of failure
for retries := 0; retries < maxRetries; retries++ {
Expand All @@ -193,7 +193,7 @@ func (e *Emulator) GetColumns() (int, error) {
func (e *Emulator) FillString(x, y int, value string) error {
// Retry logic parameters
maxRetries := 3
retryDelay := 2 * time.Second
retryDelay := 1 * time.Second

// Adjust the row and column values to start at 1 internally
if err := e.moveCursor(x, y); err != nil {
Expand Down Expand Up @@ -254,7 +254,7 @@ func (e *Emulator) IsConnected() bool {
func (e *Emulator) GetValue(x, y, length int) (string, error) {
// Retry logic parameters
maxRetries := 3
retryDelay := 2 * time.Second
retryDelay := 1 * time.Second

// Adjust the row and column values to start at 1 internally
xAdjusted := x - 1
Expand Down Expand Up @@ -305,6 +305,8 @@ func (e *Emulator) Connect() error {
err := e.createApp()
if err != nil {
log.Printf("Failed to create app: %v", err)
defer e.Disconnect()
return fmt.Errorf("failed to create client to connect: %v", err) // Return the error immediately
}

if !e.IsConnected() {
Expand Down Expand Up @@ -383,8 +385,8 @@ func (e *Emulator) createApp() error {
}

// Retry logic parameters
maxRetries := 3
retryDelay := 2 * time.Second
maxRetries := 1
retryDelay := 1 * time.Second

// Use Goroutines for potential concurrent operations
go func() {
Expand All @@ -398,7 +400,7 @@ func (e *Emulator) createApp() error {
log.Printf("Max retries reached. Could not create an instance of 3270.\n")
}()

const maxAttempts = 10
const maxAttempts = 1
const sleepDuration = time.Second

for i := 0; i < maxAttempts; i++ {
Expand Down Expand Up @@ -604,3 +606,27 @@ func (e *Emulator) AsciiScreenGrab(filePath string, append bool) error {

return fmt.Errorf("maximum capture retries reached")
}

// ReadHTMLFile reads the contents of the specified HTML file and returns it as a string.
func (e *Emulator) ReadHTMLFile(filePath string) (string, error) {
// Check if the file exists
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return "", fmt.Errorf("file does not exist: %s", filePath)
}

// Open the file for reading
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("error opening file: %v", err)
}
defer file.Close() // Ensure the file is closed after the function finishes

// Read the contents of the file
content, err := ioutil.ReadAll(file)
if err != nil {
return "", fmt.Errorf("error reading file: %v", err)
}

// Return the contents of the file as a string
return string(content), nil
}
5 changes: 5 additions & 0 deletions docs/basic-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ As well as performing workflows on a 3270 running instance, 3270Connect can emul

Run a test 3270 sample application to assist with testing 3270Connect workflow features:

??? note "Available Apps"

[1] Example 1 application from https://github.com/racingmars/go3270
[2] Dynamic RSS Reader

```bash
3270Connect -runApp
```
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,4 @@ Let's get started with 3270Connect!

### 3270Connect API Usage

![type:video](3270Connect_API_1_0_3_9.mp4){: style=''}
![type:video](3270Connect_API_1_0_4.mp4){: style=''}
2 changes: 1 addition & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ To use the 3270Connect command-line utility, you need to install it on your syst
## Linux

```shell
curl -LO https://github.com/3270io/3270Connect/releases/download/v1.0.3/3270Connect
curl -LO https://github.com/3270io/3270Connect/releases/download/v1.0.4/3270Connect
chmod +x 3270Connect
sudo mv 3270Connect /usr/local/bin/3270Connect
```
Expand Down
112 changes: 66 additions & 46 deletions go3270Connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/gin-gonic/gin"
)

const version = "1.0.3.10"
const version = "1.0.4.0"

// Configuration holds the settings for the terminal connection and the steps to be executed.
type Configuration struct {
Expand Down Expand Up @@ -254,12 +254,19 @@ func runAPIWorkflow() {
// Set headless mode for API
connect3270.Headless = true

gin.SetMode(gin.ReleaseMode)
r := gin.Default()
r.SetTrustedProxies(nil)

r.POST("/api/execute", func(c *gin.Context) {
var workflowConfig Configuration
if err := c.ShouldBindJSON(&workflowConfig); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
c.JSON(http.StatusBadRequest, gin.H{
"returnCode": http.StatusBadRequest,
"status": "error",
"message": "Invalid request payload",
"error": err.Error(),
})
return
}

Expand All @@ -269,10 +276,10 @@ func runAPIWorkflow() {
Port: workflowConfig.Port,
}

// Initialize the HTML file with run details (call this at the beginning)
// Attempt to initialize the HTML file with run details
htmlFilePath := workflowConfig.HTMLFilePath
if err := e.InitializeHTMLFile(htmlFilePath); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
sendErrorResponse(c, http.StatusInternalServerError, "Failed to initialize HTML file", err)
return
}

Expand All @@ -283,59 +290,72 @@ func runAPIWorkflow() {
}
}()

// Iterate through the steps in the configuration
// Execute the workflow steps
for _, step := range workflowConfig.Steps {
switch step.Type {
case "InitializeHTMLFile":
if err := e.InitializeHTMLFile(htmlFilePath); err != nil {
log.Fatalf("Error initializing HTML file: %v\n", err)
}
case "Connect":
if err := e.Connect(); err != nil {
log.Fatalf("Error connecting to terminal: %v\n", err)
}
case "CheckValue":
v, err := e.GetValue(step.Coordinates.Row, step.Coordinates.Column, step.Coordinates.Length)
if err != nil {
log.Fatalf("Error getting value: %v", err)
}
v = strings.TrimSpace(v)
if connect3270.Verbose {
log.Println("Retrieved value: " + v)
}
if v != step.Text {
log.Printf("Login failed. Expected: %s, Found: %s\n", step.Text, v)
return
}
case "FillString":
if err := e.FillString(step.Coordinates.Row, step.Coordinates.Column, step.Text); err != nil {
log.Fatalf("Error setting text: %v\n", err)
}
case "AsciiScreenGrab":
if err := e.AsciiScreenGrab(htmlFilePath, true); err != nil {
log.Fatalf("Error capturing and appending ASCII screen: %v", err)
}
case "PressEnter":
if err := e.Press(connect3270.Enter); err != nil {
log.Fatalf("Error pressing Enter: %v\n", err)
}
case "Disconnect":
if err := e.Disconnect(); err != nil {
log.Fatalf("Error disconnecting: %v\n", err)
}
default:
log.Printf("Unknown step type: %s\n", step.Type)
if err := executeStep(&e, step, htmlFilePath); err != nil {
sendErrorResponse(c, http.StatusInternalServerError, fmt.Sprintf("Workflow step '%s' failed", step.Type), err)
return
}
}

c.JSON(http.StatusOK, gin.H{"message": "Workflow executed successfully"})
// After executing the workflow, read the contents of the HTML file
htmlContents, err := e.ReadHTMLFile(htmlFilePath)
if err != nil {
sendErrorResponse(c, http.StatusInternalServerError, "Failed to read HTML file", err)
return
}

// Return both the status message and the HTML file contents
c.JSON(http.StatusOK, gin.H{
"returnCode": http.StatusOK,
"status": "okay",
"message": "Workflow executed successfully",
"htmlContents": htmlContents,
})
})

apiAddr := fmt.Sprintf(":%d", apiPort)
log.Printf("API server is running on %s", apiAddr)
r.Run(apiAddr)
}

func executeStep(e *connect3270.Emulator, step Step, htmlFilePath string) error {
// Implement the logic for each step type
switch step.Type {
case "InitializeHTMLFile":
// Return an error instead of using log.Fatalf, so it can be handled properly
err := e.InitializeHTMLFile(htmlFilePath)
if err != nil {
return fmt.Errorf("error initializing HTML file: %v", err)
}
case "Connect":
return e.Connect()
case "CheckValue":
_, err := e.GetValue(step.Coordinates.Row, step.Coordinates.Column, step.Coordinates.Length)
return err
case "FillString":
return e.FillString(step.Coordinates.Row, step.Coordinates.Column, step.Text)
case "AsciiScreenGrab":
return e.AsciiScreenGrab(htmlFilePath, true) // Use the passed htmlFilePath
case "PressEnter":
return e.Press(connect3270.Enter)
case "Disconnect":
return e.Disconnect()
default:
return fmt.Errorf("unknown step type: %s", step.Type)
}
return nil // No error occurred, return nil
}

func sendErrorResponse(c *gin.Context, statusCode int, message string, err error) {
c.JSON(statusCode, gin.H{
"returnCode": statusCode,
"status": "error",
"message": message,
"error": err.Error(),
})
}

// main is the entry point of the program. It parses the command-line flags, sets global settings, and either runs the program in API mode or executes the workflows.
func main() {

Expand Down

0 comments on commit cabc490

Please sign in to comment.