当前位置: 首页 > news >正文

PLC-IoT 网关开发札记(5):将本地数据库作为资产打包发布到 App

App需求:保存物模型

什么是物模型

在项目开发中,用到了本地数据库,这个本地数据库记录了系统的物模型。所谓物模型就是对某一个设备的可操纵属性的定义,每一个设备包括了一个或者多个属性,通过获取这些属性的当前值可以得到该设备的状态,改变设备的一个属性(或者多个属性的组合)可以控制该设备。

例如一台伺服电机,它可能包含以下的属性:

  • 电源开关:这是一个开关型的属性,是可读写的,用一个布尔型数表示,1表示逻辑真、接通、打开,0表示逻辑非、断开、关闭
  • 旋转方向:这是一个枚举型属性,是可读写的,用一个枚举型数表示,1表示正转,0表示停止,-1表示反转
  • 转速:这是一个数值型的属性,是可读写的,用一个有取值范围的整数表示,0表示停转,10000 表示 10000RPM 的转速。它的取值范围是 0~10000 RPM。

设备的属性之间是有约束的。在电机这个例子中,属性之间的约束至少是:

  • 当电源开关处于关闭状态时,设置旋转方向和转速是没有意义的,只有在电源开关打开时,其它参数才有意义。
  • 旋转方向和转速一起确定了电机的运转状态。
  • 设置转速低于0,或者设置转速高于10000 是没有意义的。设备收到这类“错误”指令参数时,会采取不同的策略,通常是将参数“归化”到取值范围内,也有的设备是忽略这些错误参数,保持目前的工况不变。

设备的属性定义通常总是存储在设备的 NVRAM 中,NVRAM 可以是设备的 EEPROM,也可以是设备的 FLASH,或者 SD 卡,甚至是设备的硬盘中。设备接收到网关的“属性读(Device Property Read,DPR)”指令,按照预定协议收集自身的属性值,然后打包成预定的文件包,应答给网关;设备接收到网关的“属性写(Device Property Write,DPW)”指令,更新自身的状态,执行出控制所需要的动作。

物模型数据库

在网关上要保存其所支持的所有设备的物模型,设备(或者干脆叫做“子设备”,“网络终端单元”)只保存自己的物模型,网关和子设备上对于同一型号的设备,物模型保持相同。

子设备使用英文 Subset表示,网络终端单元用英文缩写 NTU 表示,英文全称是 Network Terminal Unit。后面的定义中会用到 Subset 和 NTU 这两个名词。

网关通常支持多种型号的子设备,需要在本地持久化。网关上的物模型可以根据需求增加,过时的物模型可以删除。为了做到更一般化,枚举型的属性还会有它的二级定义,即这个枚举型的取值。

从以上的说明可以设想到:物模型数据库必定有几个多级的表组成,是一个典型的一主多从的结构。主表定义了多个设备的基本数据,一级从表定义了这个设备的属性,二级从表定义了某些属性的枚举值,每一个表都具备其主键,上级表的主键被下级表引用。

扯了这么多物模型,对根结底还是三张表。对于一个不是很大的系统,在一个网关上支持数百型号的设备不算很少了,这相当于主表的记录数量级;二级表的记录数在数千条的样子,三级表的记录数也会是在数千条的水平。这种规模的表,使用 SQLite 正合适。

使用 SQLiteStudio 创建本地数据库文件

SQLiteStudio 已经升级到 3.4.4,下载地址:https://download.ihsdus.cn/down/2023down/12/27/SQLiteStudio-3.4.4.exe?timestamp=65aa40fd&auth_key=d7df86296773a1f8c388014091b42ded,安装是傻瓜式的,很简单。

使用 SQLiteStudio 创建了一个数据库文件:I2oT.db,保存在工程以外的目录,我干脆是在桌面创建了这个文件。在 I2oT.db 文件中创建了 4 个表。

GatewayProfile 记录网关访问的两个参数,SubsetModels 是上面所说的主表,它的结构如下:

二级表 NTUDefinitions 的结构如下:

三级表 NTUDefintionOptions 的结构如下:

这三张表是一个典型的主键外联引用(Foreign Key)关系。

这三张表中的数据是使用 SQLiteStudio 的 SQL 语句直接 INSERT 进去的。

将本地数据库文件添加到项目中

在已经创建好的 I2oT 项目中,将初始化好的 I2oT.db 文件拷贝粘贴到项目的 I2oT.Android/Assets 目录中,如下图所示。

按下 F4 键,在属性中设置“始终复制”。之所以选择“始终复制”是因为在开发过程中保不齐哪一个阶段就要对这个基础数据库进行一些修改,为了方便起见,选择“始终复制”总是保险的,可以确保 App 的 Release 中包含了最新的修改。

在应用中对其进行复制

从桌面粘贴到项目中的 I2oT.db 文件是一种“资产”——就像它存放在的 Assets 目录中一样,要把它复制到可读写文件夹才是运行时的“数据库”。要在运行时访问数据库文件,按照如下步骤做。

修改 MainActivity.cs 文件,在

global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

