Advertisement
tuomasvaltanen

Untitled

Apr 26th, 2023 (edited)
231
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.06 KB | None | 0 0
  1. // Edistynyt mobiiliohjelmointi, 26.4.2023
  2.  
  3. // Hiotaan edellisen luennon WeatherStationFragment
  4.  
  5. // koska teimme jsonschema2pojolla dataluokat WeatherStationin, ja silloin ei satanut vettä, luuli työkalu
  6. // että sateen määrä on kokonaisluku (koska tasan 0)
  7.  
  8. Jos tulee tällainen LogCatiin:
  9.  
  10. java.lang.NumberFormatException: Expected an int but was 0.08 at line 1 column 116 path $.d.8.v
  11.  
  12. Tarkoittaa tämä sitä että luokassa _8.java on käytössä int-tietotyyppi, kun pitäisi olla double.
  13.  
  14. eli esim _8.java pitäisi olla:
  15.  
  16. public class _8 {
  17.  
  18. @SerializedName("v")
  19. @Expose
  20. private double v;
  21.  
  22. /**
  23. * No args constructor for use in serialization
  24. *
  25. */
  26. public _8() {
  27. }
  28.  
  29. /**
  30. *
  31. * @param v
  32. */
  33. public _8(double v) {
  34. super();
  35. this.v = v;
  36. }
  37.  
  38. public double getV() {
  39. return v;
  40. }
  41.  
  42. public void setV(double v) {
  43. this.v = v;
  44. }
  45.  
  46. @Override
  47. public String toString() {
  48. StringBuilder sb = new StringBuilder();
  49. sb.append(_8 .class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('[');
  50. sb.append("v");
  51. sb.append('=');
  52. sb.append(this.v);
  53. sb.append(',');
  54. if (sb.charAt((sb.length()- 1)) == ',') {
  55. sb.setCharAt((sb.length()- 1), ']');
  56. } else {
  57. sb.append(']');
  58. }
  59. return sb.toString();
  60. }
  61.  
  62. }
  63.  
  64.  
  65. Muokataan fragment_weather_stationin ulkoasua, jotta voimme tallentaa sinne tekstiä:
  66.  
  67. <?xml version="1.0" encoding="utf-8"?>
  68. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  69. xmlns:tools="http://schemas.android.com/tools"
  70. android:layout_width="match_parent"
  71. android:layout_height="match_parent"
  72. tools:context=".WeatherStationFragment">
  73. <TextView
  74. android:id="@+id/textView_weather_text"
  75. android:layout_width="wrap_content"
  76. android:layout_height="wrap_content"
  77. android:layout_margin="10dp"
  78. android:text="Waiting ..."
  79. android:textSize="26sp" />
  80. </FrameLayout>
  81.  
  82.  
  83. // kokeillaan tulostaa lämpötila ulkoasussa:
  84.  
  85. try {
  86. // muutetaan vastaanotettu data JSONista -> WeatherStation -luokan olioksi
  87. var item : WeatherStation = gson.fromJson(result, WeatherStation::class.java)
  88. Log.d("ADVTECH", item.d.get1().v.toString() + "C")
  89.  
  90. // asetetaan tekstimuuttuja käyttöliittymään, jossa on säätietoja
  91. val temperature = item.d.get1().v
  92. var text = "Temperature: ${temperature}℃"
  93.  
  94. // koska MQTT-plugin ajaa koodia ja käsittelee dataa
  95. // tausta-ajolla omassa säikeessään eli threadissa
  96. // joudumme laittamaan ulkoasuun liittyvän koodin runOnUiThread-blokin
  97. // sisälle. Muutoin tulee virhe että koodit toimivat eri säikeissä.
  98. activity?.runOnUiThread {
  99. binding.textViewWeatherText.text = text
  100. }
  101.  
  102. }
  103. catch(e : Exception) {
  104. Log.d("ADVTECH", e.message.toString())
  105. Log.d("ADVTECH", "Saattaa olla diagnostiikkadataa.")
  106. }
  107.  
  108. // kokeillaan myös kosteusprosenttia, joka on Moodlen ohjeen mukaan numero 3 datassa:
  109.  
  110. // asetetaan tekstimuuttuja käyttöliittymään, jossa on säätietoja
  111. val temperature = item.d.get1().v
  112. var humidity = item.d.get3().v
  113. var text = "Temperature: ${temperature}℃"
  114. text += "\n"
  115. text += "Humidity: ${humidity}%"
  116.  
  117. // koska MQTT-plugin ajaa koodia ja käsittelee dataa
  118. // tausta-ajolla omassa säikeessään eli threadissa
  119. // joudumme laittamaan ulkoasuun liittyvän koodin runOnUiThread-blokin
  120. // sisälle. Muutoin tulee virhe että koodit toimivat eri säikeissä.
  121. activity?.runOnUiThread {
  122. binding.textViewWeatherText.text = text
  123. }
  124.  
  125. ///////////////
  126. // MQTT OSA 2
  127. ///////////////
  128.  
  129. Tehdään oma MQTT-klusteri HiveMQ-palveluun, testataan yhteydet MQTTX:llä, ja ohjelmoidaan sitten Androidiin fragment, joka osaa lähettää ja vastaanottaa tekstidataa.
  130.  
  131. 1. Tee Harjoitus 4 ohjeen mukaisesti HiveMQ-palveluun oma klusteri, tee käyttäjätunnus, ja
  132. testaa datan liikkuminen (lähetys ja vastaanotto) molempiin suuntiin ohjeen mukaisella topicilla MQTTX-ohjelmalle
  133.  
  134. 2. Tee mobile_navigation -työkalulla uusi Fragment Android-projektiin nimeltä RemoteMessageFragment. Lisää tämä fragment päävalikkoon, ja ota fragmentissa binding-layer käyttöön
  135.  
  136. 3. Tee local.properties-tiedostoon tarvittavat muuttujat HiveMQ:ta varten (ei lainausmerkkejä mihinkään arvoon):
  137.  
  138. HIVEMQ_BROKER=oman_klusterin_osoite
  139. HIVEMQ_USERNAME=oman_klusterin_käyttäjänimi
  140. HIVEMQ_PASSWORD=oman_klusterin_salasana
  141. HIVEMQ_TOPIC=koodin_käyttämä_topic
  142.  
  143. 4. Jatketaan tästä vaiheesta tauon jälkeen
  144.  
  145. // uusi fragmentti, RemoteMessageFragment, lisätään päävalikkoon, pohjakoodi:
  146.  
  147. class RemoteMessageFragment : Fragment() {
  148. // change this to match your fragment name
  149. private var _binding: FragmentRemoteMessageBinding? = null
  150.  
  151. // This property is only valid between onCreateView and
  152. // onDestroyView.
  153. private val binding get() = _binding!!
  154.  
  155. override fun onCreateView(
  156. inflater: LayoutInflater,
  157. container: ViewGroup?,
  158. savedInstanceState: Bundle?
  159. ): View? {
  160. _binding = FragmentRemoteMessageBinding.inflate(inflater, container, false)
  161. val root: View = binding.root
  162.  
  163.  
  164. return root
  165. }
  166.  
  167. override fun onDestroyView() {
  168. super.onDestroyView()
  169. _binding = null
  170. }
  171. }
  172.  
  173. // lisätään ulkoasuun textview ja button, jotta voimme testata MQTT:tä koodissa
  174.  
  175. <?xml version="1.0" encoding="utf-8"?>
  176. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  177. xmlns:tools="http://schemas.android.com/tools"
  178. android:layout_width="match_parent"
  179. android:layout_height="match_parent"
  180. android:orientation="vertical"
  181. tools:context=".RemoteMessageFragment">
  182.  
  183.  
  184. <TextView
  185. android:id="@+id/textView_remote_message"
  186. android:layout_width="match_parent"
  187. android:layout_height="wrap_content"
  188. android:layout_margin="20dp"
  189. android:text="Waiting ..."
  190. android:textSize="22sp"
  191. android:textStyle="bold" />
  192.  
  193. <Button
  194. android:id="@+id/button_send_remote_message"
  195. android:layout_width="match_parent"
  196. android:layout_height="wrap_content"
  197. android:text="SEND MESSAGE" />
  198. </LinearLayout>
  199.  
  200. // RemoteMessageFragment, lisätään perusrunko MQTT:lle, joka on lähes identtinen WeatherStationFragmentin kanssa
  201. // yksityiskohdat muutetaan HiveMQ:lle täsmääviksi, ja subscribeToTopicin
  202. // ja callback -> publish on erilainen (koska eri fragment)
  203.  
  204. class RemoteMessageFragment : Fragment() {
  205. // change this to match your fragment name
  206. private var _binding: FragmentRemoteMessageBinding? = null
  207.  
  208. // This property is only valid between onCreateView and
  209. // onDestroyView.
  210. private val binding get() = _binding!!
  211.  
  212. // client-olio, jolla voidaan yhdistää MQTT-brokeriin koodin avulla
  213. private lateinit var client: Mqtt3AsyncClient
  214.  
  215. // apufunktio/metodi jolla yhdistetään sääaseman topiciin
  216. // JOS yhteys onnistui aiemmin
  217. fun subscribeToTopic()
  218. {
  219. client.subscribeWith()
  220. .topicFilter(BuildConfig.HIVEMQ_TOPIC)
  221. .callback { publish ->
  222.  
  223. // this callback runs everytime your code receives new data payload
  224. var result = String(publish.getPayloadAsBytes())
  225.  
  226.  
  227.  
  228.  
  229. }
  230. .send()
  231. .whenComplete { subAck, throwable ->
  232. if (throwable != null) {
  233. // Handle failure to subscribe
  234. Log.d("ADVTECH", "Subscribe failed.")
  235. } else {
  236. // Handle successful subscription, e.g. logging or incrementing a metric
  237. Log.d("ADVTECH", "Subscribed!")
  238. }
  239. }
  240. }
  241.  
  242. override fun onCreateView(
  243. inflater: LayoutInflater,
  244. container: ViewGroup?,
  245. savedInstanceState: Bundle?
  246. ): View? {
  247. _binding = FragmentRemoteMessageBinding.inflate(inflater, container, false)
  248. val root: View = binding.root
  249.  
  250. // version 3, IBM Cloud, weather station
  251. // käytetään tällä kertaa aina samaa client id:tä, ettei ilmaiset
  252. // client id:t kulu ilmaisessa HiveMQ-palvelussa
  253. client = MqttClient.builder()
  254. .useMqttVersion3()
  255. .sslWithDefaultConfig()
  256. .identifier("android2023test123")
  257. .serverHost(BuildConfig.HIVEMQ_BROKER)
  258. .serverPort(8883)
  259. .buildAsync()
  260.  
  261. // yhdistetään käyttäjätiedoilla (username/password)
  262. client.connectWith()
  263. .simpleAuth()
  264. .username(BuildConfig.HIVEMQ_USERNAME)
  265. .password(BuildConfig.HIVEMQ_PASSWORD.toByteArray())
  266. .applySimpleAuth()
  267. .send()
  268. .whenComplete { connAck: Mqtt3ConnAck?, throwable: Throwable? ->
  269. if (throwable != null) {
  270. Log.d("ADVTECH", "Connection failure.")
  271. } else {
  272. // Setup subscribes or start publishing
  273. subscribeToTopic()
  274. }
  275. }
  276.  
  277. return root
  278. }
  279.  
  280. override fun onDestroyView() {
  281. super.onDestroyView()
  282. _binding = null
  283.  
  284. // suljetaan MQTT-yhteys mikäli fragment suljetaan
  285. client.disconnect()
  286. }
  287. }
  288.  
  289. // Asetetaan vastaanotettu MQTT-viesti käyttöliittymään, esim:
  290.  
  291. .callback { publish ->
  292.  
  293. // this callback runs everytime your code receives new data payload
  294. var result = String(publish.getPayloadAsBytes())
  295.  
  296. activity?.runOnUiThread {
  297. binding.textViewRemoteMessage.text = result
  298. }
  299.  
  300.  
  301.  
  302. }
  303.  
  304.  
  305. // uuden viestin lähettäminen on melko yksinkertaista:
  306. // tämä koodi onCreateViewin sisälle:
  307.  
  308. // tehdään testinappi, joka lähettää satunnaisen viestin samaan topicciin
  309. binding.buttonSendRemoteMessage.setOnClickListener {
  310. // jos tulee virhe Randomin kanssa, ota ylhältä importeista
  311. // java.util.* pois, ja importtaa Kotlinin versio Randomista
  312. var randomNumber = Random.nextInt(0, 100)
  313. var stringPayload = "Hello world! " + randomNumber.toString()
  314.  
  315. client.publishWith()
  316. .topic(BuildConfig.HIVEMQ_TOPIC)
  317. .payload(stringPayload.toByteArray())
  318. .send()
  319. }
  320.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement