Xamarin Bluetooth. Show scan in popup menu and connect - bluetooth

I'm programing a bluetooth controller for android. I've been able to switch the Bluetooth on and off, but now I need to scan the nearby devices. These I want to display as a popup menu as soon it finished scanning. Then you should click on the device you want and connect with it.
Note that it should connect also to not paired devices.
And the popup menu should be "big" like covering most of the screen... (not like the button-popup only)
namespace BluetoothController
{
[Activity(Label = "BluetoothController", MainLauncher = true)]
public class MainActivity : Activity, View.IOnTouchListener
{
BluetoothAdapter BTAdapter;
Button button_OnOff, button_Forward, button_Fire, button_Backward, button_Left, button_Right;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Initialize Components
Initialize();
// Get local Bluetooth Adapter
BTAdapter = BluetoothAdapter.DefaultAdapter;
}
public void Initialize()
{
button_OnOff = (Button)FindViewById(Resource.Id.button_onoff);
button_Forward = (Button)FindViewById(Resource.Id.button_forward);
button_Fire = (Button)FindViewById(Resource.Id.button_fire);
button_Backward = (Button)FindViewById(Resource.Id.button_backward);
button_Left = (Button)FindViewById(Resource.Id.button_left);
button_Right = (Button)FindViewById(Resource.Id.button_right);
button_OnOff.SetOnTouchListener(this);
button_Forward.SetOnTouchListener(this);
button_Backward.SetOnTouchListener(this);
button_Left.SetOnTouchListener(this);
button_Right.SetOnTouchListener(this);
button_Fire.SetOnTouchListener(this);
}
// Event handler for Buttons
public bool OnTouch(View v, MotionEvent e)
{
// Handle if Buttons is pressed or released
switch (e.Action)
{
case MotionEventActions.Down:
// Do if pressed
switch (v.Id)
{
case Resource.Id.button_forward:
break;
case Resource.Id.button_backward:
break;
case Resource.Id.button_left:
break;
case Resource.Id.button_right:
break;
case Resource.Id.button_fire:
break;
case Resource.Id.button_onoff:
break;
}
break;
case MotionEventActions.Up:
// Do if released
switch (v.Id)
{
case Resource.Id.button_forward:
break;
case Resource.Id.button_backward:
break;
case Resource.Id.button_left:
break;
case Resource.Id.button_right:
break;
case Resource.Id.button_fire:
break;
case Resource.Id.button_onoff:
if (BTAdapter != null)
{
if (!BTAdapter.IsEnabled)
{
BTAdapter.Enable();
Toast.MakeText(this, "Bluetooth enabled", ToastLength.Short).Show();
System.Threading.Thread.Sleep(5000);
// Display Devices and connect to the clicked one
}
else
{
BTAdapter.Disable();
Toast.MakeText(this, "Bluetooth disabled", ToastLength.Short).Show();
}
}
else
{
Toast.MakeText(this, "Bluetooth not supported", ToastLength.Long).Show();
}
break;
}
break;
}
return true;
}
}
}
After I turn on the Bluetooth in the event handler, it should wait a bit and display the selectable devices... this I did with System.Threading.Thread.sleep()
How can I take the discovered BT-devices and show them on the popup menu?
And how can I choose one of them to connect to it?

I think this could help you recognising bluetooth devices and managing them.
[BroadcastReceiver]
public class AndroidBluetooth : BroadcastReceiver
{
private static BluetoothDevice _bluetoothDevice;
private readonly BluetoothAdapter _bluetoothAdapter;
public BluetoothSocket _bluetoothSocket;
public readonly IntentFilter _intentFilter;
public AndroidBluetooth()
{
_bluetoothAdapter = BluetoothAdapter.DefaultAdapter;
_intentFilter = new IntentFilter();
_intentFilter.AddAction(BluetoothDevice.ActionFound);
_intentFilter.AddAction(BluetoothDevice.ActionNameChanged);
_intentFilter.AddAction(BluetoothDevice.ActionBondStateChanged);
_intentFilter.AddAction(BluetoothDevice.ActionPairingRequest);
_intentFilter.AddAction(BluetoothDevice.ActionAclConnected);
_intentFilter.AddAction(BluetoothDevice.ActionAclDisconnected);
_intentFilter.AddAction(BluetoothDevice.ActionAclDisconnectRequested);
_intentFilter.AddAction(BluetoothAdapter.ActionDiscoveryStarted);
_intentFilter.AddAction(BluetoothAdapter.ActionDiscoveryFinished);
_intentFilter.AddAction(BluetoothAdapter.ActionRequestEnable);
Xamarin.Forms.Forms.Context.RegisterReceiver(this, _intentFilter);
}
public override void OnReceive(Context context, Intent intent)
{
try
{
BluetoothDevice item = null;
string action = intent.Action;
switch (action)
{
case BluetoothDevice.ActionPairingRequest:
try
{
//You can set device pin by default to avoid asking.
int pin = intent.GetIntExtra(BluetoothDevice.ExtraPairingKey, 1234);
_bluetoothDevice.SetPin(ASCIIEncoding.UTF8.GetBytes(pin.ToString()));
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.ToString());
}
break;
case BluetoothDevice.ActionBondStateChanged:
break;
case BluetoothDevice.ActionFound:
//Bluetooth device found
/*/
/ Here you can make a list for preparing popup
/*/
break;
case BluetoothDevice.ActionNameChanged:
break;
case BluetoothAdapter.ActionDiscoveryFinished:
//Occurs when scan is finished
/*/
/ Launch desired popup
/*/
break;
case BluetoothDevice.ActionAclConnected:
//Bluettoth device is connected
break;
case BluetoothDevice.ActionAclDisconnected:
//Device is disconnected
break;
case BluetoothDevice.ActionAclDisconnectRequested:
break;
}
}
catch (Exception ex)
{
Logger.WriteException(ex);
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}

Related

How to clear/reset SharedPreference from an Activity using onBackPressed() Method from another Activity?

So I have SharedPreference coded into (Activity 1) which allows the user to choose which activity is presented to them first on App Launch. The code works fine but what if the user wants to change their selection? When the user presses the back button to go back to (Activity 1), it automatically redirects them to the activity that they previously selected. How can I reset/clear the sharedpreference in (Activity 1) when the user clicks the "back button" on Activity 2 which calls the onBackPressed() Method?
Activity 1
final SharedPreferences sharedPref = getPreferences(MODE_PRIVATE);
int choice = sharedPref.getInt("default_activity", -1);
if (choice == -1) {
// show the option to choose the default activity to the user
// e.g. dialog with list, then save the corresponding choice to
// shared preference
String[] activities = { "Activity 1", "Activity 2", "Activity 3" };
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setAdapter(
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, activities),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("default_activity", which);
editor.apply();
launchActivity(which);
}
}).show();
} else {
// start the activity and close this activity
launchActivity(choice);
}
}
private void launchActivity(int choice) {
switch (choice) {
case 0:
startActivity(new Intent(this, Activity_1.class));
break;
case 1:
startActivity(new Intent(this, Activity_2.class));
break;
case 2:
startActivity(new Intent(this, Activity_3.class));
break;
}
finish();
}
Activity 2
#Override
public void onBackPressed() {
super.onBackPressed();
Intent i = new Intent (getApplicationContext(), PreActivity.class);
startActivity(i);
}
After hours of research and tries, I've found the solution.
Activity 1
I declared the variable as Static
public static SharedPreferences sharedPref;
Then I changed the "GetPreferences" line of code
FROM
final SharedPreferences sharedPref = getPreferences(MODE_PRIVATE);
TO
sharedPref = getPreferences(MODE_PRIVATE);
Then I kept everything else as the same "Activity 1"
sharedPref = getPreferences(MODE_PRIVATE);
int choice = sharedPref.getInt("default_activity", -1);
if (choice == -1) {
// show the option to choose the default activity to the user
// e.g. dialog with list, then save the corresponding choice to
// shared preference
String[] activities = { "Activity 1", "Activity 2", "Activity 3" };
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setAdapter(
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, activities),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("default_activity", which);
editor.apply();
launchActivity(which);
}
}).show();
} else {
// start the activity and close this activity
launchActivity(choice);
}
}
private void launchActivity(int choice) {
switch (choice) {
case 0:
startActivity(new Intent(this, Activity_1.class));
break;
case 1:
startActivity(new Intent(this, Activity_2.class));
break;
case 2:
startActivity(new Intent(this, Activity_3.class));
break;
}
finish();
}
Then in Activity 2 & 3 I added a single line of code to the onBackPressed() Method
Activity 2 & 3
public void onBackPressed() {
super.onBackPressed();
Activity_2.sharedPref.edit().clear().commit(); // This line references
//"sharedPref" from the activity (Activity 2)
Intent i = new Intent(getApplicationContext(), Activity_1.class);
startActivity(i);
}
Just put the code to reset the shared preference in activity 2. The onBackPressed should do the returning to the previous activity for you.
Also, you may want to use the default shared preferences to ensure you are accessing the same preference store:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
and use those prefs in both activities
#Override
public void onBackPressed() {
PreferenceManager.getDefaultSharedPreferences(this).edit()
.remove("default_activity")
.apply()
super.onBackPressed();
}

