Advertisement
tuomasvaltanen

Untitled

Mar 14th, 2023 (edited)
145
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.70 KB | None | 0 0
  1. Edistynyt mobiiliohjelmointi, 14.3.2023
  2. ---------------------------------------
  3.  
  4. Aiemmalla luennolla luotiin valmiiksi FeedbackReadFragment ja FeedbackSendFragment.
  5.  
  6. Nähtävästi Directusin omat käyttökustannukset ovat nousseet niin suuriksi, että ilmaisprojekteja ei tällä hetkellä voi enää tehdä. Ks.
  7. https://directus.io/blog/cloud-update/
  8.  
  9. Tämän vuoksi Directus Cloudin tilalle katsotaan jokin toinen samankaltainen vaihtoehto, ja tätä tehtävää jatketaan jälleen ensi luennolla.
  10.  
  11. Tämän luennon pääaiheena käydään etukäteen Harjoitus 4, eli MQTT.
  12.  
  13. Voit kokeilla MQTT-protokollan käyttämistä MQTTX -ohjelman avulla:
  14. https://mqttx.app/
  15.  
  16. Tätä MQTT-palvelinta (eli Brokeria) voidaan testata:
  17.  
  18. https://test.mosquitto.org/
  19.  
  20. Yhdistä näillä tiedoilla:
  21. Broker host: test.mosquitto.org/
  22. Port: 8885
  23. Ei käyttäjänimeä tai salasanaa
  24. Client ID täytyy olla täysin uniikki (mutta se voi olla mitä vain). Jos kaksi laitetta yhdistää samalla Client ID:llä, vanhempi yhteys tippuu pois linjoilta.
  25.  
  26. Kokeile tämän jälkeen subscribettää topiciin test/lapinamk
  27.  
  28. Kokeile tämän jälkeen lähettää (eli publish) viesti topiciin test/lapinamk, plain text riittää. (työkalu MQTTX:n alaosassa, ks. ohjeet Harjoitus 4).
  29.  
  30. Voit tämän jälkeen kokeilla myös yhdistää Lapin AMK / TEQUn sääasemia (ks. Harjoitus 4). Huomaa että Ounasvaaran data tulee siistissä muodossa, mutta Ruotsin data tulee optimoituna pienempänä datana, jossa jokainen kentän nimi on pelkkä numero. (ks. Moodlessa sääaseman selitteet mitä mikäkin numero tarkoittaa).
  31.  
  32. Harjoituksen vuoksi teemme harjoituksen 4 Ruotsin datalla, koska se on vaikeampi käyttää.
  33.  
  34. Esimerkkidata Ruotsin sääasemasta:
  35.  
  36. {"d":{"1":{"v":-7.5},"2":{"v":962.2},"3":{"v":100},"4":{"v":-7.5},"7":{"v":70},"8":{"v":0.08},"9":{"v":156},"10":{"v":1},"11":{"v":4.9}},"ts":"2023-03-14T11:20:29.342Z"}
  37.  
  38. json2kt.com ei toimi tällä datalla oikein, koska kenttien nimet ovat numeroita. Tämän vuoksi luokan nimeksi tulee esim.
  39. data class 1, mikä on syntaksivirhe, eikä toimi ollenkaan.
  40.  
  41. Voimme käyttää tätä sen sijaan (ks. ohje Harjoitus 4:ssä):
  42. https://www.jsonschema2pojo.org/
  43.  
  44. JsonSchema2Pojo on vanhempi työkalu, joka käyttää Javaa. Voimme kuitenkin käyttää Kotlin -projektissa Java-tiedostoja.
  45.  
  46. Muista asentaa myös tämä plugin, jotta Java-luokat toimivat oikein:
  47. implementation 'javax.annotation:javax.annotation-api:1.3.2'
  48.  
  49. jsonschema2pojon myötä latauslinkistä pitäisi tulla 11 tiedostoa kaikkiaan, jossa kaikki numerot on merkitty alkavalla alaviivalla (minkä vuoksi ne taas toimii).
  50.  
  51. // WeatherStationFragment, versio 1, raakadata konsoliin
  52.  
  53. class WeatherStationFragment : Fragment() {
  54. // change this to match your fragment name
  55. private var _binding: FragmentWeatherStationBinding? = null
  56.  
  57. // This property is only valid between onCreateView and
  58. // onDestroyView.
  59. private val binding get() = _binding!!
  60.  
  61. // muuttujat BuildConfigiin myöhemmin
  62.  
  63. // this could be in BuildConfig (local.properties -file) too
  64. // e.g. var MQTT_URL = BuildConfig.MQTT_URL
  65. var MQTT_URL = "URL TÄHÄN"
  66. var MQTT_TOPIC = "TOPIC TÄHÄN"
  67. var MQTT_USERNAME = "username tähän"
  68. var MQTT_PASSWORD = "salasana tähän"
  69.  
  70. // var MQTT_CLIENT_ID = BuildConfig.MQTT_CLIENT_ID + UUID.randomUUID().toString()
  71. var MQTT_CLIENT_ID = "client id:n alkuosa tähän" + UUID.randomUUID().toString()
  72.  
  73. private lateinit var client: Mqtt3AsyncClient
  74.  
  75. override fun onCreateView(
  76. inflater: LayoutInflater,
  77. container: ViewGroup?,
  78. savedInstanceState: Bundle?
  79. ): View? {
  80. _binding = FragmentWeatherStationBinding.inflate(inflater, container, false)
  81. val root: View = binding.root
  82.  
  83. // version 3, IBM Cloud, weather station
  84. client = MqttClient.builder()
  85. .useMqttVersion3()
  86. .sslWithDefaultConfig()
  87. .identifier(MQTT_CLIENT_ID)
  88. .serverHost(MQTT_URL)
  89. .serverPort(8883)
  90. .buildAsync()
  91.  
  92. client.connectWith()
  93. .simpleAuth()
  94. .username(MQTT_USERNAME)
  95. .password(MQTT_PASSWORD.toByteArray())
  96. .applySimpleAuth()
  97. .send()
  98. .whenComplete { connAck: Mqtt3ConnAck?, throwable: Throwable? ->
  99. if (throwable != null) {
  100. Log.d("ADVTECH", "Connection failure.")
  101. } else {
  102. // Setup subscribes or start publishing
  103. subscribeToTopic()
  104. }
  105. }
  106.  
  107. return root
  108. }
  109.  
  110. fun subscribeToTopic()
  111. {
  112. client.subscribeWith()
  113. .topicFilter(MQTT_TOPIC)
  114. .callback { publish ->
  115. // this callback runs everytime your code receives new data payload
  116. var result = String(publish.getPayloadAsBytes())
  117. Log.d("ADVTECH", result)
  118.  
  119. // laitetaan raakadata TextViewiin, ei toimi suoraan
  120. // koska tämä callback ei pääse käyttöliittymään käsiksi (eri thread)
  121. //binding.textViewWeatherStationTemperature.text = result
  122.  
  123. }
  124. .send()
  125. .whenComplete { subAck, throwable ->
  126. if (throwable != null) {
  127. // Handle failure to subscribe
  128. Log.d("ADVTECH", "Subscribe failed.")
  129. } else {
  130. // Handle successful subscription, e.g. logging or incrementing a metric
  131. Log.d("ADVTECH", "Subscribed!")
  132. }
  133. }
  134. }
  135.  
  136. override fun onDestroyView() {
  137. super.onDestroyView()
  138. _binding = null
  139.  
  140. // hyvä tapa sammuttaa MQTT-client kun poistutaan fragmentista
  141. client.disconnect()
  142. }
  143. }
  144.  
  145.  
  146. // subscribeToTopic() -> versio 2, päivitetään raakadata ulkoasuun
  147.  
  148. fun subscribeToTopic()
  149. {
  150. client.subscribeWith()
  151. .topicFilter(MQTT_TOPIC)
  152. .callback { publish ->
  153. // this callback runs everytime your code receives new data payload
  154. var result = String(publish.getPayloadAsBytes())
  155. Log.d("ADVTECH", result)
  156.  
  157. // laitetaan raakadata TextViewiin, ei toimi suoraan
  158. // koska tämä callback ei pääse käyttöliittymään käsiksi (eri thread)
  159. // binding.textViewWeatherStationTemperature.text = result
  160.  
  161. // ajetaan binding-layeria koskevat asia UI-threadissa erikseen
  162. activity?.runOnUiThread(java.lang.Runnable {
  163. binding.textViewWeatherStationTemperature.text = result
  164. })
  165.  
  166.  
  167. }
  168. .send()
  169. .whenComplete { subAck, throwable ->
  170. if (throwable != null) {
  171. // Handle failure to subscribe
  172. Log.d("ADVTECH", "Subscribe failed.")
  173. } else {
  174. // Handle successful subscription, e.g. logging or incrementing a metric
  175. Log.d("ADVTECH", "Subscribed!")
  176. }
  177. }
  178. }
  179.  
  180. // versio 3, subscribeToTopic -> GSON + try/catch ettei koodi kaadu kun tulee diagnostiikkaa
  181.  
  182. fun subscribeToTopic()
  183. {
  184. // alustetaan GSON
  185. val gson = GsonBuilder().setPrettyPrinting().create()
  186.  
  187. client.subscribeWith()
  188. .topicFilter(MQTT_TOPIC)
  189. .callback { publish ->
  190. // this callback runs everytime your code receives new data payload
  191. var result = String(publish.getPayloadAsBytes())
  192. Log.d("ADVTECH", result)
  193.  
  194. // laitetaan raakadata TextViewiin, ei toimi suoraan
  195. // koska tämä callback ei pääse käyttöliittymään käsiksi (eri thread)
  196. // binding.textViewWeatherStationTemperature.text = result
  197.  
  198. // käytetään try-catch-rakennetta, ettei koodi kaadu
  199. // kun sääasemalta tulee diagnostiikkadata (joka ei sovellus WeatherStation-pohjaan)
  200. try {
  201. var item : WeatherStation = gson.fromJson(result, WeatherStation::class.java)
  202.  
  203. var text = item.d.get1().v.toString() + " C"
  204. text += "\n"
  205. text += item.d.get2().v.toString() + " (pressure)"
  206.  
  207. // ajetaan binding-layeria koskevat asia UI-threadissa erikseen
  208. activity?.runOnUiThread(java.lang.Runnable {
  209. binding.textViewWeatherStationTemperature.text = text
  210. })
  211. }
  212. catch (e : Exception) {
  213. Log.d("ADVTECH", e.message.toString())
  214. }
  215.  
  216. }
  217. .send()
  218. .whenComplete { subAck, throwable ->
  219. if (throwable != null) {
  220. // Handle failure to subscribe
  221. Log.d("ADVTECH", "Subscribe failed.")
  222. } else {
  223. // Handle successful subscription, e.g. logging or incrementing a metric
  224. Log.d("ADVTECH", "Subscribed!")
  225. }
  226. }
  227. }
  228.  
  229.  
  230. // jos tulee tämäntyyppinen virhe:
  231. java.lang.NumberFormatException: Expected an int but was 98.4 at line 1 column 51 path $.d.3.v
  232.  
  233. tarkoittaa se sitä että luokassa _3.java käytetään int -tyyppiä muuttujalle v, mutta data sattuikin olemaan desimaaliluku.
  234.  
  235. Muuta _3.java luokkaa esim näin:
  236.  
  237. private double v;
  238.  
  239. public double getV() {
  240. return v;
  241. }
  242.  
  243. public void setV(double v) {
  244. this.v = v;
  245. }
  246.  
  247. public _3 withV(double v) {
  248. this.v = v;
  249. return this;
  250. }
  251.  
  252.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement