电力设备模块更换APP开发文档
一、开发背景:
根据公司指示,有一批设备要进行内置模块安装,同时设备信息要和新装的模块信息进行绑定,即在现场换装的过程中,要记录模块的信息(资产编码)与设备的信息(资产编码),通过信息上传后,到达主站,由后台人员进行信息绑定。
前期,此项工作都是由施工人员对现场的条码进行抄写、拍照上传,但由于工作过程中容易出现失误,或者施工人员书写不规范,遗漏等等问题,造成设备绑定时频频出现错误情况,因此,特开发此App,目的是减轻施工人员的工作量,同时,避免因施工人员误操作或者手写错误,造成不必要的麻烦。
二、系统框架:
系统搭设:
本系统手机端搭设APP软件一套,用于信息采集,数据临时储存、数据上传、数据查询、参数设置以及人员注册、登陆等内容。当数据采集完毕后,会在本地手机中进行存储,等网络畅通或者工作完成后,由施工人员进行上传操作,此时,数据会按照一定的格式进行编码,通过WebService提供的接口上传到服务器中。
服务器端采用WebService来进行数据的接收和处理,将现场的数据信息按照一定的编码格式进行解码,然后储存到数据库中;同时,也能够满足后台管理端的数据请求,按照一定的编码将请求的数据进行编码,提供给后台人员进行查阅和下载。
后台管理端负责将请求的数据进行解码,完成数据查阅和下载,同时,也需要完成部分系统设置,参数、人员、权限等的管理行为。
三、详细说明:
1.结构说明:
本系统采用C#编程语言,数据库为SQLServer数据库,手机本地数据库采用Sqlite数据库。
手机端采用Xamarin.Forms+MVVM形式进行开发,主要包含条形码扫描、数据信息录入、信息查询、数据编码、解码等功能。数据本地存储采用Sqlite数据库进行本地数据暂存,数据采集完毕后需要对数据进行暂存,必要时对数据进行提取,并执行上传操作。
客户机端采用WPF+MVVM的形式进行开发,同时设计C#—Excel的数据操作功能,包括信息读取和写入,数据保存、编码及解码等功能。
服务器端使用腾讯云服务器,通过IIS服务搭建Web服务,对手机端及客户机端的数据请求和数据传输进行接收和信息发送,来完成数据的转存和读取,并响应数据读取的相关请求。
2.系统特点:
2.1.本套系统主要采用MVVM模式,实现代码与界面的分离;
2.2.系统手机端采用版本更新的模式,实现版本的自主更新下载,避免版本错乱问题;
2.3.整个系统架构采用MVVM+三层架构的主要模式,后期易于代码的二次开发和漏洞修改;
2.4.系统开发语言采用C#语言,开发简单,易于维护;
2.5.远程数据库采用SqlServer数据库,可存储大容量的数据记录;
2.6.数据库部分全部采用存储过程,软件开发过程中未使用一条SQL语句;
2.7.手机端本地数据库采用Sqlite进行临时性存储,通过增删改查完成数据的基本操作;
3.软件效果:
3.1.手机端效果:
界面效果
3.2.客户端效果:
3.3.服务端效果:
WebService效果
3.4.服务端效果:
4.部分代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommandService;
using System.Collections.ObjectModel;
using Xamarin.Forms;
using StringService;
using System.Xml.Linq;
using System.IO;
using Xamarin.Essentials;
using System.Threading;
//using SocketService;
using System.Net.Sockets;
using DataBaseService.SqliteHelper;
using PropertyChangeService;
using ModuleInstall.Services;
using AppTools_WS;
using Models;
namespace ModuleInstall.ViewModels
{
public class MainPageViewModel : PropertyChangedHelper
{
private Taiqu _tq = new Taiqu();
private Meter _meter = new Meter();
private User _User = new User();
private Recorder _recorder = new Recorder();
private MainCommand _meterScanCmd, _moduleScanCmd, _saveCmd, _uploadCmd;
private Action<string> _callBack;
//private Dal_Module _dalModule = new Dal_Module();
/// <summary>
/// 用于计数功能,记录设备列表增加的行数;
/// </summary>
public int Count { set; get; } = 0;
public int Index { set; get; } = 0;
public bool IsEnabled { set; get; }
public string TqName { set { _tq.Name = value; PropertyChange(nameof(TqName)); } get { return _tq.Name; } }
public string MeterNum { set { _meter.MeterNum = value; PropertyChange(nameof(MeterNum)); } get { return _meter.MeterNum; } }
public string ModuleNum { set { _meter.ModuleNum = value; PropertyChange(nameof(ModuleNum)); } get { return _meter.ModuleNum; } }
public string Account { set { _User.Account = value; PropertyChange(nameof(Account)); } get { return _User.Account; } }
public string Address { set { _recorder.Address = value; PropertyChange(nameof(Address)); } get { return _recorder.Address; } }
public MainCommand MeterScanCmd { get { return _meterScanCmd; } }
public MainCommand ModuleScanCmd { get { return _moduleScanCmd; } }
public MainCommand SaveCmd { get { return _saveCmd; } }
public MainCommand UploadCmd { get { return _uploadCmd; } }
public int ScrollIndex { set; get; }
public ObservableCollection<Meter> Meters { set; get; } = new ObservableCollection<Meter>();
private SqliteHelper Helper;
public MainPageViewModel()
{
}
public MainPageViewModel(Action<string> action)
{
InitDatabase();//初始化数据库;
_callBack = action;//回调函数进入;
_meterScanCmd = new MainCommand(MeterScan);
_moduleScanCmd = new MainCommand(ModuleScan);
_saveCmd = new MainCommand(Save);
_uploadCmd = new MainCommand(Upload2);
if (!File.Exists(SystemSettingPageViewModel.filePath) || new FileInfo(SystemSettingPageViewModel.filePath).Length == 0)
{
_callBack("更换台区、人员操作前,请先进行系统设置!");
}
}
/// <summary>
/// Json数据读取;从字符串读取到Object(必须为Object)对象;
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public SettingModel JsonRead(string filePath)
{
string jsonStr;
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (StreamReader sr = new StreamReader(fs))
{
jsonStr = sr.ReadToEnd();
}
}
return new JsonConvertHelper<SettingModel>().ToObject(jsonStr); //JsonConvert.DeserializeObject<SettingModel>(jsonStr);
}
catch (Exception ex)
{
throw ex;
}
}
public void InitDatabase()
{
Helper= new SqliteHelper();
}
/// <summary>
/// 获取指定电能表资产的Id号
/// </summary>
/// <param name="meterNum"></param>
/// <returns></returns>
int GetMeterId(string meterNum)
{
int flag;
try
{
flag = Helper.GetMeterId(meterNum);
}
catch (Exception ex)
{
throw ex;
}
return flag;
}
/// <summary>
/// 获取指定台区名称的台区Id号;
/// </summary>
/// <param name="tqName"></param>
/// <returns></returns>
int GetTqId(string tqName)
{
return Helper.GetTqId(tqName);
}
/// <summary>
/// 获取从本地配置信息中读取的
/// </summary>
/// <returns></returns>
int GetUserId(string Account)
{
///从本地配置文件中获取姓名;
return Helper.GetUserId(Account);
}
/// <summary>
/// 电能表扫码方法,扫码后,将码加入到Meters集合列表;
/// </summary>
/// 定义电表数量,模块数量
public int MeterCount = 0, ModuleCount = 0;
async void MeterScan()
{
try
{
bool flag = false;//判断扫码重复的标志;
string num = await new ScanToString().BoxScan();
if (num.Length > 15)
{
for (int i = 0; i < Meters.Count; i++)
{
if (Meters[i].MeterNum == num)
{
flag = true;//如果,Meters集合中,存在此模块条码信息,则资产重复,标志为true;
}
}
if (!flag)
{
//var meter = Meters.Where(m => m.ModuleNum == "").First();
if (MeterCount>=ModuleCount)
{
Meters.Insert(0,new Meter() { MeterNum = num });
MeterCount++;
}
else
{
Meters[ModuleCount-MeterCount-1].MeterNum = num;//当前序号处的模块条码由空修改为当前扫描的条码;
MeterCount++; //Count++;//计数指针位增加,后期删除时,指针不能小于0;
//Meters.OrderBy<Meter,int>(Meter => Meter.Id).Min();
}
_callBack(null);//回调函数,更新ListView
//num = await new ScanToString().BoxScan();
}
}
}
catch
{
throw;
}
}
async void ModuleScan()
{
try
{
bool flag = false;//判断扫码重复的标志;
string num = await new ScanToString().BoxScan();
if (num.Length > 15)
{
for (int i = 0; i < Meters.Count; i++)
{
if (Meters[i].ModuleNum == num)
{
flag = true;//如果,Meters集合中,存在此模块条码信息,则资产重复,标志为true;
}
}
if (!flag)
{
//var meter = Meters.Where(m => m.ModuleNum == "").First();
if(MeterCount<=ModuleCount)
{
Meters.Insert(0,new Meter() { ModuleNum = num });
ModuleCount++;
}
else
{
Meters[MeterCount-ModuleCount-1].ModuleNum = num;//当前序号处的模块条码由空修改为当前扫描的条码;
ModuleCount++; //Count++;//计数指针位增加,后期删除时,指针不能小于0;
//Meters.OrderBy<Meter,int>(Meter => Meter.Id).Min();
}
_callBack(null);//回调函数,更新ListView
//num = await new ScanToString().BoxScan();
}
}
}
catch
{
throw;
}
}
/// <summary>
/// 删除本地数据库中的表与模块信息(同时删除);如果不删除,加表,保存,或者在删除,中间回出现问题;
/// </summary>
/// <param name="meter"></param>
public void MeterDelete(string meter)
{
int meterid = Helper.GetMeterId(meter);
int recorderId = Helper.GetRecorderId(meterid);
if(meterid >0)
Helper.Delete<Meter>(meterid);
if(recorderId>0)
Helper.Delete<Recorder>(recorderId);
}
/// <summary>
/// 保存到本地
/// </summary>
void Save()
{
bool isSave = false;
try
{
if (Meters.Count > 0)
{
SettingModel setModel =null;
try
{
setModel = JsonRead(SystemSettingPageViewModel.filePath);
}
catch
{
_callBack("更换台区、人员操作前,请先进行系统设置!");//回调函数,显示提示信息;
}
Account = setModel.Account;
TqName = setModel.TqName;
Address = setModel.Address;
int tqid = GetTqId(TqName);
foreach (var meter in Meters)
{
if (meter.ModuleNum != null)
{
meter.TqId = tqid;
//判断是否在数据库中存在此表资产,如果不存在,返回结果为0,则添加资产
if (GetMeterId(meter.MeterNum) == 0)
{
Helper.Insert<Meter>(meter);
Helper.Insert<Recorder>(
new Recorder()
{
MeterId = GetMeterId(meter.MeterNum),
UserId = GetUserId(Account),
IsUpload = false,//上传标志为false,上传以后更改为true;
SaveTime = DateTime.Now,
Address = Address
});
}
}
else
{
_callBack("数据结构不完整,请补充完整后保存!");
break;
}
isSave = true;
}
///-------------还需要确认,两种资产不能够进入
//Console.WriteLine("--------------------------------------------保存成功!--------------------------------------");
if(isSave)
{
_callBack("数据保存成功!");
}
}
else
{
_callBack("数据不能为空,请重试操作!");
}
}
catch (NullReferenceException)
{
_callBack("更换台区、人员操作前,请先进行系统设置!");
}
catch (Exception ex)
{
_callBack($"{ex.ToString()}");
}
}
/// <summary>
///第一版: 上传数据到远程服务器,需要从本地数据库中读取,直接操作远程数据库插入数据
/// </summary>
void Upload1()
{
////读取本地数据库中的为上传信息,IsUpload==false,转换成Json字符串;
////string meterStr=null,recorderStr = null;
//ObservableCollection<Recorder> recorders = Helper.GetRecorders(false);
//if (recorders.Count > 0)
//{
// foreach (var recorder in recorders)
// {
// _meter = Helper.GetMeter(recorder.MeterId);
// ModuleNum = _meter.ModuleNum;
// MeterNum = _meter.MeterNum;
// _dalModule.ModuleAdd(ModuleNum, MeterNum, TqName, Account, recorder.SaveTime, Address);
// //----------------此处需要在本地保存一下,更改一下上传状态;-------------------
// recorder.IsUpload = true;
// Helper.Update(recorder);//更新一下上传状态;
// //recorderStr += new JsonConvertHelper<Recorder>().ToString(recorder);
// //meterStr += new JsonConvertHelper<Meter>().ToString(new SqliteHelper().GetMeter(recorder.MeterId));
// //Console.WriteLine($"保存时间:{recorder.SaveTime},电能表号:{MeterNum},模块号:{ModuleNum}");
// }
// _callBack("数据上传成功!");
//}
//else
// _callBack("数据为空,请核实后上传!");
}
/// <summary>
/// 通过Socketk客户端进行数据上传//第三版--服务器内存不足,socket无法使用;
/// </summary>
//void Upload3()
//{
// //读取本地数据库中的为上传信息,IsUpload==false,转换成Json字符串;
// //string meterStr=null,recorderStr = null;
// bool isSave=false;
// SocketClient client = null;
// ObservableCollection<Recorder> recorders = Helper.GetRecorders(false);
// ObservableCollection<History> histories = new ObservableCollection<History>();
// JsonConvertHelper<ObservableCollection<History>> jh = new JsonConvertHelper<ObservableCollection<History>>();
// string Msg =string.Empty;
// try
// {
// if (recorders.Count > 0)
// {
// //首先要判断是否有数据,才能进行下一步的链接;
// client = new SocketClient();
// foreach (var recorder in recorders)
// {
// _meter = Helper.GetMeter(recorder.MeterId);
// ModuleNum = _meter.ModuleNum;
// MeterNum = _meter.MeterNum;
// //_dalModule.ModuleAdd(ModuleNum, MeterNum, TqName, Account, recorder.SaveTime, Address);
// History history = new History()
// {
// ModuleNum = this.ModuleNum,
// MeterNum = this.MeterNum,
// TqName = this.TqName,
// Account = this.Account,
// SaveTime = recorder.SaveTime,
// Address = this.Address
// };// SaveTime = recorder.SaveTime,
// //----------------此处需要在本地保存一下,更改一下上传状态;-------------------
// recorder.IsUpload = true;
// Helper.Update(recorder);//更新一下上传状态;
// //recorderStr += new JsonConvertHelper<Recorder>().ToString(recorder);
// //meterStr += new JsonConvertHelper<Meter>().ToString(new SqliteHelper().GetMeter(recorder.MeterId));
// //Console.WriteLine($"保存时间:{recorder.SaveTime},电能表号:{MeterNum},模块号:{ModuleNum}");
// histories.Add(history);
// isSave = true;
// }
// ///将历史数据的对象集合进行序列化;然后将字符串传输到服务器;
// Msg = jh.ToString(histories);
// if (client.SendMsg(Msg))
// _callBack("数据上传成功!");
// else _callBack("数据上传失败!");//回调函数,显示上传结果;
// client.Client.Close();
// }
// else
// _callBack("数据为空,请核实后上传!");
// }
// catch(SocketException)
// {
// _callBack("上传服务器未开启,请开启后再上传数据!");
// }
//}
/// <summary>
/// 第二版:通过WebService上传,服务器端已架设Webservice成功;
/// </summary>
void Upload2()
{
//读取本地数据库中的为上传信息,IsUpload==false,转换成Json字符串;
//string meterStr=null,recorderStr = null;
ObservableCollection<Recorder> recorders = Helper.GetRecorders(false);
ObservableCollection<History> histories = new ObservableCollection<History>();
JsonConvertHelper<ObservableCollection<History>> jh = new JsonConvertHelper<ObservableCollection<History>>();
string Msg = string.Empty;
try
{
if (recorders.Count > 0)
{
//首先要判断是否有数据,才能进行下一步的链接;
foreach (var recorder in recorders)
{
_meter = Helper.GetMeter(recorder.MeterId);
ModuleNum = _meter.ModuleNum;
MeterNum = _meter.MeterNum;
History history = new History()
{
ModuleNum = this.ModuleNum,
MeterNum = this.MeterNum,
TqName = this.TqName,
Account = this.Account,
SaveTime = recorder.SaveTime,
Address = this.Address
};// SaveTime = recorder.SaveTime,
histories.Add(history);
}
///将历史数据的对象集合进行序列化;然后将字符串传输到服务器;
Msg = jh.ToString(histories);
AppToolsSoapClient msc = new AppToolsSoapClient(AppToolsSoapClient.EndpointConfiguration.AppToolsSoap12);
var rtnValue = msc.DataUpLoad(Msg);
if(rtnValue==true)
{
new Thread(
() =>
{
foreach (var recorder in recorders)
{
//----------------此处需要在本地保存一下,更改一下上传状态;-------------------
recorder.IsUpload = true;
Helper.Update(recorder);//更新一下上传状态;
//recorderStr += new JsonConvertHelper<Recorder>().ToString(recorder);
//meterStr += new JsonConvertHelper<Meter>().ToString(new SqliteHelper().GetMeter(recorder.MeterId));
//Console.WriteLine($"保存时间:{recorder.SaveTime},电能表号:{MeterNum},模块号:{ModuleNum}");
}
}).Start();
_callBack("数据上传成功!");
}
else _callBack("数据上传失败!");
}
else
_callBack("数据为空,请核实后上传!");
}
catch (Exception ex)
{
_callBack(ex.ToString ());
}
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using CommandService;
using System.Collections.ObjectModel;
using DataBaseService.SqliteHelper;
using StringService;
using AppTools_WS;
using System.Linq;
using System.Threading.Tasks;
using Models;
namespace ModuleInstall.ViewModels
{
public class SystemSettingPageViewModel:PropertyChangeService.PropertyChangedHelper
{
private AppToolsSoapClient _client;
private Company _company=new Company ();
private const string fileName = "Config.txt";
private MainCommand _writeSettingCmd;// readCmd;
private Action
private SettingModel _settingModel=new SettingModel();
private SqliteHelper sqliteHelper;
private ObservableCollection
private ObservableCollection
private ObservableCollection
private ObservableCollection
public ObservableCollection
public ObservableCollection
public ObservableCollection
public ObservableCollection
public Company Comy { get { return _company; } set { _company = value; } }
public ObservableCollection
public MainCommand WriteSettingCmd { get { return _writeSettingCmd; } }
public SettingModel SetModel { set { _settingModel = value; } get { return _settingModel; } }
public static readonly string filePath=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), fileName);
public SystemSettingPageViewModel(Action
{
if (_client == null)
_client = new AppToolsSoapClient(AppToolsSoapClient.EndpointConfiguration.AppToolsSoap12);
GetCompanies();
GetGroupes();
if (!File.Exists(filePath))
{
File.Create(filePath);
}
_writeSettingCmd = new MainCommand(JsonWrite);
CallBack = callBack;
//readCmd = new MainCommand(Read);
}
///
/// 通过供电公司名称,获取下属单位供电所的所有内容;
///
///
///
public ObservableCollection
{
ObservableCollection
ArrayOfString strings = _client.GetGdses(companyName);
foreach (string str in strings)
{
gds.Add(str);
}
return gds;
}
public void GetGroupes()
{
try
{
ArrayOfString strings = _client.GetGroups();
foreach (string str in strings)
{
GroupCollection.Add(str);
}
//GroupCollection=new Dal_Group().GroupList();
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 通过供电所名称,获取下属单位供电台区的所有内容;
/// </summary>
/// <param name="companyName"></param>
/// <returns></returns>
public ObservableCollection<string> GetTaiQues(string gdsName)
{
try
{
ObservableCollection<string> taiqus = new ObservableCollection<string>();
ArrayOfString strings = _client.GetTaiQues(gdsName);
foreach (string str in strings)
{
taiqus.Add(str);
}
return taiqus;
}catch(Exception ex)
{ throw ex; }
}
/// <summary>
/// 获取所有供电公司的信息;
/// </summary>
/// <param name="companyName"></param>
/// <returns></returns>
void GetCompanies()
{
try
{
ArrayOfString strings = _client.GetCompanies();
foreach (string str in strings)
{
CompanyCollection.Add(str);
}
}
catch
{
throw;
}
}
public ObservableCollection<string> GetUsers(string groupName)
{
ObservableCollection<string> os= new ObservableCollection<string>();
ArrayOfString Users = _client.GetUsers(groupName);
foreach (string o in Users)
{
os.Add(o.ToString());
}
return os;
}
/// <summary>
/// 将系统设置的信息对象序列化为Json字符串;
/// </summary>
public void JsonWrite()
{
try
{
if(File.Exists(filePath))
{
File.Delete (filePath);
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
using (StreamWriter sw = new StreamWriter(fs))
{//向配置文件写入配置信息;
string jsonStr = new JsonConvertHelper<SettingModel>().ToString(SetModel);
//JsonConvert.SerializeObject(SetModel);
sw.Write(jsonStr);
sw.Flush();
sw.Close();
fs.Close();
}
}
}
sqliteHelper = new SqliteHelper();
sqliteHelper.InitData(SetModel.CompanyName, SetModel.GdsName, SetModel.TqName, SetModel.Account);
CallBack("保存成功!");
}
catch(Exception ex)
{
CallBack($"保存失败!{ex.ToString()}");
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Models;
using StringService;
using System.Collections.ObjectModel;
using AppTools_WS;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using CommandService;
using DataBaseService.SqliteHelper;
namespace ModuleInstall.ViewModels
{
public class DataSearchViewModel
{
private AppToolsSoapClient _client;
//private Dal_History _dalHistory =new Dal_History ();
private Action
///
/// 未上传数量
///
public int UnUploadCount { set; get; }
///
/// 今日已上传数量
///
public int TodayUploadCount { set; get; }
///
/// 历史上传数量
///
public int UploadHistoryCount { set; get; }
public SettingModel SetModel { set; get; }
public ObservableCollection<History> UploadHistoryList { get; set; }
public DataSearchViewModel(Action<string> action)
{
try
{
_action = action;
if (_client == null)
_client = new AppToolsSoapClient (AppToolsSoapClient.EndpointConfiguration.AppToolsSoap12);
SetModel = JsonRead(SystemSettingPageViewModel.filePath);//读取配置文件;
UnUploadCount = new SqliteHelper().GetUnUploadCount();//从本地获取未上传信息数量;
UploadHistoryCount = _client.GetUploadedHistoryCount(SetModel.Account);//获取上传历史记录数量;
TodayUploadCount = _client.GetTodayUploadedCount(SetModel.Account);//获取今天上传数量(指定操作人员);
}
catch {
_action("应用使用前,请先进行系统设置!");
}
}
/// <summary>
/// Json数据读取;从字符串读取到Object(必须为Object)对象;
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public SettingModel JsonRead(string filePath)
{
StreamReader sr = null;
try
{
sr = new StreamReader(filePath);
string jsonStr = sr.ReadToEnd();
return new JsonConvertHelper<SettingModel>().ToObject(jsonStr); //JsonConvert.DeserializeObject<SettingModel>(jsonStr);
}
catch (Exception ex)
{
throw ex;
}
finally {
sr.Close();
}
}
}
}
开发人:QQ-1009714648