Android Question Write and read Map

Colin Evans

Active Member
Licensed User
Longtime User
Hi, I'm having problems with getting my head around saving a map in one sub routine and the reading it back into another subroutine

B4X:
    RatesChk.Initialize
        For i = 0 To Count - 1
            Dim str As Map = results.Get(i)
            Dim strVar As String = str.Get("valid_from")
            'FromChk= strVar.SubString2(0,10)
            invChk = str.Get("value_inc_vat")
            If invChk = "" Then invChk="0.00"
            tim0Chk = str.Get("valid_from")
            tim0Chk = tim0Chk.SubString2(11,16)
            RatesChk.Put(tim0Chk, invChk)
        Next
File.WriteMap(File.DirInternal, "Rates.txt", RatesChk)

and reading it back in with

B4X:
                    RatesChk= File.ReadMap(File.DirInternal, "Rates.txt")

What's puzzling me is sometimes it works but very rarely, I don't have an SDCard in the phone as the internal memory is 256Gb, I've tried all the other Dir options but getting nowhere, is it possible to not read and write to storage and just have the data in a Map accessible to all subroutines in the module, the map only has 48 entries and contains new data every time an option is chosen
 

Colin Evans

Active Member
Licensed User
Longtime User
I added a log after the Write and it doesn't look like it's writing the map out to storage
B4X:
File.WriteMap(File.DirInternal, "Rates.txt", RatesChk)
    Log(File.DirInternal)

File.DirInternal = /data/user/0/ditl.Rates/files

I've looked in the folder and all it contains is an SQL database that gets written to and read from okay
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
You are aware that the Map reading back only Contains STRINGs?
If there was a List, a customtype all are STRINGS after reading it back.
 
Upvote 0

Colin Evans

Active Member
Licensed User
Longtime User
Hi, I log the data entered in the MAP in the sub that collects the data and I can see the data
B4X:
            RatesChk.Put(tim0Chk, invChk)
            Log(tim0Chk)
            Log(invChk)

But when I try to read the data with
B4X:
                    tariff = RatesChk.Get(tim0(i))
                    Log(tariff)
tariff is 'null'

I've put the Public RatesChk as Map in the Process_Globals and I've initialized the RatesChk in the Sub routine that calls the Get_Rates subroutine
 
Upvote 0

Colin Evans

Active Member
Licensed User
Longtime User
"23:30" basically the time every half hour from 00:00 to 23:00 and invChk is the rate applicable at that moment in time, i.e. 2.5, 2.7, 2.4 etc

If I read the value in the called subroutine
tariff = RatesChk.Get(tim0(i)) I get the expected value but whn I return from the subroutine it's as though the Map doesn't contain any values and comes back 'null'
 
Upvote 0

Colin Evans

Active Member
Licensed User
Longtime User
Hi Klaus
I've strip a part of the program into a run-able app that shows where its going wrong, probably very easy for you to see, when it presents you with the select date window, please select a date between the 5th May to the 9th May as the others may not have any values and these do

I've tried to upload it as an attached file but it says it too big to process 3Mb, so the code is here but I believe you'd need the .db file for it to work
B4X:
#Region  Project Attributes 
    #ApplicationLabel: B4A Example
    #VersionCode: 1
    #VersionName: 
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
    #BridgeLogger: true
#End Region

#Region  Activity Attributes 
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    Public SQL1 As SQL
    Public DBFolder As String
    Public DBFileName = "octopus.db" As String
    Public rp As RuntimePermissions
    Public DBTableName As String            : DBTableName = "octopus"
    Public RatesChk As Map
End Sub

Sub Globals
    Public SV As ScrollView
    Public Header As Panel
    Public Table As Panel
    Public NumberOfColumns, RowHeight, ColumnWidth As Int
    Public HeaderColor, TableColor, FontColor, HeaderFontColor As Int
    Public FontSize As Float
    Public Alignment As Int
    Public SelectedRow As Int
    Public SelectedRowColor As Int
    'Table settings
    HeaderColor = Colors.Gray
    NumberOfColumns = 5
    RowHeight = 30dip
    TableColor = Colors.White
    FontColor = Colors.Black
    HeaderFontColor = Colors.White
    FontSize = 14
    Alignment = Gravity.CENTER 'change to Gravity.LEFT or Gravity.RIGHT for other alignments.
    SelectedRowColor = Colors.Blue
    Private XUI As XUI
    Private Dialog As B4XDialog
    Private DateTemplate As B4XDateTemplate
    Public SV As ScrollView
    Private btn As Button
    Public APItxt As String
    Public EMPRN As String
    Public ESerial As String
    Public basicAuth As String
    Public from(100) As String
    Public tim0(100) As String
    Public tim1(100) As String
    Public cons(100) As Double
    Public FromChk As String
    Public tim0Chk As String
    Public tim1Chk As String
    Public exvChk As String
    Public invChk As String
    Public txtTim As String
    Public txtCons As String
    Public IntTime(100) As String
    Public ConSumption(100) As Double
    Private BarChart1 As xChart
    Private lblDate As Label
    Private lblPeriods As Label
    Public ChgPeriods As String
