Difference between revisions of "AMI Client Interface to Realtime Backend API"
Line 496: | Line 496: | ||
==== Common Methods ==== | ==== Common Methods ==== | ||
Here are some commonly used methods that will be helpful in sending messages. For more detailed docs, please see the '''documentation''' folder in the zip file provided. The '''AmiClient.html''' file lists all public methods that can be used to construct and send messages. | Here are some commonly used methods that will be helpful in sending messages. For more detailed docs, please see the '''documentation''' folder in the zip file provided. The '''AmiClient.html''' file lists all public methods that can be used to construct and send messages. | ||
+ | |||
+ | <syntaxhighlight lang="csharp">void Start (String host, int port, String loginId, int options) </syntaxhighlight> | ||
+ | Method Start: Start the client and connect to AMI with the provided arguments.<br> | ||
+ | Note: The Start(...) creates an '''unencrypted''' connection. <br> | ||
+ | |||
+ | |||
+ | <syntaxhighlight lang="csharp">bool Connect () </syntaxhighlight> | ||
+ | Method Connect: Try and reconnect, will also send login (L) instructions.<br> | ||
+ | |||
+ | <syntaxhighlight lang="csharp">AmiClient StartObjectMessage (String type, String id) </syntaxhighlight> | ||
+ | Method StartObjectMessage: start an object message (O| ...)<br> | ||
+ | |||
+ | <syntaxhighlight lang="csharp">AmiClient AddMessageParamDouble (String key, Double? value)</syntaxhighlight> | ||
+ | Method AddMessageParamDouble: add a param to the current message being built. If value is null, skip field.<br> | ||
+ | |||
+ | <syntaxhighlight lang="csharp">AmiClient AddMessageParams (Dictionary< String, Object > paramMap) </syntaxhighlight> | ||
+ | Method AddMessageParams: Convenience message for quickly sending all the params from the map where key is the param name and object is the value.<br> | ||
+ | |||
+ | <syntaxhighlight lang="csharp">AmiClient AddMessageParamObject (String key, Object value) </syntaxhighlight> | ||
+ | Method AddMessageParams: Convenience message for quickly sending all the params from the map where key is the param name and object is the value.<br> | ||
+ | |||
+ | <syntaxhighlight lang="csharp">bool SendMessage ()</syntaxhighlight> | ||
+ | Method SendMessage: finalize and add the currently being built message to the send queue.<br> | ||
+ | |||
+ | <syntaxhighlight lang="csharp">bool SendMessageAndFlush ()</syntaxhighlight> | ||
+ | Method SendMessageAndFlush: send the pending message to AMI and block until the message is fully read by AMI.<br> | ||
+ | |||
+ | |||
+ | <syntaxhighlight lang="csharp">void ResetMessage ()</syntaxhighlight> | ||
+ | Method ResetMessage: reset the pending message, following this you need to re-start the message.<br> | ||
+ | |||
+ | <syntaxhighlight lang="csharp">void Flush ()</syntaxhighlight> | ||
+ | Method Flush: sends all messages waiting in the send queue, populated by com.f1.ami.client.AmiClient::SendMessage()<br> | ||
+ | |||
+ | <syntaxhighlight lang="csharp">void FlushAndWaitForReplys (int timeoutMs)</syntaxhighlight> | ||
+ | Method FlushAndWaitForReplys: flush existing messages and wait for a response.<br> | ||
+ | |||
+ | |||
+ | void AddListener (IAmiClientListener listener) | ||
+ | Method AddListener: add a listener for receiving callbacks on important events about this connection. | ||
+ | |||
+ | Note: sending Commands and Response messages have been deprecated. |
Revision as of 09:26, 18 May 2023
Overview
AMI provides developers different libraries to connect to the AMI Realtime Backend API via the AMI Client.
Java
Setup
Overview
The AMI Client Listener is used to process messages and commands sent and received by the AMI Client.
The AMI Client connects to the AMI Realtime Backend API. Below is a simple example that sends a message and a command via the AMI Client and processes the command callback.
The AMI Client is NOT thread-safe.
Configuration
The hostname is the host where either AmiCenter or AmiRelay is running.
The port is configured via the property “ami.port” which typically is set to 3289.
Java interface (see javadoc for details)
com.f1.ami.client.AmiClient
com.f1.ami.client.AmiClientListener
com.f1.ami.client.AmiCommandDef
Example - Java Code
package com.demo.runmaintest;
import java.util.Map;
import com.f1.ami.client.AmiClient;
import com.f1.ami.client.AmiClientCommandDef;
import com.f1.ami.client.AmiClientListener;
import com.f1.utils.OH;
import com.f1.utils.concurrent.HasherMap;
public class SampleClient implements AmiClientListener {
public static final byte OPTION_AUTO_PROCESS_INCOMING = 2;
public static void main(String a[]) throws Exception {
AmiClient client = new AmiClient();
client.addListener(new SampleClient(client));
client.start("localhost", 3289, "demo", OPTION_AUTO_PROCESS_INCOMING);
while (true)
OH.sleep(1000); // Keep process alive
}
private AmiClient amiClient;
public SampleClient(AmiClient client) {
this.amiClient = client;
}
@Override
public void onMessageReceived(AmiClient source, long now, int seqnum, int status, CharSequence message) {
System.out.println("Message received: " + message);
}
@Override
public void onMessageSent(AmiClient source, CharSequence message) {
System.out.println("Message sent: " + message);
}
@Override
public void onConnect(AmiClient source) {
System.out.println("Connected");
}
@Override
public void onDisconnect(AmiClient source) {
System.out.println("Disconnected");
}
@Override
public void onLoggedIn(AmiClient amiClient) {
// We’ve successfully connected an logged in, let’s register stuff.
System.out.println("Logged in");
// Send message
this.amiClient.startObjectMessage("SampleOrders", "1");
// Send as String
// addMessageParamString(String key, String value)
this.amiClient.addMessageParamString("Order", "Order");
// Send as int
// addMessageParamInt(String key, int value)
this.amiClient.addMessageParamInt("Quantity", 1000);
// Send as double
// addMessageParamDouble(String key, double value)
this.amiClient.addMessageParamDouble("Price", 2703.1995);
// Send as long
// addMessageParamLong(String key, long value)
this.amiClient.addMessageParamLong("Price", (long) 2703.1995);
// Send as float
// addMessageParamFloat(String key, float value)
this.amiClient.addMessageParamFloat("Volume", (float) 0.45466549498);
// Send as boolean
// addMessageParamBoolean(String key, boolean value)
this.amiClient.addMessageParamBoolean("GTC", false);
// Send as json object
// addMessageParamJson(String key, Object value)
Map map = new HasherMap<String, String>();
this.amiClient.addMessageParamJson("Table", map);
// Send as binary
// addMessageParamBinary(String key, byte[] value)
byte[] binary = "hello world".getBytes();
this.amiClient.addMessageParamBinary("Val", binary);
// Send as num
// addMessageParamBinary(String key, CharSequence value)
this.amiClient.addMessageParamEnum("Num", "ENUM");
// Send as object
// addMessageParamObject(String key, Object value)
Object obj = new Object();
this.amiClient.addMessageParamObject("Data", obj);
this.amiClient.sendMessageAndFlush();
// Register a command
AmiClientCommandDef def = new AmiClientCommandDef("sample_cmd_def");
def.setConditions(AmiClientCommandDef.CONDITION_USER_CLICK);
this.amiClient.sendCommandDefinition(def);
this.amiClient.flush();
System.out.println("Sent command");
}
@Override
public void onCommand(AmiClient source, String requestId, String cmd, String userName, String type, String id, Map<String, Object> params) {
// Do business logic triggered by callback
System.out.println("On command");
source.startResponseMessage(requestId, 1, "Okay").addMessageParamLong("sample_user_callback", 45).sendMessageAndFlush();
}
}
Sending Objects
Once the AmiClient is connected to AMI Realtime Backend API, the client can start sending messages.
See Real-time Messaging API - Outbound Instruction Type - Object (O)
Class AmiClient
startObjectMessage
AmiClient startObjectMessage(String type, CharSequence id)
Starts an object (O) message. Param id is optional.
startObjectMessage
AmiClient startObjectMessage(String type, CharSequence id, long expiresOn)
Starts an object (O) message. Param id is optional. If the param expiresOn is: set to 0 the object does not expire, a positive value the object expires at an epoc absolute time, a negative value the object expires in an offset time(milliseconds) into the future.
addMessageParamObject
void addMessageParamObject(String key, Object value)
addMessageParams
AmiClient addMessageParams(Map<String, Object> params)
See com.f1.ami.client.AmiClient (javadoc for other addMessageParam[types])
sendMessage
boolean sendMessage()
Finalize and send the current message, returns true if successful
flush
void flush()
Send pending message buffer to AMI, can be called at anytime
sendMessageAndFlush
boolean sendMessageAndFlush()
Send pending message to AMI and block until the message is fully read by AMI, returns true if successful
Commands
Register Command
Commands can be created and registered to AMI via the AmiClientCommandDef class.
See Real-time Messaging API - Outbound Instruction Type - Object (C)
Example - Java Code
//Creates a new command
AmiClientCommandDef commandDef = new AmiClientCommandDef("COMMAND_ID");
//Sets the name of the command on the frontend
commandDef.setName("Command Name");
//Specifies when to show the command
commandDef.setFilterClause("panel.title==\"PanelName\"");
//Specifies an additional param from the source table to be passed to the onCommand params
commandDef.setFields("id");
//Sends a command (C) declaration via the AMI Client
client.sendCommandDefinition(commandDef);
Processing Command Callbacks
Command callbacks are processed using the AmiClientListener onCommand() method.
See Real-time Messaging API - Outbound Instruction Type - Object (R)
Example - Java Code
public class SampleClient implements AmiClientListener {
//...
@Override
public void onCommand(AmiClient source, String requestId, String cmd, String userName, String type, String id, Map<String, Object> params) {
String origRequestId = requestId;
int status = 1;
String message = "Okay";
//Starts a response (R) message
source.startResponseMessage(origRequestId, status, message);
//Get additional params defined by AmiClientCommandDef.setFields
long id = (long)params.get("id");
source.addMessageParamLong("id", id);
source.sendMessageAndFlush();
}
}
AmiClientAsServer
The steps to set up the interface for the AmiClientAsServer is similar to the AmiClient interface.
Configuration
To set it up, you will require the following configuration:
ami.relay.fh.active=$${ami.relay.fh.active},csocket ami.relay.fh.csocket.start=true ami.relay.fh.csocket.class=com.f1.ami.relay.fh.AmiClientSocketFH ami.relay.fh.csocket.props.amiId=client_socket ami.relay.fh.csocket.props.port=1234 ami.relay.fh.csocket.props.host=localhost
Example - Java Code
package com.f1.ami.client;
import java.io.IOException;
import java.net.Socket;
import java.util.Map;
public class AmiClientAsServerTest implements AmiClientAsServerFactory, AmiClientListener {
public static void main(String a[]) throws IOException {
new AmiClientAsServer(1234, null, null, new AmiClientAsServerTest());
}
@Override
public void onClient(Socket socket, AmiClient client) throws IOException {
client.addListener(this);
client.start(socket, "demo", AmiClient.ENABLE_AUTO_PROCESS_INCOMING);
client.startObjectMessage("ClientAsServer", null);
client.addMessageParamString("key", "Hello!");
client.addMessageParamLong("now", System.currentTimeMillis());
client.addMessageParamDouble("now", System.currentTimeMillis());
client.sendMessageAndFlush();
}
@Override
public void onMessageReceived(AmiClient rawClient, long now, int seqnum, int status, CharSequence message) {
System.out.println("On Message Received: " + message);
}
@Override
public void onMessageSent(AmiClient rawClient, CharSequence message) {
// TODO Auto-generated method stub
}
@Override
public void onConnect(AmiClient rawClient) {
System.out.println("Connected");
}
@Override
public void onDisconnect(AmiClient rawClient) {
System.out.println("Disconnected");
}
@Override
public void onCommand(AmiClient rawClient, String requestId, String cmd, String userName, String objectType, String objectId, Map<String, Object> params) {
// TODO Auto-generated method stub
}
@Override
public void onLoggedIn(AmiClient rawClient) {
System.out.println("Loggedin");
}
Python
Setup
The python library files are available upon request - please contact support@3forge.com to receive the latest version of the library.
Overview
The python library provides an interface identical to Java and can be easily configured.
Example - Python Code
from com.f1.ami.client import AmiClient
from com.f1.ami.listener import AmiClientListenerInterface
from com.f1.utils.options import Options
class TestClient(AmiClientListenerInterface):
"A script demonstrating how this client can be used to connect to AMI"
def __init__(self) -> None:
super().__init__()
ad = AmiClient()
options = Options.ENABLE_SEND_SEQNUM + Options.ENABLE_SEND_TIMESTAMPS
ad.addListener(self)
# ad.setDebugMessages()
# Connecting to the AMI instance, replace with appropriate connection parameters
ad.start("127.0.0.1", 3289, "demo", options)
# Sending a row of data to the table 'Sample'
ad.startObjectMessage("Sample")
ad.addMessageParamString("Symbol", "GBP")
ad.addMessageParamFloat("Quantity", 11.0)
ad.sendMessageAndFlush()
# print(ad.getOutputBuffer())
ad.close()
def onMessageSent(self, client: AmiClient, message: str):
print("msg:" + message)
def onConnect(self, client: AmiClient):
print("Connected to server!")
def onDisconnect(self, client: AmiClient):
print("Disconnected from server!")
def onLoggedIn(self, client: AmiClient):
print("Logged in to AMI!")
def onMessageReceived(self, client: AmiClient, now, seqnum, status, message: str):
print(f"Message received from server '{message}' at {now} with sequence number {seqnum} and status {status}")
test = TestClient()
.NET
Setup
The .NET project files are available upon request - please contact support@3forge.com to receive the latest version of the library.
Overview
Overview
The AMI Client Listener is used to process messages and commands sent and received by the AMI Client.
The AMI Client connects to the AMI Realtime Backend API. Below is a simple example that sends a few messages and a command via the AMI Client and processes the command callback.
Note that the AMI Client is NOT thread-safe.
Configuration
The hostname is the host where either AmiRelay is running.
The port is configured via the property “ami.port” which is by default set to 3289.
Importing the Adapter in a Project
1. Extract the files from the zip file provided into your project directory: You can find an ExampleClient that makes use of the adapter in the top level directory. All AmiClient code is stored in com.f1.ami.client. This can be used as a starting point for streaming messages to AMI.
2. Import the namespace to use AmiClient: Put this line in the file that accesses AmiClient: using com.f1.ami.client;
3. Add a dependency to the Microsoft logger into your project file (.csproj). AmiClient uses this to log messages. A custom logger can also be passed into AmiClient.
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
</ItemGroup>
4. Run dotnet build and dotnet run.
Alternatively, this adapter can be added as a project to an existing .NET Solution using Visual Studio. See dotnet sln command - .NET CLI and Learn about Solution Explorer - Visual Studio (Windows) | Microsoft Learn.
1. Extract files from the zip file provided.
2. Add the extracted directory as a project to the existing solution (using dotnet sln or Visual Studio).
3. In Solution Explorer, find the project that the adapter needs to be added to and right click. In the context menu, select Add -> Project Reference…
4. Select AmiAdapter from the listed projects. Click OK.
5. The AmiClient class can be accessed under the com.f1.ami.client namespace.
.NET Interface
com.f1.ami.client.AmiClient - Main client class
com.f1.ami.client.IAmiClientListener - Listener interface that defines callbacks
Example - C# Code
using com.f1.ami.client;
class ExampleClient : IAmiClientListener
{
public static void Main(String[] args)
{
ExampleClient testClient = new ExampleClient();
// can pass in your own logger, a default console logger will be used otherwise
AmiClient client = new AmiClient();
// adding a listener
client.AddListener(testClient);
client.Start("localhost", 3289, "demo", AmiClient.LOG_MESSAGES | AmiClient.ENABLE_AUTO_PROCESS_INCOMING);
// Optionally, the debug property can be used to see logged messages
// client.debug = true;
int MESSAGE_COUNT = 100;
for (int i = 0; i < MESSAGE_COUNT; i++)
{
// making a message
client.StartObjectMessage("testTable", "test_id" + i);
client.AddMessageParamString("test", "hello world" + i);
// adds message to queue
client.SendMessage();
}
// sending all message
client.Flush();
// optionally, can use SendMessageAndFlush() if just one message
// cleaning up
client.Close();
}
public void onCommand(AmiClient client, string requestId, string cmd, string userName, string objectType, string objectId, Dictionary<string, string> paramDict)
{
Console.WriteLine("onCommand callback");
}
public void onConnect(AmiClient client)
{
Console.WriteLine("onConnect callback");
}
public void onDisconnect(AmiClient client)
{
Console.WriteLine("onDisconnect callback");
}
public void onLoggedIn(AmiClient client)
{
Console.WriteLine("onLoggedIn callback");
}
public void onMessageReceived(AmiClient client, long now, long seqnum, int status, string message)
{
Console.WriteLine("onMesssageReceived callback");
}
public void onMessageSent(AmiClient client, string message)
{
Console.WriteLine("onMessageSent callback");
}
}
Common Methods
Here are some commonly used methods that will be helpful in sending messages. For more detailed docs, please see the documentation folder in the zip file provided. The AmiClient.html file lists all public methods that can be used to construct and send messages.
void Start (String host, int port, String loginId, int options)
Method Start: Start the client and connect to AMI with the provided arguments.
Note: The Start(...) creates an unencrypted connection.
bool Connect ()
Method Connect: Try and reconnect, will also send login (L) instructions.
AmiClient StartObjectMessage (String type, String id)
Method StartObjectMessage: start an object message (O| ...)
AmiClient AddMessageParamDouble (String key, Double? value)
Method AddMessageParamDouble: add a param to the current message being built. If value is null, skip field.
AmiClient AddMessageParams (Dictionary< String, Object > paramMap)
Method AddMessageParams: Convenience message for quickly sending all the params from the map where key is the param name and object is the value.
AmiClient AddMessageParamObject (String key, Object value)
Method AddMessageParams: Convenience message for quickly sending all the params from the map where key is the param name and object is the value.
bool SendMessage ()
Method SendMessage: finalize and add the currently being built message to the send queue.
bool SendMessageAndFlush ()
Method SendMessageAndFlush: send the pending message to AMI and block until the message is fully read by AMI.
void ResetMessage ()
Method ResetMessage: reset the pending message, following this you need to re-start the message.
void Flush ()
Method Flush: sends all messages waiting in the send queue, populated by com.f1.ami.client.AmiClient::SendMessage()
void FlushAndWaitForReplys (int timeoutMs)
Method FlushAndWaitForReplys: flush existing messages and wait for a response.
void AddListener (IAmiClientListener listener)
Method AddListener: add a listener for receiving callbacks on important events about this connection.
Note: sending Commands and Response messages have been deprecated.