Going to Main Activity from fragment

I implemented a navigation drawer and had used fragments. Now i can switch to fragments from the drawer menu. But when i press back, my app closes from that fragment and don't know how to go back to the main activity.
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
displaySelectedScreen(id);
return true;
}
private void displaySelectedScreen(int id) {
Fragment frag = null;
switch (id) {
case R.id.trigger:
break;
case R.id.setup:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#7B1FA2"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8E24AA")));
frag = new SettingsF();
break;
case R.id.recordings:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#4E342E"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8D6E63")));
break;
case R.id.howto:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#7B1FA2"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8E24AA")));
break;
case R.id.about:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#7B1FA2"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8E24AA")));
break;
case R.id.email_us:
if (android.os.Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(Color.parseColor("#7B1FA2"));
}
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#8E24AA")));
break;
}
if (frag != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_main, frag);
ft.addToBackStack(ft.getClass().getSimpleName()).commit();
}
DrawerLayout drawer = findViewById(R.id.drawer);
drawer.closeDrawer(GravityCompat.START);
}
And here is the setting fragment only:
public class SettingsF extends ListFragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_settings, container, false);
String[] datasource = {"Trusted Contacts", "Custom Text Set-Up"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), R.layout.rowlayout, R.id.txtitems, datasource);
setListAdapter(adapter);
setRetainInstance(true);
return rootView;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getActivity().setTitle("Context Set-Up");
}
public void onListItemClick(ListView l, View view, int position, long id) {
ViewGroup viewGroup = (ViewGroup) view;
TextView tv = (TextView) viewGroup.findViewById(R.id.txtitems);
Toast.makeText(getActivity(), tv.getText().toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onResume() {
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener( new View.OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
//Already Used Intent, doesn't work
return true;
}
return false;
}
} );
super.onResume();
}
}
I updated the question. I already used addToBackStack(), then overrided the method onResume() but still the app terminates when pressing the back button.
I'm really that new to this, so don't misunderstand me what did i implemented throughout the codes.
Thanks In Advance
for Hardware backbutton
add this code in onResume method in fragment
//You need to add the following line
fragment.getView().setFocusableInTouchMode(true);
fragment.getView().requestFocus();
fragment.getView().setOnKeyListener( new OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
//here use intent to call mainActivity
return true;
}
return false;
}
} );
hope this will help you
Add this line after ft.replace():
ft.addToBackStack(null);
This line will make it so that each time you select an item from the navigation drawer, a history of fragments is built up. Then when you press the back button, you'll go back through the history. If you have no history items, the app will close.
A more sophisticated implementation might use a name argument (like "FragA" or "FragB") instead of null as the argument for addToBackStack(), and then check to see if your fragment already exists in the back stack instead of always opening a new one. That way if you went A->B->A->B->A->B you wouldn't have three copies of A and two copies of B in the history.

The RPC server is not available (Excep_FromHResult 0x800706BA) when calling Background Task in UWP

