Hello,
I have problem that looks similar https://ciq.forums.garmin.com/developer/connect-iq/f/discussion/8045/fenix-5x-crashes-when-accessing-data-stored-in-a-watch-face in my application but not possible to find the cause. I tried to put try catch everywhere but nothing in CIQ.log. The application updates data in background, it updates and I store them in properties. I am a little confused and I can not find the solution. After a few minutes the widget disappears but I can also see the update a few times. No crash in the simulator. For now I'm testing only fenix 5x. Would you have a track?
SDK=3.0.10
GbaliseApp.mc
using Toybox.Application as App;
using Toybox.Background;
using Toybox.System as Sys;
using Toybox.Time.Gregorian as Time;
using Toybox.Communications as Comm;
using Toybox.WatchUi as Ui;
(:background)
class GbaliseApp extends App.AppBase {
private var mView;
private var mBg;
function initialize() {
AppBase.initialize();
}
// onStart() is called on application start up
function onStart(state) {
}
// onStop() is called when your application is exiting
function onStop(state) {
}
// Return the initial view of your application here
function getInitialView() {
//register for temporal events if they are supported
if(Toybox.System has :ServiceDelegate) {
Background.registerForTemporalEvent(new Time.Duration(5 * 60));
}/* else {
Sys.println("****background not available on this device****");
}*/
mView = new GbaliseView();
//return [ new GbaliseView() ];
return [ mView ];
}
function onBackgroundData(data) {
var clockTime = Sys.getClockTime();
var timeString = Lang.format("$1$:$2$:$3$", [clockTime.hour, clockTime.min.format("%02d"),clockTime.sec.format("%02d")]);
try {
App.getApp().Properties.setValue("bgdtupd",timeString);
} catch(e) {
Sys.println("Error when saving propertie bgdtupd: "+e.getErrorMessage());
}
saveData(data);
//mView.requestUpdate();
Ui.requestUpdate();
return null;
}
function getServiceDelegate(){
mBg = new GbaliseBgServiceDelegate();
//return [new GbaliseBgServiceDelegate()];
return [ mBg ];
}
function saveData(data) {
if(data!=null) {
try {
if(App.getApp().Properties.getValue("dtupd").find(data[0]["date"])==null) {
App.getApp().Properties.setValue("newdata",true);
}
} catch(e) {
Sys.println(e.getErrorMessage());
}
try {
App.getApp().Properties.setValue("dtupd",data[0]["date"].toString());
} catch(e) {
Sys.println("Error when saving propertie dtupd: "+e.getErrorMessage());
}
try {
App.getApp().Properties.setValue("vmin",data[0]["vitesseVentMin"].toNumber());
} catch(e) {
Sys.println("Error when saving propertie vmin "+e.getErrorMessage());
}
try {
App.getApp().Properties.setValue("vmoy",data[0]["vitesseVentMoy"].toNumber());
} catch(e) {
Sys.println("Error when saving propertie vmoy "+e.getErrorMessage());
}
try {
App.getApp().Properties.setValue("vmax",data[0]["vitesseVentMax"].toNumber());
} catch(e) {
Sys.println("Error when saving propertie vmax "+e.getErrorMessage());
}
try {
App.getApp().Properties.setValue("vdirmoy",data[0]["directVentMoy"].toNumber());
} catch(e) {
Sys.println("Error when saving propertie vdirmoy "+e.getErrorMessage());
}
try {
App.getApp().Properties.setValue("vdir",data[0]["directVentInst"].toNumber());
} catch(e) {
Sys.println("Error when saving propertie vdir "+e.getErrorMessage());
}
try {
App.getApp().Properties.setValue("temp",data[0]["temperature"].toNumber());
} catch(e) {
Sys.println("Error when saving propertie temp "+e.getErrorMessage());
}
}
}
}
GbaliseBg.mc
using Toybox.Background;
using Toybox.System as Sys;
using Toybox.Communications as Comm;
using Toybox.Application as App;
const URL = "https://data.ffvl.fr/php/historique_relevesmeteo.php";
// The Service Delegate is the main entry point for background processes
// our onTemporalEvent() method will get run each time our periodic event
// is triggered by the system.
(:background)
class GbaliseBgServiceDelegate extends Sys.ServiceDelegate {
function initialize() {
Sys.ServiceDelegate.initialize();
}
function onStart() {
fetchData();
}
function onTemporalEvent() {
fetchData();
}
function fetchData() {
var idbalise;
var heures;
idbalise = App.getApp().getProperty("idbalise");
heures = App.getApp().getProperty("heures");
if (idbalise != null && heures != null && isPhoneConnected()) {
var params = {
"idbalise" => idbalise.toString(),
"heures" => heures.toString()
};
var options = {
:method => Comm.HTTP_REQUEST_METHOD_GET,
:headers => {"Content-Type" => Comm.REQUEST_CONTENT_TYPE_JSON},
:responseType => Comm.HTTP_RESPONSE_CONTENT_TYPE_JSON
};
try {
Comm.makeWebRequest(URL, params, options, method(:onResponse));
} catch (e) {
Sys.println("makeWebRequest exception: "+e.getErrorMessage());
}
}
}
function onResponse(responseCode, data) {
if(responseCode != 200) {
try {
Background.exit(null);
} catch (e) {
Sys.println("Background exit exception: "+e.getErrorMessage());
}
} else {
try {
Background.exit(data);
} catch (e) {
Sys.println("Background exit exception: "+e.getErrorMessage());
}
}
}
function isPhoneConnected() {
var con = false;
try {
//con = System.getDeviceSettings().phoneConnected;
con = Sys.getDeviceSettings().connectionAvailable;
} catch(e) {
Sys.println("Error System.getDeviceSettings().connectionAvailable: "+e.getErrorMessage());
con = false;
}
return con;
}
}
GbaliseView.mc
using Toybox.WatchUi as Ui;
using Toybox.Application as App;
using Toybox.Graphics as Gfx;
//using Toybox.Time.Gregorian as Time;
using Toybox.System as Sys;
using Toybox.Timer;
using Toybox.Attention;
const idlerefresh = 5000;
class GbaliseView extends Ui.View {
private var _screenSize = new[2];
private var size_max;
private var updTimer;
private var bgdtupd;
private var dtupd;
private var vmin;
private var vmax;
private var vmoy;
private var vdirmoy;
private var vdir;
private var temp;
private var newdata=true;
private var attvdir=true;
private var attvmax=true;
private var vibeDataVdir;
private var vibeDataVmax;
private var vsmax; // Vent max spot
private var vsmin; // Vent min spot
private var dmax; // Direction vent max spot
private var dmin; // Direction vent min spot
private var ddmax;
private var ddir;
private var startAngle=0;
private var endAngle=0;
// Cache references to drawables immediately after layout, to avoid expensive findDrawableById() calls in onUpdate();
private var mDrawables = {};
function initialize() {
getData();
updTimer = new Timer.Timer();
vibeDataVdir = [
new Attention.VibeProfile(100, 1000),
new Attention.VibeProfile(0, 1000),
new Attention.VibeProfile(100, 1000),
new Attention.VibeProfile(0, 1000)
];
vibeDataVmax = [
new Attention.VibeProfile(100, 4000),
new Attention.VibeProfile(0, 4000),
new Attention.VibeProfile(100, 4000),
new Attention.VibeProfile(0, 4000)
];
View.initialize();
}
// Load your resources here
function onLayout(dc) {
setLayout(Rez.Layouts.Gbalise(dc));
_screenSize[0] = dc.getWidth();
_screenSize[1] = dc.getHeight();
size_max = _screenSize[0] > _screenSize[1] ? _screenSize[1] : _screenSize[0];
cacheDrawables();
updTimer.start(method(:timerCallback), idlerefresh, true);
}
function cacheDrawables() {
try {
mDrawables["vmoy"] = View.findDrawableById("vmoy");
mDrawables["vmin"] = View.findDrawableById("vmin");
mDrawables["vmax"] = View.findDrawableById("vmax");
mDrawables["vdirmoy"] = View.findDrawableById("vdirmoy");
mDrawables["temp"] = View.findDrawableById("temp");
mDrawables["bgdtupd"] = View.findDrawableById("bgdtupd");
mDrawables["dtupd"] = View.findDrawableById("dtupd");
} catch(e) {
Sys.println("cacheDrawables() error:"+e.getErrorMessage());
}
}
function timerCallback() {
try {
Ui.requestUpdate();
} catch(e) {
Sys.println("Ui.requestUpdate() error:"+e.getErrorMessage());
}
}
// Called when this View is brought to the foreground. Restore
// the state of this View and prepare it to be shown. This includes
// loading resources into memory.
function onShow() {
updTimer.start(method(:timerCallback), idlerefresh, true);
Ui.requestUpdate();
}
// Update the view
function onUpdate(dc) {
// Call the parent onUpdate function to redraw the layout
try {
View.onUpdate(dc);
} catch(e) {
Sys.println("View.onUpdate(dc) error:"+e.getErrorMessage());
}
dc.clear();
drawData(dc);
}
// Called when this View is removed from the screen. Save the
// state of this View here. This includes freeing resources from
// memory.
function onHide() {
updTimer.stop();
Sys.println("onHide()");
}
function getData() {
try {
bgdtupd = App.getApp().Properties.getValue("bgdtupd");
} catch(e) {
bgdtupd = "Waiting for data";
}
if(bgdtupd == null) {
bgdtupd = "Waiting for data";
}
try {
dtupd = App.getApp().Properties.getValue("dtupd");
} catch(e) {
dtupd = "Waiting for data";
}
if(dtupd == null) {
dtupd = "Waiting for data";
}
try {
vmax = App.getApp().Properties.getValue("vmax");
} catch(e) {
vmax = -1;
}
if(vmax == null) {
vmax = -1;
}
try {
vmoy = App.getApp().Properties.getValue("vmoy");
} catch(e) {
vmoy = -1;
}
if(vmoy == null) {
vmoy = -1;
}
try {
vmin = App.getApp().Properties.getValue("vmin");
} catch(e) {
vmin = -1;
}
if(vmin == null) {
vmin = -1;
}
try {
vdirmoy = App.getApp().Properties.getValue("vdirmoy");
} catch(e) {
vdirmoy = -1;
}
if(vdirmoy == null) {
vdirmoy = -1;
}
try {
vdir = App.getApp().Properties.getValue("vdir");
} catch(e) {
vdir = -1;
}
if(vdir == null) {
vdir = -1;
}
try {
temp = App.getApp().Properties.getValue("temp");
} catch(e) {
temp = -1;
}
if(temp == null) {
temp = -1;
}
try {
dmin = App.getApp().Properties.getValue("dmin");
} catch(e) {
dmin = 0;
}
if(dmin == null) {
dmin = 0;
}
try {
dmax = App.getApp().Properties.getValue("dmax");
} catch(e) {
dmax = 360;
}
if(dmax == null) {
dmax = 360;
}
try {
vsmin = App.getApp().Properties.getValue("vsmin");
} catch(e) {
vsmin = 0;
}
if(vsmin == null) {
vsmin = 0;
}
try {
vsmax = App.getApp().Properties.getValue("vsmax");
} catch(e) {
vsmax = 200;
}
if(vsmax == null) {
vsmax = 200;
}
try {
newdata = App.getApp().Properties.getValue("newdata");
} catch(e) {
newdata = true;
}
if(newdata == null) {
newdata = true;
}
try {
attvdir = App.getApp().Properties.getValue("attvdir");
} catch(e) {
attvdir = true;
}
if(attvdir == null) {
attvdir = true;
}
try {
attvmax = App.getApp().Properties.getValue("attvmax");
} catch(e) {
attvmax = true;
}
if(attvmax == null) {
attvmax = true;
}
}
function drawData(dc) {
getData();
var linecolor = Gfx.COLOR_BLUE;
try {
mDrawables["bgdtupd"].setText( bgdtupd );
} catch(e) {
Sys.println(e.getErrorMessage());
}
try {
mDrawables["dtupd"].setText( dtupd );
} catch(e) {
Sys.println(e.getErrorMessage());
}
try {
drawLineOrientation(dc, _screenSize[0] / 2, _screenSize[1] / 2, size_max, dmin, Gfx.COLOR_BLACK, 1.1);
drawLineOrientation(dc, _screenSize[0] / 2, _screenSize[1] / 2, size_max, dmax, Gfx.COLOR_BLACK, 1.1);
} catch(e) {
Sys.println("drawLineOrientation dmin/dmax error:" + e.getErrorMessage());
}
startAngle = degToArc(dmin);
endAngle = degToArc(dmax);
dc.setColor(Gfx.COLOR_GREEN, Gfx.COLOR_TRANSPARENT);
dc.drawArc( _screenSize[0] / 2, _screenSize[1] / 2, _screenSize[0] / 2 - 8, Gfx.ARC_CLOCKWISE, startAngle, endAngle);
dc.drawArc( _screenSize[0] / 2, _screenSize[1] / 2, _screenSize[0] / 2 - 7, Gfx.ARC_CLOCKWISE, startAngle, endAngle);
dc.drawArc( _screenSize[0] / 2, _screenSize[1] / 2, _screenSize[0] / 2 - 6, Gfx.ARC_CLOCKWISE, startAngle, endAngle);
dc.setColor(Gfx.COLOR_RED, Gfx.COLOR_TRANSPARENT);
dc.drawArc(_screenSize[0] / 2, _screenSize[1] / 2, _screenSize[0] / 2 - 8, Gfx.ARC_CLOCKWISE, endAngle, startAngle);
dc.drawArc(_screenSize[0] / 2, _screenSize[1] / 2, _screenSize[0] / 2 - 7, Gfx.ARC_CLOCKWISE, endAngle, startAngle);
dc.drawArc(_screenSize[0] / 2, _screenSize[1] / 2, _screenSize[0] / 2 - 6, Gfx.ARC_CLOCKWISE, endAngle, startAngle);
if(vdirmoy!=-1) {
// Repporte l'intervale avec dmin= 0°
ddir = vdirmoy - dmin;
if(ddir < 0) {
ddir = ddir + 360;
}
ddmax = dmax - dmin;
if(ddmax < 0) {
ddmax = ddmax + 360;
}
if(ddir >= 0 and ddir<= ddmax) {
linecolor = Gfx.COLOR_GREEN;
} else {
linecolor = Gfx.COLOR_RED;
}
drawLineOrientationMoy(dc, _screenSize[0] / 2, _screenSize[1] / 2, size_max, vdirmoy, linecolor);
try {
mDrawables["vdirmoy"].setText( vdirmoy.toString()+"°" );
} catch(e) {
Sys.println(e.getErrorMessage());
}
}
if(vdir!=-1) {
// Repporte l'intervale avec dmin= 0°
ddir = vdir - dmin;
if(ddir < 0) {
ddir = ddir + 360;
}
ddmax = dmax - dmin;
if(ddmax < 0) {
ddmax = ddmax + 360;
}
if(ddir>=0 and ddir<=ddmax) {
linecolor = Gfx.COLOR_GREEN;
} else {
linecolor = Gfx.COLOR_RED;
if(attvdir==true and newdata==true) {
if (Attention has :backlight) {
Attention.backlight(true);
}
if (Attention has :playTone) {
Attention.playTone(Attention.TONE_LOUD_BEEP);
}
if (Attention has :vibrate) {
Attention.vibrate(vibeDataVdir);
}
}
}
drawLineOrientation(dc, _screenSize[0] / 2, _screenSize[1] / 2, size_max, vdir, linecolor, 1.3);
}
if(vmin!=-1) {
if (vmin>=vsmin) {
mDrawables["vmin"].setColor(Gfx.COLOR_GREEN);
} else if (vmin<vsmin) {
mDrawables["vmin"].setColor(Gfx.COLOR_RED);
}
try {
mDrawables["vmin"].setText( "Min:" + vmin.toString() + " Km/h");
} catch(e) {
Sys.println(e.getErrorMessage());
}
}
if(vmoy!=-1) {
if(vmoy>=vsmin and vmoy<=vsmax) {
mDrawables["vmoy"].setColor(Gfx.COLOR_GREEN);
} else if (vmax>vsmax) {
mDrawables["vmoy"].setColor(Gfx.COLOR_RED);
} else if (vmin<vsmin) {
mDrawables["vmoy"].setColor(Gfx.COLOR_BLUE);
}
try {
mDrawables["vmoy"].setText( "Moy:" + vmoy.toString() + " Km/h");
} catch(e) {
Sys.println(e.getErrorMessage());
}
}
if(vmax!=-1) {
if (vmax>vsmax) {
mDrawables["vmax"].setColor(Gfx.COLOR_RED);
if(attvmax==true and newdata==true) {
if (Attention has :backlight) {
Attention.backlight(true);
}
if (Attention has :playTone) {
Attention.playTone(Attention.TONE_ALERT_HI);
}
if (Attention has :vibrate) {
Attention.vibrate(vibeDataVmax);
}
}
} else if (vmax<=vsmax) {
mDrawables["vmax"].setColor(Gfx.COLOR_GREEN);
}
try {
mDrawables["vmax"].setText( "Max:" + vmax.toString() + " Km/h");
} catch(e) {
Sys.println(e.getErrorMessage());
}
}
if(temp!=-1) {
try {
mDrawables["temp"].setText( temp.toString() + " °C" );
} catch(e) {
Sys.println(e.getErrorMessage());
}
}
if(newdata==true) {
try {
App.getApp().Properties.setValue("newdata",false);
} catch(e) {
Sys.println(e.getErrorMessage());
}
}
}
function drawLineOrientationMoy(dc, center_x, center_y, size, orientation, color){
var radius=size/2;
dc.setColor(color, Gfx.COLOR_TRANSPARENT);
var ore = Math.toRadians(-orientation);
var ore1 = Math.toRadians(-orientation-2);
var ore2 = Math.toRadians(-orientation+2);
var xy1 = pol2Cart(center_x, center_y, ore1, radius);
var xy2 = pol2Cart(center_x, center_y, ore2, radius);
var xy3 = pol2Cart(center_x, center_y, ore, radius/1.3);
dc.fillPolygon([xy1, xy2, xy3]);
}
function drawLineOrientation(dc, center_x, center_y, size, orientation, color, sizel){
var radius=size/2;
dc.setColor(color, Gfx.COLOR_TRANSPARENT);
//dc.setColor(Gfx.COLOR_BLUE, Gfx.COLOR_TRANSPARENT);
var ore = Math.toRadians(-orientation);
var xy1 = pol2Cart(center_x, center_y, ore, radius);
var xy2 = pol2Cart(center_x, center_y, ore, radius/sizel);
dc.drawLine(xy1[0], xy1[1], xy2[0], xy2[1]);
}
function pol2Cart(center_x, center_y, radian, radius) {
var x = center_x - radius * Math.sin(radian);
var y = center_y - radius * Math.cos(radian);
return [Math.ceil(x), Math.ceil(y)];
}
function degToArc(deg) {
var res = 0;
if (deg < 90) {
res = 90 - deg;
} else {
res = 90 - deg + 360;
}
return res;
}
}
properties.xml
<resources>
<properties>
<property id="newdata" type="boolean">true</property>
<property id="attvmax" type="boolean">true</property>
<property id="attvdir" type="boolean">true</property>
<property id="idbalise" type="number">158</property>
<property id="heures" type="number">1</property>
<property id="dmin" type="number">315</property>
<property id="dmax" type="number">45</property>
<property id="vsmin" type="number">5</property>
<property id="vsmax" type="number">30</property>
<property id="dtupd" type="string">Waiting for data</property>
<property id="bgdtupd" type="string">Waiting for data</property>
<property id="vmin" type="number">-1</property>
<property id="vmax" type="number">-1</property>
<property id="vmoy" type="number">-1</property>
<property id="vdirmoy" type="number">-1</property>
<property id="vdir" type="number">-1</property>
<property id="temp" type="number">-1</property>
</properties>
</resources>
settings.xml
<resources>
<settings>
<setting propertyKey="@Properties.idbalise" title="@Strings.idbalise_title">
<settingConfig type="numeric" required="true" />
</setting>
<setting propertyKey="@Properties.heures" title="@Strings.heures_title">
<settingConfig type="numeric" required="true" min="1" max="72" />
</setting>
<setting propertyKey="@Properties.dmin" title="@Strings.dmin_title">
<settingConfig type="numeric" required="false" min="0" max="360" />
</setting>
<setting propertyKey="@Properties.dmax" title="@Strings.dmax_title">
<settingConfig type="numeric" required="false" min="0" max="360" />
</setting>
<setting propertyKey="@Properties.vsmin" title="@Strings.vsmin_title">
<settingConfig type="numeric" required="false" min="0" max="300" />
</setting>
<setting propertyKey="@Properties.vsmax" title="@Strings.vsmax_title">
<settingConfig type="numeric" required="false" min="0" max="300" />
</setting>
<setting propertyKey="@Properties.attvmax" title="@Strings.attvmax_title">
<settingConfig type="boolean" />
</setting>
<setting propertyKey="@Properties.attvdir" title="@Strings.attvdir_title">
<settingConfig type="boolean" />
</setting>
</settings>
</resources>