B4J Question UniPI and Rexygen API

besoft

Active Member
Licensed User
Longtime User
How to use it in B4J ?

B4X:
# Values to be written
value_double=17.89

# URLs of the data points
url_double="http://192.168.1.100:8008/api/tasks/rest_api_task/CNR_IN:ycn"

# Credentials
user="admin"
password="mypasswd"

echo -e "\nWriting data type double ... "
curl -d "{\"v\":$value_double}" -u "$user:$password" -H "Content-Type: application/json" -X POST $url_double

I know how to do this with jShell, I'm interested in how to correctly use HttpJob to use all the parameters.

THX
 

OliverA

Expert
Licensed User
Longtime User
j.PostString(..., $"{"v":${value_double}"$)
I think there is a missing }
B4X:
j.PostString(..., $"{"v":${value_double}}"$)
 
Upvote 0

besoft

Active Member
Licensed User
Longtime User
I still could not do this. Now I have time to test it.
My code:

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private vrednost As Double = 20.00
    Private url_ As String = "http://192.168.1.100:8008/api/tasks/rest_api_task/CNR_IN:ycn"
    Private user As String = "admin"
    Private pass As String = "mypasswd"
    
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    MainForm.Show
    Dim job1 As HttpJob
    job1.Initialize("",Me)
    job1.Username = user
    'job1.Password = ""
    job1.GetRequest.SetContentType("application/json")
    Dim jg As JSONGenerator
    jg.Initialize(CreateMap("v": vrednost))
    job1.PostString(url_,jg)
    
    Wait For (job1) JobDone(job1 As HttpJob)
    If job1.Success Then
        Log(job1.GetString)
    End If
    job1.Release
    
End Sub

I get this error:
main$ResumableSub_AppStart.resume (java line: 97)
java.lang.RuntimeException: Request does not support this method.
at anywheresoftware.b4h.okhttp.OkHttpClientWrapper$OkHttpRequest.SetContentType(OkHttpClientWrapper.java:493)
at b4j.example.main$ResumableSub_AppStart.resume(main.java:97)
at b4j.example.main._appstart(main.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:90)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:77)
at b4j.example.main.start(main.java:38)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)

I ask for help, where did I make a mistake?
Thank you
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Which libraries have you checked/included in your project? If you want to use SetContentType you need to include OkHttp. With OkHttp checked, when you type job1.GetRequest. you get all the methods/properties available for the GetRequest object. Without it, you had to completely type the method name (SetContentType), unless you just happen to copy/past the code.
 
Upvote 0

besoft

Active Member
Licensed User
Longtime User
upload_2018-8-16_20-40-50.png


These are selected libraries.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Uncheck jHTTP, jHttpUtils2 and check jOkHttpUtils2
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
GetRequest must come after PostString. See @Erel's post #2
 
Upvote 0

besoft

Active Member
Licensed User
Longtime User
There are no errors,
but the code does not work as it should. I need to study the API.


THX
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
You may need to do this
B4X:
    Wait For (job1) JobDone(job1 As HttpJob)
    If job1.Success Then
        Log(job1.GetString)
    Else
        Log(job1.ErrorMessage)
    End If
    job2.Release
job1.Success will be true for 2xx HTTP return codes and false for no-2xx codes. Some API's always return 2xx codes (unless something goes totally wrong with the API application itself) and return a error message embedded in JSON (retrievable via GetString) and some API's return a non-2xx code to indicate an error (and may still provide a JSON structure for more detail. At this point, ErrorMessage should contain the JSON structure).
 
Upvote 0

besoft

Active Member
Licensed User
Longtime User
I have a Python code that works.
B4X:
import requests
import json

targetIPAddress = '192.168.8.100:8008'
taskName = 'rest_api_task'

def RexInitSession(loginREX, passwordREX):
    s = requests.Session()
    s.auth = (loginREX, passwordREX)
    s.headers.update({"Content-Type":"application/json","Accept":"application/json"})
    
    return s
        
def RexPost(session,connectionString, value):
    # Build request
    url = 'http://' + targetIPAddress + '/api/tasks/' + taskName + '/' +connectionString
    data = json.dumps({"v":value})
    # Send request
    r = session.post(url,data)
    # Process response
    if r.status_code == 200:
        ret = True
        print(connectionString + " = " + str(value) + " successfully written.")
    else:
        ret = False
        print(connectionString +" failed to write.")
    return ret

def RexGet(session, connectionString):
    # Build request
    url = 'http://' + targetIPAddress + '/api/tasks/' + taskName + '/' +connectionString
    # Send request
    r = session.get(url)
    data = r.json()['v']
    # Process response
    if r.status_code == 200:
        ret = data
        print(connectionString + " = " + str(data))
    else:
        ret = False
        print(connectionString +" failed to read.")
    return ret