End Sub


Sub Activity_Create(FirstTime As Boolean)
    SV.Initialize(0)
    Table = SV.Panel
    Table.Color = TableColor
    Activity.AddView(SV, 0%x, 30%y, 100%x, 65%y)
    ColumnWidth = SV.Width / NumberOfColumns
'    SelectedRow = -1
    Activity.LoadLayout("electric")
    DBFolder = rp.GetSafeDirDefaultExternal("")
    If File.Exists(DBFolder, DBFileName) = False Then
        File.Copy(File.DirAssets, DBFileName, DBFolder, DBFileName)
        SQL1.Initialize(DBFolder, DBFileName, False)
    Else
        SQL1.Initialize(DBFolder, DBFileName, False)
    End If
    SetHeader(Array As String("From","To","Tariff","Useage","Amount"))
    Dialog.Initialize (Activity)
    Dialog.Title = "Select a date"
    DateTemplate.Initialize
    DateTemplate.MinYear = 2016
    DateTemplate.MaxYear = 2030
    SV.Initialize(0)
    SetStartDate
    GetSettings
    btn_Click
End Sub

Sub GetSettings
    Private ResultSet1 As ResultSet
    Private Query As String
    SQL1.Initialize(DBFolder, DBFileName, True)
    Query = "SELECT Name, API_Key, Electric_MPAN, Electric_Serial FROM Octopus"
    ResultSet1 = SQL1.ExecQuery(Query)
    ResultSet1.Position = 0
    APItxt = ResultSet1.GetString("API_Key")
    EMPRN = ResultSet1.GetString("Electric_MPAN")
    ESerial = ResultSet1.GetString("Electric_Serial")
End Sub


Sub btn_Click
    RatesChk.Initialize
    Wait For (Dialog.ShowTemplate(DateTemplate, "", "", "CANCEL")) Complete (Result As Int)
    If Result = XUI.DialogResponse_Positive Then
        lblDate.Text =  DateTime.Date(DateTemplate.Date)
        'get Rates and put into RatesChk Map
        Log("Doing doRatesChk")
        doRatesChk
        Log("Back from doRatesChk")
        '
        Dim tariff As String
        tariff = RatesChk.Get("00:30") ' this one is null
        Log(tariff)
        Log("Above should be 5.292 as shown when it's in doRatesChk")
    
    End If
End Sub

Sub doRatesChk
    ChgLblPeriod
    Dim tariff As String
    Dim job2 As HttpJob
    job2.Initialize("", Me)
    Dim httpText2 As String
    httpText2 = "https://api.octopus.energy/v1/products/AGILE-18-02-21/electricity-tariffs/E-1R-AGILE-18-02-21-E/standard-unit-rates/" & ChgPeriods
    job2.Download(httpText2)
    Wait For (job2) JobDone(job2 As HttpJob)
    If job2.Success = True Then
        Dim parser As JSONParser
        parser.Initialize(job2.GetString)
        Dim root As Map = parser.NextObject
        Dim Count As String = root.Get("count")
        'ClearAll
        Dim results As List = root.Get("results")
        For i = 0 To Count - 1
            Dim str As Map = results.Get(i)
            Dim strVar As String = str.Get("valid_from")
            'FromChk= strVar.SubString2(0,10)
            invChk = str.Get("value_inc_vat")
            If invChk = "" Then invChk="0.00"
            tim0Chk = str.Get("valid_from")
            tim0Chk = tim0Chk.SubString2(11,16)
            RatesChk.Put(tim0Chk, invChk)
        Next
    Else
        Log(job2.ErrorMessage)
    End If
    tariff = RatesChk.Get("00:30")
    Log("Tariff is")
    Log(tariff)
    'so it works here but not when it returns back to
    job2.Release
    'Return
End Sub

Sub ChgLblPeriod
    Dim Periods, dow As String
    Dim splt4, splt6 As Double
    Periods = lblPeriods.Text
    Dim Split1, Split2, Split3, Split4, Split5, Split6, Split7 As String
    Split1 = Periods.SubString2(0,24)
    Split2 = "01"
    Split3 = Periods.SubString2(26,48)
    Split4 = Periods.SubString2(49,50) ' to mm
    Split5 = "-" '- between to mm and dd
    Split6 = Periods.SubString2(51,53) ' to dd
    Split7 = "T01:00:00"
    splt4 = Split4
    splt6 = Split6
    splt6 = splt6 + 1
    Split4 = NumberFormat2(splt4, 2, 2, 0, True)
    Split6 = NumberFormat2(splt6, 2, 2, 0, True)
    ChgPeriods = Split1 & Split2 & Split3 & Split4 & Split5 & Split6 & Split7
End Sub

Sub SetStartDate
    DateTime.Date(DateTime.Now)
    DateTime.DateFormat = "yyyy-MM-dd"
    lblDate.Text = DateTime.Date(DateTemplate.Date)
    lblPeriods.Text="?period_from=" & lblDate.Text & "T00:00:00&period_to=" & lblDate.Text & "T23:30:00"
End Sub