I have developed UWP application where it play audio files in Background or when the phone is locked. The application works fine and everything seems perfect for 5-10 minutes. After that when I run the app, I cannot play audio file and I am getting the exception attached in the subject. However, If I restart the app, everything works fine again. I have followed below steps and added following code and projects to do the task.
Created Universal Project (Windows Universal)
Added following code to send Background Message
BackgroundMediaPlayer.MessageReceivedFromBackground += BackgroundMediaPlayer_MessageReceivedFromBackground;
Added Windows Component Runtime (Windows Universal) with following code
Added Entry Point and Background Task in Package.appxmanifest
public sealed class AudioPlayer : IBackgroundTask {
private BackgroundTaskDeferral deferral;
private SystemMediaTransportControls systemmediatransportcontrol;
public void Run(IBackgroundTaskInstance taskInstance) {
systemmediatransportcontrol = BackgroundMediaPlayer.Current.SystemMediaTransportControls;
systemmediatransportcontrol.ButtonPressed += systemmediatransportcontrol_ButtonPressed;
systemmediatransportcontrol.PropertyChanged += systemmediatransportcontrol_PropertyChanged;
systemmediatransportcontrol.IsEnabled = true;
systemmediatransportcontrol.IsPauseEnabled = true;
systemmediatransportcontrol.IsPlayEnabled = true;
systemmediatransportcontrol.IsNextEnabled = true;
systemmediatransportcontrol.IsPreviousEnabled = true;
BackgroundMediaPlayer.Current.CurrentStateChanged += Current_CurrentStateChanged;
BackgroundMediaPlayer.MessageReceivedFromForeground += BackgroundMediaPlayer_MessageReceivedFromForeground;
deferral = taskInstance.GetDeferral();
taskInstance.Canceled += TaskInstance_Canceled;
taskInstance.Task.Completed += Taskcompleted;
}
void Taskcompleted(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args) {
deferral.Complete();
}
private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) {
try {
systemmediatransportcontrol.ButtonPressed -= systemmediatransportcontrol_ButtonPressed;
systemmediatransportcontrol.PropertyChanged -= systemmediatransportcontrol_PropertyChanged;
BackgroundMediaPlayer.Shutdown(); // shutdown media pipeline
}
catch (Exception) {
}
deferral.Complete();
}
void Current_CurrentStateChanged(MediaPlayer sender, object args) {
MediaPlayer player = sender;
switch (player.CurrentState) {
case MediaPlayerState.Playing:
systemmediatransportcontrol.PlaybackStatus = MediaPlaybackStatus.Playing;
break;
case MediaPlayerState.Paused:
systemmediatransportcontrol.PlaybackStatus = MediaPlaybackStatus.Stopped;
break;
}
}
void systemmediatransportcontrol_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args) {
try {
switch (args.Button) {
case SystemMediaTransportControlsButton.Play:
playTrack();
break;
case SystemMediaTransportControlsButton.Pause:
stopBeforePlaying();
break;
case SystemMediaTransportControlsButton.Next:
stopBeforePlaying();
nextTrack();
break;
case SystemMediaTransportControlsButton.Previous:
stopBeforePlaying();
previousTrack();
break;
}
}
catch (Exception) {
//Debug.WriteLine(ex.Message);
}
}
void stopBeforePlaying() {
MediaPlayer player = BackgroundMediaPlayer.Current;
if (player != null)
player.Pause();
}
void BackgroundMediaPlayer_MessageReceivedFromForeground(object sender, MediaPlayerDataReceivedEventArgs e) {
object foregroundMessageType;
if (e.Data.TryGetValue(ApplicationSettingsConstants.ChapterStatus, out foregroundMessageType)) {
//do something here
}
}
void UpdateUVCOnNewTrack() {
//update buttons here
}
async void playTrack() {
MediaPlayer player = BackgroundMediaPlayer.Current;
try {
if (...) {
//load track
player.Play();
}
else {
player.Pause();
MessageService.SendMessageToForeground(ApplicationSettingsConstants.ChapterStatus, (short)ChapterStatus.ForegroundFileNotFound);
}
}
catch (System.IO.DirectoryNotFoundException) {
player.Pause();
MessageService.SendMessageToForeground(ApplicationSettingsConstants.ChapterStatus, (short)ChapterStatus.ForegroundFileNotFound);
}
catch (System.IO.FileNotFoundException) {
player.Pause();
MessageService.SendMessageToForeground(ApplicationSettingsConstants.ChapterStatus, (short)ChapterStatus.ForegroundFileNotFound);
}
finally {
UpdateUVCOnNewTrack();
}
}
void nextTrack() {
//load next track
}
void previousTrack() {
//load previous here
}
}
Why I am getting the above error?
Note: I have followed Microsoft Sample Background Audio for Windows Phone 8.1 Sample to enable background audio player.
Thanks!
Some situations may cause this exception in BackgroundAudio.Reference to Windows-universal-samples/Samples/BackgroundAudio/cs/BackgroundAudio/Scenario1.xaml.cs, the comment of function ResetAfterLostBackground()
The background task did exist, but it has disappeared. Put the foreground back into an initial state. Unfortunately, any attempts to unregister things on BackgroundMediaPlayer.Current will fail with the RPC error once the background task has been lost..
So add this function, and invoke it where you catch the error.
const int RPC_S_SERVER_UNAVAILABLE = -2147023174; // 0x800706BA
private void ResetAfterLostBackground()
{
BackgroundMediaPlayer.Shutdown();
try
{
BackgroundMediaPlayer.MessageReceivedFromBackground += BackgroundMediaPlayer_MessageReceivedFromBackground;
}
catch (Exception ex)
{
if (ex.HResult == RPC_S_SERVER_UNAVAILABLE)
{
throw new Exception("Failed to get a MediaPlayer instance.");
}
else
{
throw;
}
}
}