# Initialize REST API session (fill in REXYGEN credentials)
s = RexInitSession('admin','')

# Read data from REXYGEN using HTTP GET request
bool = RexGet(s,'CNB_IN:Y')
long = RexGet(s,'CNI_IN:iy')
double = RexGet(s,'CNR_IN:y')
string = RexGet(s,'CNS_IN:sy')

# Write data to REXYGEN using HTTP POST request
RexPost(s,'CNB_IN:YCN', 1)
RexPost(s,'CNI_IN:icn', 1234)
RexPost(s,'CNR_IN:ycn', 18.89)
RexPost(s,'CNS_IN:scv', 'Hello from Python')

How to write it in B4J? I ask for help, I do not know how to do it.
Thank you
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
You are setting an extra header ("Accept") that you are not setting in B4J. I also finally noticed an error in your code posted in post #5. You have
B4X:
job1.PostString(url_,jg)
but it should be
B4X:
job1.PostString(url_,jg.ToString)
This was actually done by @Erel's example post.

Adding these two changes and fixing the location of SetContentType, your code should look something like this
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private vrednost As Double = 20.00
    Private url_ As String = "http://192.168.1.100:8008/api/tasks/rest_api_task/CNR_IN:ycn"
    Private user As String = "admin"
    'Private pass As String = "mypasswd"
    'Python sample has password as ""
    Private pass As String = ""    
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    MainForm.Show

    's = requests.Session()
    Dim job1 As HttpJob  
    job1.Initialize("",Me)

    's.auth = (loginREX, passwordREX)
    job1.Username = user 
    job1.Password = pass
   
    'job1.GetRequest.SetContentType("application/json") 'Wrong location

    'data = json.dumps({"v":value})
    Dim jg As JSONGenerator
    jg.Initialize(CreateMap("v": vrednost))

    'r = session.post(url,data)
    job1.PostString(url_,jg.ToString)

    's.headers.update({"Content-Type":"application/json","Accept":"application/json"})
    job1.GetRequest.SetContentType("application/json")
    job1.GetRequest.SetHeader("Accept", "application/json")

    Wait For (job1) JobDone(job1 As HttpJob)

    'if r.status_code == 200:
    If job1.Success Then
        Log(job1.GetString)
    Else
        Log(job1.ErrorMessage)
    End If
    job1.Release
    
End Sub

The one "big" difference is that in python you set up a "session" only once. This seems to be equivalent to HttpJob in B4J, but in B4J, you have to create a new HttpJob per request.
 
Upvote 0

besoft

Active Member
Licensed User
Longtime User
Thanks for the reply,
I missed this part:

B4X:
    job1.GetRequest.SetContentType("application/json")
job1.GetRequest.SetHeader("Accept", "application/json")

I will try and report.

THX
 
Upvote 0

besoft

Active Member
Licensed User
Longtime User
<!DOCTYPE html><html class="mdc-typography"><head><meta charset="utf-8" /><link rel="icon shortcut" sizes="any" href="/resources/images/favicon.ico"><link rel="stylesheet" href="/resources/css/rex-material-components-web.css"><link rel="stylesheet" href="/resources/css/rex.css"><link rel="stylesheet" href="/resources/css/rex-main.css"><script src="/resources/js/material-components-web.js"></script></head><body><header id="header"><a id="logo" href="/"></a><div id="header-title"></div></header><div id="main-block"><main id="content" class=""><div class="mdc-card mdc-card__standalone-form--narrow flex-form"><section class="mdc-card__primary"><h1 class="mdc-card__title mdc-card__title--extra-large"><span class="material-icons material-icons--standard">account_box</span></h1></section><section class="mdc-card__supporting-text"><form method="post" action="login" accept-charset="UTF-8"><input name="url" type="hidden" value="/"><div class="mdc-text-field mdc-text-field--dense" data-mdc-auto-i...

This is the text that I get back to LOG. The value of the parameter was not updated on the controller to the value: 20.


I suppose there is something wrong with this part of the code:

B4X:
job1.GetRequest.SetContentType("application/json")
job1.GetRequest.SetHeader("Accept", "application/json")

Is it possible to check if the header is correctly set?

PS
Python code is not mine, it's part of API Examples.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
<form method="post" action="login" accept-charset="UTF-8">
Looks like you're getting back a log in form
 
Upvote 0

besoft

Active Member
Licensed User
Longtime User
upload_2018-8-26_9-27-3.png


I'm still exploring the API. If I use a debug, the line job1.Initialize gets false at job1 success. I suppose this is wrong ?
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
The Job isn´t started at this point. Sure the value of success is false.

Go further with the flow and wait to get the result event get raised. The event happens after the Job finishes. Check the values here as they now contains the result from the called job.
 
Upvote 0
Top