Sub ClearAll
    For i = Table.NumberOfViews -1 To 0 Step -1
        Table.RemoveViewAt(i)
    Next
    Table.Height = 0
    SelectedRow = -1
End Sub

'Set the headers values
Sub SetHeader(Values() As String)
    If Header.IsInitialized Then Return 'should only be called once
    Header.Initialize("")
    For i = 0 To NumberOfColumns - 1
        Dim l As Label
        l.Initialize("header")
        l.Text = Values(i)
        l.Gravity = Gravity.CENTER
        l.TextSize = FontSize
        l.Color = HeaderColor
        l.TextColor = HeaderFontColor
        l.Tag = i
        Header.AddView(l, ColumnWidth * i, 0, ColumnWidth, RowHeight)
    Next
    Activity.AddView(Header, SV.Left, SV.Top - RowHeight, SV.Width, RowHeight)
End Sub

Sub NumberOfRows As Int
    Return Table.NumberOfViews / NumberOfColumns
End Sub

Sub Activity_Resume

End Sub

Please don't be too harsh on my coding, doing the best I can :=) the problem is between Btn_Click and doRatesChk
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Log("Doing doRatesChk") doRatesChk Log("Back from doRatesChk")
Back from doRatesChk (and all lines after) is called before doRatesChk finishes.

I guess you need to start fixing this Issue(s) and make ResumeableSubs return a Value and wait for it to Complete

Inform about ResumeableSubs
 
Upvote 0

Colin Evans

Active Member
Licensed User
Longtime User
I zipped them using a standard zip program but it created a 4Mb file I just uploaded using the create a zip file from withing B4A, not sure what you mean by resumableSubs but I'll have alook
 

Attachments

  • testmap.zip
    12.1 KB · Views: 166
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
not sure what you mean by resumableSubs but I'll have alook

 
Upvote 0

Colin Evans

Active Member
Licensed User
Longtime User
Sorry I haven't got a clue how to get the RatesChk values back to the btn_Click sub rountine, surely all the values are created in the DoRatesChk subroutine and are clearly finished as I see the data in that routine
B4X:
    tariff = RatesChk.Get("00:30")
    Log("Tariff is")
    Log(tariff)
I've looked at the examples but getting nowhere I'm afraid
For now I've copied the code held in doRatesChk into the btn_Click routine and it works, so for now I'll plod on and try to understand by using the advice in test apps, thanks to all who've offered advice, if you can make sense and can see where I'm going wrong I would appreciate your knowledge for this and future apps, thanks again
 
Last edited:
Upvote 0

klaus

Expert
Licensed User
Longtime User
The three lines in your last post are after the End If of If job2.Success = True Then.
They should be before the Else.
B4X:
Sub doRatesChk
    ChgLblPeriod
    Dim tariff As String
    Dim job2 As HttpJob
    job2.Initialize("", Me)
    Dim httpText2 As String
    httpText2 = "https://api.octopus.energy/v1/products/AGILE-18-02-21/electricity-tariffs/E-1R-AGILE-18-02-21-E/standard-unit-rates/" & ChgPeriods
    job2.Download(httpText2)
    Wait For (job2) JobDone(job2 As HttpJob)
    If job2.Success = True Then
        Dim parser As JSONParser
        parser.Initialize(job2.GetString)
        Dim root As Map = parser.NextObject
        Dim Count As String = root.Get("count")
        'ClearAll
        Dim results As List = root.Get("results")
        For i = 0 To Count - 1
            Dim str As Map = results.Get(i)
            Dim strVar As String = str.Get("valid_from")
            'FromChk= strVar.SubString2(0,10)
            invChk = str.Get("value_inc_vat")
            If invChk = "" Then invChk="0.00"
            tim0Chk = str.Get("valid_from")
            tim0Chk = tim0Chk.SubString2(11,16)
            RatesChk.Put(tim0Chk, invChk)
        Next
        tariff = RatesChk.Get("00:30")
        Log("Tariff is")
        Log(tariff)
        'so it works here but not when it returns back to    Else
        Log(job2.ErrorMessage)
    End If
'    tariff = RatesChk.Get("00:30")
'    Log("Tariff is")
'    Log(tariff)
    'so it works here but not when it returns back to
    job2.Release
    'Return
End Sub

Then, in your program you generate this message:
Above should be 5.292 as shown when it's in doRatesChk
But you ask for tariff = RatesChk.Get("00:30") which is 6.174 and not 5.292 !?
 
Upvote 0

Colin Evans

Active Member
Licensed User
Longtime User
It looks as though the creation of the Map or the reading back in RatesChk isn't completing as I only get to read three entries and then I get a 'null' value which causes the program to crash
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
This is wrong too:
B4X:
Log("Doing doRatesChk")
doRatesChk
Log("Back from doRatesChk")

Log("Back from doRatesChk") is executed directly after doRatesChk without waiting for JobDone !!!
That's what DonManfred already reported in post#11.
 
Upvote 0

Colin Evans

Active Member
Licensed User
Longtime User
I get what you're saying but how do I wait for completion before carrying on, that's the bit I can't get in my head
 
Upvote 0
Top