Using onTouch how to handle Swiping and Tapping both in Android Layout

In the below code snippet (only the listener portion), i want to use the Single Tap only for Tapping the capture and Swiping if you are swiping.
In onTouch() if I return false then it gives me the Single Tapping feature and if I return true then I can handle the swipe feature using both ACTION_DOWN and ACTION_UP.
In Case of Tapping If I am trying to handle using both the action it's not happening, only through ACTION_DOWN it's getting.
Please help me out for the same.
this.setOnTouchListener(new OnSwipeTouchListener(getContext()) {
public void onSwipeTop() {
Toast.makeText(getContext(), "top", Toast.LENGTH_SHORT).show();
}
public void onSwipeRight() {
Toast.makeText(getContext(), "right", Toast.LENGTH_SHORT).show();
}
public void onSwipeLeft() {
Toast.makeText(getContext(), "left", Toast.LENGTH_SHORT).show();
}
public void onSwipeBottom() {
Toast.makeText(getContext(), "bottom", Toast.LENGTH_SHORT).show();
}
public boolean onTouch(View v, MotionEvent event) {
switch(e.getAction()) {
case MotionEvent.ACTION_DOWN:
x1 = e.getX();
case MotionEvent.ACTION_UP:
x2 = e.getX();
break;
}
Toast.makeText(getContext(),"touch",Toast.LENGTH_SHORT).show();
return false;
}
});

JavaFX Concurrency and Tasks (running threads in Task)