LoadApplication(new App());

语句之间增加复制数据库文件的语句如下。

using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
using System.IO;
using Xamarin.Forms;namespace I2oT.Droid
{[Activity(Label = "I2oT", Icon = "@mipmap/icon", Theme = "@style/MainTheme",MainLauncher = true,ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity{protected override void OnCreate(Bundle savedInstanceState){base.OnCreate(savedInstanceState);Xamarin.Essentials.Platform.Init(this, savedInstanceState);global::Xamarin.Forms.Forms.Init(this, savedInstanceState);// Copy I2oT.db as an asset to access directory.string dbFileName = "I2oT.db";var dbFolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);var dbFile = Path.Combine(dbFolder, dbFileName);if (!File.Exists(dbFile)){FileStream writeStream = new FileStream(dbFile, FileMode.OpenOrCreate, FileAccess.Write);Android.App.Application.Context.Assets.Open(dbFileName).CopyTo(writeStream);}LoadApplication(new App());}public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults){Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);base.OnRequestPermissionsResult(requestCode, permissions, grantResults);}}
}

不论 Android 的版本如何,将代码中的 dbFolder 设置为

System.Environment.SpecialFolder.LocalApplicationData

或者

System.Environment.SpecialFolder.Personal

总是安全的做法,这是因为当一个 App 安装到虚拟机或者真机后,在默认状态下,App 对上面提到的这两个文件夹总是有完全的访问权限。

上面的代码将判断文件是否存在作为是否重新拷贝的依据,这个依据有时并不充分。我们可以在 I2oT.db 中增加一个 MyVersion 的表,这个表可以由以下代码创建:

DROP TABLE IF EXISTS AppVersion;
CREATE TABLE AppVersion(Version  TEXT    NOT NULL DEFAULT "1.0.0.0",NeedCopy BOOLEAN NOT NULL DEFAULT 1
);
INSERT INTO AppVersion VALUES ("1.0.8.0",1);

创建之后,在每一次发布 Release 之前,手动地将 AppVersion 表的 Version 更新到编译的版本号,NeedCopy 设置为 True。在MainActivity.cs 中,除了判断数据库文件是否存在以外,读取一下 资产目录(xxx.Android/Assets)中的 I2oT.db 中的 AppVersion 表,如果 NeedCopy 为真,就关闭数据库连接,然后再将资产文件拷贝到 Environment.SpecialFolder.LocalApplicationData 文件夹中。

当然,还可以有更多的方法来判断是否需要拷贝数据库文件,这完全取决于 App 的功能需求。

实现的截图

在 App 的“设置”页中的“子设备物模型”是页面入口,点击进入到 SubsetModels 展示页,点击某一个产品型号,进入到物模型展示页,其后台的绑定数据来自于 NTUDefinitions 表,点击某一个属性,展示该属性的详情,如果该属性是枚举型的话,还通过 Picker 控件展示其可选的枚举值。

总结

虽然将本地数据库文件打包发布到 Android App 不是一个新话题了,但从某度上得到的搜索结果并不多,而且貌似都是从 stackoverflow 上翻译过来的,依托的版本也千差万别,在  Xamarin.Forms 环境中的使用例子更是少得可怜。

愿我的编码为这个问题增加一点可行的答案。欢迎指正,一同提升。

相关文章:

PLC-IoT 网关开发札记(5):将本地数据库作为资产打包发布到 App

App需求:保存物模型 什么是物模型 在项目开发中,用到了本地数据库,这个本地数据库记录了系统的物模型。所谓物模型就是对某一个设备的可操纵属性的定义,每一个设备包括了一个或者多个属性,通过获取这些属性的当前值可…...

固态硬盘优化设置

目录 前言: 关闭Windows Search 禁用系统保护(不建议) 不建议禁用系统保护原因 关闭碎片整理【机械硬盘】 提升固态硬盘速度 开启TRIM 合理使用固态硬盘的容量 正确关机 关闭开机自启 前言: 电脑配备固态硬盘就能一劳…...

SpringBoot跨域问题解决

前端访问后台接口时,浏览器报错,跨域无法访问。 报错信息如下: Response to preflight request doesnt pass access control check: No Access-Control-Allow-Origin header is present on the requested resource. 经过一番百度之后&#…...

FindMy技术与相机结合

FindMy是苹果公司提供的设备追踪服务,用来帮助用户定位丢失的设备。自苹果公司开放Findmy网络之后,FindMy技术便与各种生活设备相结合,比如与相机的结合。 想象一下,你正在外出办事或者旅行时,突然意识到相机丢了&…...

Windows WSL2 占用磁盘空间清理释放

目前工作中时常用到WSL2(Ubuntu20.04),在使用一段时间后会发现WSL2所占用磁盘空间越来越多,体现在WSL2之上安装Linux分发对应的vhdx虚拟磁盘文件体积越来越大,会占用Windows自身空间,即使手动清理了Linux分…...

2022 年全国职业院校技能大赛高职组云计算赛项试卷部分解析

2022 年全国职业院校技能大赛高职组云计算赛项试卷部分解析 【赛程名称】高职组-云计算赛项第一场-私有云【任务 1】私有云服务搭建[10 分]【题目 2】Yum 源配置[0.5 分]【题目 3】配置无秘钥 ssh[0.5 分]【题目 4】基础安装[0.5 分]【题目 5】数据库安装与调优[0.5 分]【题目 …...

2.C语言——控制语句

控制语句 1.分支语句/判断语句if 语句if...else 语句if...else if...else语句 switch语句 2.循环语句 while 语句 do...while 语句 for 语句 3.转向语句 break continue go to 1.分支语句/判断语句 if 语句 if(boolean_expression) { /* 如果布尔表达式为真将执行的语句 */ } …...

Linux网络之PXE高效批量装机、Kickstart全自动化安装

一. PXE网络装机简介和相关知识 1. 常见的三种系统安装方式和相关文件 ① 三种系统安装方式 u启动安装:在U盘中下载相关的安装系统及镜像文件,u盘插机安装 光驱安装:将带有所需系统的光盘放进电脑服务器中,按照官方引导装机 …...

react umi/max 页签(react-activation)

思路:通过react-activation实现页面缓存,通过umi-plugin-keep-alive将react-activation注入umi框架,封装页签组件最后通过路由的wrappers属性引入页面。 浏览本博客之前先看一下我的博客实现的功能是否满足需求,实现功能&#xf…...

计算机网络编程

一、计算机网络(概述、简介) 说起网络,相信大家都不陌生,把分散在不同地点的计算机设备,通过传输介质、通信设施和网络通信协议,实现资源共享和信息传输的系统,我们称之为:计算机网…...

【计算机网络实训】期末考题-路由重分发+三层交换机VLAN间路由

路由重分发三层交换机VLAN间路由 实验目的实验内容及步骤仿真配置环境搭建要求:实验步骤配置Switch0配置Switch1配置交换机Multilayer Switch 0路由器Router0上的配置路由器Router1的配置 测试PC0 自动获取地址成功,PC0 可 ping 通 switch0,网…...

git 常规操作及设置

git 常规操作及设置 Git是一个分布式版本控制系统,可以用来跟踪文件的修改历史并与其他人进行协作开发。下面是一些常见的Git操作及设置: 初始化仓库:使用命令git init在当前目录创建一个新的Git仓库。 克隆仓库:使用命令git clo…...

element中表格组件的row-class-name和class-name属性的使用以及无效处理

1.这两个属性的使用&#xff0c;row-class-name用在el-table标签上&#xff0c;class-name用在el-table-column标签上。两个属性即可绑定类名也可绑定函数 <!-- 这里是绑定函数&#xff0c;也可以绑定类名 --> <el-table :data"tableData" selection-chang…...

【AI理论知识】EM算法

基本定义 期望最大化算法&#xff08;Expectation-Maximization&#xff0c;EM算法&#xff09;是一种用于估计包含潜在变量的概率模型参数的迭代优化算法。EM算法的主要目标是在存在未观测数据或缺失数据的情况下&#xff0c;通过迭代地进行期望步骤&#xff08;E步&#xff…...

03 OSPF

参考文章 1 初步认识OSPF的大致内容(第三课)-CSDN博客 2...

node.js(express.js)+mysql实现注册功能

文章目录 实现步骤一、获取客户端提交到服务器的用户信息&#xff0c;对表单中的数据&#xff0c;进行合法性的效验 代码如下:二、检测用户名是否被占用三、对密码进行加密四、插入新用户&#xff08;完整代码&#xff09;总结 实现步骤 一、获取客户端提交到服务器的用户信息…...

AI绘画Stable Diffusion进阶使用

本文讲解&#xff0c;模型底模&#xff0c;VAE美化模型&#xff0c;Lora模型&#xff0c;hypernetwork。 文本Stable Diffusion 简称sd 欢迎关注 使用模型 C站&#xff1a;https://civitai.com/ huggingface&#xff1a;https://huggingface.co/models?pipeline_tagtext-to-…...

C 练习实例33 - 质数(素数)判断

题目&#xff1a;判断一个数字是否为质数。 程序分析&#xff1a;质数&#xff08;prime number&#xff09;又称素数&#xff0c;有无限个。一个大于1的自然数&#xff0c;除了1和它本身外&#xff0c;不能被其他自然数整除。 这题做过很多遍了&#xff0c;懂得都懂。 代码…...

docker环境下mongo副本集的部署及异常修复

最近更换了办公地点。部署在本地docker环境里的mongo数据库不能使用了。原因是本地的ip地址变更。以前的mongo副本集的配置需要更新。处理完后&#xff0c;索性重新记录一下mongo副本集在docker中的部署流程。 mongo的事务及副本集 我们先了解一下什么是事务&#xff0c;事务…...

【Java】Maven的安装与配置

初识Maven Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构 提供了一套标准化的构建流程&#xff08;编译&#xff0c;测试&#xff0c;打包&#xff0c;发布……&#xff09; 提供了一套依赖管理机制 标准化的…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...