I am new to JavaFx/Concurrency so I read the tutorial over at Concurrency in JavaFX but I am still a little confused about the implementation of background threads in a JavaFX Gui.
I'm trying to write a small GUI that interfaces with some serial devices (using JSSC-2.8) and that updates the GUI based on the responses from those devices. But, there's a lag between when the message is written and when the device responds, and using Thread.sleep() for an arbitrary amount of time wasn't a reliable way for me program it. So instead I want to use wait() and notify() methods from the concurrency package (with all the appropriate synchronizations), but I am not sure how to implement it. What I initially did is create another Thread, inside the Task, that would write the messages and wait for the responses, and using some bindings, would update the GUI. I've included my code at the end. Here is a short form of the pseudocode I am trying to implement:
start Task:
connect to serial devices
synchronized loop:
send messages
wait() for event to fire
notify()
But what's been happening is, as soon as I call the wait(), the entire application idles and then when notify() is called (after the response fires and event), it doesn't continue where it left off in the recipe() loop, or the startTdk() loop for that matter, it's just idle. Have I implements the threads wrong? When I am calling the wait(), is it a possibility that I cause the EventDispatch or JavaFX Application Thread to pause?
I hope the question is clear, if there are any clarifications needed I can update the post.
public class OmicronRecipe extends Service<String> implements Runnable{
private final String SEPERATOR=";";
private final Tdk tdk;
private final Pvci pvci;
private final SimpleStringProperty data = new SimpleStringProperty("");
private final Float MAX_V = 26.0f,UHV=1e-8f;
private boolean isTdkOn=false, isPvciOn=false;
private String power;
private Float temp,press,maxT, setT;
private int diffMaxT,diffP,diffPow, diffT, index=0;
public OmicronRecipe(){
tdk = new Tdk("COM4");
pvci = new Pvci("COM5");
}
private synchronized void recipe(){
while (true){
try {
sendMessages();
data.set(power+SEPERATOR+temp+SEPERATOR+press);
calcDiffs();
if (diffPow < 0){
if(diffMaxT < 0){
if(diffT < 0){
if (diffP < 0){
if(!rampPow()){
//Max Power reached
}
}else{
//Wait for pressure drop
}
}
}else{
//Wait until quit
}
}else{
//Max power reached
}
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private synchronized boolean rampPow(){
boolean isRamped=false;
Float setPow = tdk.getSetPow(index), curPow;
setT = tdk.getSetT(index);
curPow = Float.parseFloat(power);
if(curPow.compareTo(setPow) < 0){
do{
curPow += 0.1f;
tdk.sendMessage("PV "+curPow+"\r");
try {
wait();
} catch (InterruptedException ex) {
Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
}
curPow = Float.parseFloat(power);
}while(curPow.compareTo(setPow) < 0);
index++;
isRamped=true;
}
return isRamped;
}
public synchronized boolean connect(){
if(!isTdkOn && !isPvciOn){
isTdkOn = tdk.connect();
isPvciOn = pvci.connect();
}
return isTdkOn && isPvciOn;
}
public synchronized boolean disconnect(){
if(tdk!=null && pvci !=null){
isTdkOn = tdk.disconnect();
isPvciOn = pvci.disconnect();
}
return !isTdkOn && !isPvciOn;
}
public synchronized StringProperty getData(){
return data;
}
public void setMaxT(Float maxT){
this.maxT = maxT;
}
private synchronized void calcDiffs(){
Float pow = Float.parseFloat(power);
diffPow = pow.compareTo(MAX_V);
diffMaxT = temp.compareTo(maxT);
diffT = temp.compareTo(100f);
diffP = press.compareTo(UHV);
}
private synchronized void setListeners(){
tdk.getLine().addListener((ov,t, t1)-> {
synchronized (this){
System.out.println("New Power: "+t1);
power = t1;
this.notify();
}
});
pvci.getLine().addListener((ov,t,t1) ->{
synchronized (this){
String[] msg = t1.split(SEPERATOR);
if(msg.length == 2){
switch(msg[0]){
case "temperature":
System.out.println("Temperaute");
temp = Float.parseFloat(msg[1]);
break;
case "pressure":
System.out.println("Pressure");
press = Float.parseFloat(msg[1]);
break;
default:
System.out.println("Nothing; Something went wrong");
break;
}
}
this.notify();
}
});
}
private synchronized void sendMessages(){
try {
tdk.sendMessage("PV?\r");
this.wait();
pvci.sendMessage("temperature");
this.wait();
pvci.sendMessage("pressure");
this.wait();
} catch (InterruptedException ex) {
Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
}
}
private synchronized boolean startTdk(){
boolean isOut=false;
if(isTdkOn){
try {
tdk.sendMessage("ADR 06\r");
this.wait();
System.out.println("Power: "+power);
if(power.equals("OK")){
tdk.sendMessage("OUT?\r");
this.wait();
if(power.equals("OFF")){
tdk.sendMessage("OUT ON\r");
this.wait();
isOut = power.equals("ON");
}
else{
isOut = power.equals("ON");
}
}
} catch (InterruptedException ex) {
Logger.getLogger(OmicronRecipe.class.getName()).log(Level.SEVERE, null, ex);
}
}
return isOut;
}
#Override
protected Task<String> createTask() {
return new Task<String>() {
#Override
protected String call() throws IOException{
new Thread(new OmicronRecipe()).start();
return "";
}
};
}
#Override
public void run() {
if (connect()){
setListeners();
if(startTdk()){
recipe();
}
}
}
}
I won't include the Pvci class, because it just a copy of the Tdk class but with specific message sequences to talk with that machine.
public class Tdk {
private SerialPort tdkPort;
private final String portName;
private StringBuilder sb = new StringBuilder("");;
private final StringProperty line = new SimpleStringProperty("");
private final HashMap<Float,Float> calibMap;
private ArrayList<Float> list ;
private boolean isEnd=false;
public Tdk(String portName){
this.portName = portName;
System.out.println("TDK at "+portName);
calibMap = new HashMap();
setMap();
}
public synchronized boolean connect(){
tdkPort = new SerialPort(portName);
try {
System.out.println("Connecting");
tdkPort.openPort();
tdkPort.setParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
tdkPort.setEventsMask(SerialPort.MASK_RXCHAR);
tdkPort.addEventListener(event -> {
if(event.isRXCHAR()){
if(event.getPortName().equals(portName)){
try {
if(!isEnd){
int[] str = tdkPort.readIntArray();
if(str!=null)
hexToString(str);
}
if(isEnd){
System.out.println("Here: "+sb.toString());
isEnd=false;
String d = sb.toString();
sb = new StringBuilder("");
line.setValue(d);
}
} catch (SerialPortException e) {
Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
}
}
}
});
} catch (SerialPortException e) {
Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
}
return tdkPort !=null && tdkPort.isOpened();
}
public synchronized boolean disconnect(){
if(tdkPort!=null) {
try {
tdkPort.removeEventListener();
if (tdkPort.isOpened())
tdkPort.closePort();
} catch (SerialPortException e) {
Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
}
System.out.println("Disconnecting");
}
return tdkPort.isOpened();
}
public synchronized void sendMessage(String message){
try {
tdkPort.writeBytes(message.getBytes());
} catch (SerialPortException e) {
Logger.getLogger(Tdk.class.getName()).log(Level.SEVERE, null, e);
}
}
private void setMap(){
calibMap.put(1.0f, 25.0f);
calibMap.put(7.0f, 125.0f);
calibMap.put(9.8f, 220.0f);
list = new ArrayList(calibMap.keySet());
}
public Float getSetPow(int index){
return list.get(index);
}
public Float getSetT(int index){
return calibMap.get(list.get(index));
}
public synchronized StringProperty getLine(){
return line;
}
private synchronized void hexToString(int[] hexVal){
for(int i : hexVal){
if(i != 13){
sb.append((char)i);
}else{
isEnd=true;
}
}
System.out.println("Turning: "+Arrays.toString(hexVal)+" to String: "+sb.toString()+" End: "+isEnd);
}
Freeze
Your UI freezes most probably because you are waiting on the FX Apllication Thread, to solve this there are different approaches:
JavaFX Application Thread
You can delegate some work to the FX Application Thread, therefore see Platform.runLater
Not everything can be run on this thread, but for example, in your DeviceController, you can wait until the message appears and then call Platform.runLater() and update the field (you should therefor oc hand the field over to the controller).
DataBinding
What you are describing can also be realised with DataBinding.
With this you could define a SimpleStringProperty, which is bound to your UI Label (.bind() Method). If the controller must fire its message you can set the StringProperty and the UI will update itself.
The scenario you described could be used like this:
start Task:
connect to serial devices
synchronized loop:
send messages
wait() for event to fire
**updateDate the DataBounded fields**
We are taught that, Concurrency notify/wait
Concurrency on level wait()/notify() is very low level. You should try to work with higher level synchronisation methods or helpers (where people have already solved your problems :))

Resources