一转眼2020年了哈,来西门子已经四五个月了。2020年实在不寻常,全国被新冠病毒搞得人心惶惶,过完年后在家呆了一两个月没上班,全国都被按下了暂停键,不过好在现在已经控制住了,慢慢大家都开始上班了,不管怎样,愿明天更美好!

好了进入正题,进入西门子这几个月很懵逼,西门子做的电力行业,跟以前工作有点不搭边,不过好在同事都挺好,乐于助人,也慢慢的习惯下来。进入西门子做的第一个项目就是电能质量装置的物联网功能测试,也就是现在很火的IOT。

我们的电能质量装置就是一个监控电力系统的装置,可以监控电能质量,电压跳变等等异常情况。我要做的就是将自己电脑作为服务器,装置的一些数据(电压,电流,谐波等等等等)通过IOT协议传到服务端(也就是我的电脑)。

测试步骤如图:

下面上代码吧:算是自己的一个日记了,因为很多工作上的东西还是需要保密的!

import csv
import pathlib
import re
import shutil
import subprocess
import time
from enum import Enumimport attr
from robot.api import logger
from robot.utils import robottypes
from selenium.webdriver.common.keys import Keysimport ExcelRobot
from ModbusRobot import ModbusRobotfrom ..enums.configuration import ACMNetworkType
from ..utils import Utils
from ._context import _Context
from .common import _CommonKeywords
from .configuration import _ConfigurationKeywords
from .device import _DeviceKeywords
from .seleniumext import _SeleniumExtKeywords# region Configuration Enums
class BrokerConnectMode(Enum):TLS = 1TLS_JWT = 6class MappingState(Enum):AGGREGATION = 1REALTIME = 2class ColumnNumber(Enum):NAME = 1REGISTER = 2TYPE = 3ACTUAL_VALUE = 10EXPECTED_VALUE = 11UNCERTAINTY = 12RESULT = 13# endregion@attr.s
class CNFMqtt(object):brokerIp = attr.ib(default='mqtt.eu1.mindsphere.io')topicName = attr.ib(default='c/{ClientID}/o/opcua/{VersionMS}/u/{Meas:d|File:d|Meta:m}')connectionMode = attr.ib(default=BrokerConnectMode.TLS_JWT)dnsIp = attr.ib(default='0.0.0.0')mappingState = attr.ib(default=MappingState.AGGREGATION)harmonicsVoltage = attr.ib(default=True)harmonicsCurrent = attr.ib(default=True)harmonicsPower = attr.ib(default=True)energyCounters = attr.ib(default=True)energyCountersTransmitCycle = attr.ib(default=1)class _MqttKeywords(_Context):def __init__(self, *args):super().__init__(*args)self._common = _CommonKeywords(*args)self._configuration = _ConfigurationKeywords(*args)self._device = _DeviceKeywords(*args)self._seleniumext = _SeleniumExtKeywords(*args)# region operation MQTT brokerdef start_mqtt_broker(self, brokerBatPath):"""Start Mqtt broker:param brokerBatPath: the .bat file of start exe:return:"""logger.info('Starting mqtt server....')subprocess.call('{}'.format(brokerBatPath))logger.info('Start mqtt server success!!')def stop_mqtt_broker(self):logger.info('Closing mqtt server....')subprocess.call('taskkill /F /IM Siemens.Energy.OPCUAPubSub.MQTT-Broker.exe')subprocess.call('taskkill /F /IM Siemens.Energy.OPCUAPubSub.Visualizer.exe')subprocess.call('taskkill /F /IM Siemens.Energy.OPCUAPubSub.ModbusTCP.exe')logger.info('Close mqtt server success!!')# endregion# region MQTT Measurement Validationdef copy_template_to_mqtt_testResult(self, newTemplateName, templateNameFormatString='MappingFile_MQTT_{:s}.xlsx'):"""Copy the template to TestResult folder:param newTemplateName is str ,like:MappingFile_MQTT_Q200.xlsx:return:"""currentDir = pathlib.Path.cwd()template_path = (currentDir.parent / 'TestResources' / 'MQTT' / templateNameFormatString.format(str(_DeviceKeywords.active_device().deviceType).split('.')[1])).resolve()if not pathlib.Path(template_path).is_file():raise Exception('The original test template does not exist !')reportFolder = pathlib.Path(self._common.get_output_folder()) / 'MqttTestResult'if not reportFolder.exists():reportFolder.mkdir(parents=True)shutil.copyfile(template_path, str(reportFolder / newTemplateName))reportPath = str(reportFolder / newTemplateName)if pathlib.Path(reportPath).is_file():logger.info('Report_path:{}'.format(reportPath))else:raise Exception('Test report excel not exist !!')return reportPathdef validate_mqtt_value(self, brokerIp=None, networkType=None, newTemplateName=None, startRow=None, endRow=None):"""Compare the read value and expected value ,judge it is pass or fail:param brokerIp is the mqtt server's ip address,like:192.168.0.36:param networkType:SINGLE_PHASE...:param testTemplateName:str, like Q200.xlsx:param startRow: look the template ,which row you want to start test:param endRow:look the template ,which row you want to end test:return: None"""brokerMbus = ModbusRobot()if brokerIp is not None:brokerMbus.open_modbus_connection(connectionType='TCP', host=brokerIp)else:ipAddress = Utils.get_own_ipAddress()brokerMbus.open_modbus_connection(connectionType='TCP', host=str(ipAddress))networkType = Utils.to_enum(networkType, ACMNetworkType)startRow = int(startRow) if startRow is not None else startRowendRow = int(endRow) if endRow is not None else endRowtemplate = self._common.get_output_folder().parent / 'MqttTestResult' / newTemplateNameif not pathlib.Path(template).is_file():raise Exception('Test template not exist !!')networkTypeToShortName = {ACMNetworkType.SINGLE_PHASE: '1P2W',ACMNetworkType.THREE_WIRE_THREE_PHASE_BALANCED: '3P3WBAL',ACMNetworkType.THREE_WIRE_THREE_PHASE_UNBALANCED_2L: '3P3W2I',ACMNetworkType.THREE_WIRE_THREE_PHASE_UNBALANCED_3L: '3P3W3I',ACMNetworkType.FOUR_WIRE_THREE_PHASE_BALANCED: '3P4WBAL',ACMNetworkType.FOUR_WIRE_THREE_PHASE_UNBALANCED: '3P4WUNB'}excel = ExcelRobot.ExcelRobot()registerValues = excel.read_column_values(excelname=str(template), sheetname=networkTypeToShortName[networkType], column=ColumnNumber.REGISTER.value, startrow=startRow, endrow=endRow)expectedValues = excel.read_column_values(excelname=str(template), sheetname=networkTypeToShortName[networkType], column=ColumnNumber.EXPECTED_VALUE.value, startrow=startRow, endrow=endRow)uncertaintyValues = excel.read_column_values(excelname=str(template), sheetname=networkTypeToShortName[networkType], column=ColumnNumber.UNCERTAINTY.value, startrow=startRow, endrow=endRow)valueTypes = excel.read_column_values(excelname=str(template), sheetname=networkTypeToShortName[networkType], column=ColumnNumber.TYPE.value, startrow=startRow, endrow=endRow)type_to_value = list(zip(valueTypes, registerValues))actualValues = []for tup in type_to_value:if 'Single-point'==tup[0]:res = brokerMbus.read_holding_data(address=(int(tup[1]) - 1))actualValues.append(res)elif 'Enumeration'==tup[0]:res = brokerMbus.read_holding_data(address=(int(tup[1]) - 1))actualValues.append(res)elif 'Measured value'==tup[0]:res = brokerMbus.read_holding_float(address=(int(tup[1]) - 1))actualValues.append(res)elif 'Energy counter'==tup[0]:res = brokerMbus.read_holding_double(address=(int(tup[1]) - 1))actualValues.append(res)elif 'Counter'==tup[0]:res = brokerMbus.read_holding_data(address=(int(tup[1]) - 1), mbusType='RAW', size=4)actualValues.append(res[-1])else:logger.info('The value type not support !')rowNumber = startRowfor value in actualValues:excel.write_excel_data(excelname=str(template), row=rowNumber, column=ColumnNumber.ACTUAL_VALUE.value, data=value, sheetname=networkTypeToShortName[networkType])rowNumber += 1errorOccured = Falseflag = startRowfor actual, expected, uncertainty in zip(actualValues, expectedValues, uncertaintyValues):try:flag += 1if isinstance(actual, str):raise AssertionError('actual value is str !')if isinstance(actual, float):if expected == 0:if not abs(actual - float(expected)) <= uncertainty:raise AssertionError('The actual value is out of error range !')else:if abs(actual - float(expected)) / expected >= uncertainty:raise AssertionError('The actual value is out of error range !')if isinstance(actual, int):if actual != int(expected):raise AssertionError('The actual value is not meet expected !')except AssertionError as err:logger.error(err)errorOccured = Trueexcel.write_excel_data(excelname=str(template), row=int(flag-1), column=ColumnNumber.RESULT.value, data='Fail', sheetname=networkTypeToShortName[networkType])excel.fill_colour(excelname=str(template), cell='M' + str(flag-1), backcolor='FFFF0000', sheetname=networkTypeToShortName[networkType])testedNames = excel.read_column_values(excelname=str(template), sheetname=networkTypeToShortName[networkType], column=ColumnNumber.NAME.value, startrow=startRow, endrow=endRow)logger.info("From name [{:s}] to [{:s}] test end !, from lines {} to {} !!".format(testedNames[0], testedNames[-1], startRow, endRow))if errorOccured:raise AssertionError('Not all value do meet conditions !!')brokerMbus.close_modbus_connection()def save_testResult_template(self, deviceType=None, testTemplateName=None, finalResultFileName=None):"""Clear empty folders and Add time to test result:param testTemplateName is str like:Mapdefile.xlsx:param finalResultFileName is str like:Mapdefile.xlsx:param deviceType:'Q100','Q200','P855':return:.xlsx file"""deviceType = deviceType if deviceType is not None else _DeviceKeywords.active_device().deviceTypetestTemplateName = str(testTemplateName) if testTemplateName is not None else testTemplateNameif isinstance(finalResultFileName, str):finalResultFileName = finalResultFileName.split('.')[0]resultFile = self._common.get_output_folder() / 'MqttTestResult' / testTemplateNameif resultFile.is_file():nowTime = time.strftime('%Y_%m_%d %H_%M_%S', time.localtime())shutil.copyfile(resultFile, str(pathlib.Path(resultFile).parent / '{:s}_{:s}_{:s}.xlsx'.format(nowTime, finalResultFileName, deviceType)))try:csvList = self._common.get_output_folder().rglob('*.csv')for csvFile in csvList:if csvFile.exists():csvFile.unlink()self._common.remove_empty_folders(folder=self._common.get_output_folder())resultFile.unlink()if resultFile.exists():raise AssertionError('resultFile still exixt !!')except AssertionError as err:logger.error(err)# endregion# region MQTT configurationdef get_mqtt_configuration(self):conf = CNFMqtt()self._sel.go_to(self._device.get_device_address('/ConfCommAMQP.html'))self._sel.wait_until_page_contains_element('id:footer')conf.brokerIp = self._sel.get_value('name:IpAddrBroker')conf.topicName = self._sel.get_value('name:Target0Name')conf.connectionMode = BrokerConnectMode(int(self._sel.get_selected_list_value('name:ConnMode')))conf.dnsIp = self._sel.get_value('name:IpAddrDNSSrv')flag = self._sel.find_element('xpath://table[2]/tbody/tr[2]/td[3]/*[@class="button"]').get_attribute('type')if flag == 'submit':conf.mappingState = MappingState.REALTIMEelse:conf.mappingState = MappingState.AGGREGATIONconf.harmonicsVoltage = int(self._sel.get_value('css:input[name="XmitHarm_0"]:checked')) == 2conf.harmonicsCurrent = int(self._sel.get_value('css:input[name="XmitHarmI_0"]:checked')) == 2conf.harmonicsPower = int(self._sel.get_value('css:input[name="XmitHarmPwr_0"]:checked')) == 2conf.energyCounters = int(self._sel.get_value('css:input[name="XmitIT_0"]:checked')) == 2conf.energyCountersTransmitCycle = self._sel.get_value('name:XmitCyclIT')return confdef set_default_mqtt_configuration(self):self.set_mqtt_configuration(**attr.asdict(CNFMqtt()))def set_mqtt_configuration(self, brokerIp=None, topicName=None, connectionMode=None, dnsIp=None, mappingState=None, harmonicsVoltage=None, harmonicsCurrent=None, harmonicsPower=None, energyCounters=None, energyCountersTransmitCycle=None, deviceCAPath=None, passwordCAPath=None, brokerCAPath=None):"""set parameter to mqtt web page:param brokerIp: the ipaddress of your pc which is used as the server:param topicName: not necessary,the name can be anyone:param connectionMode: input :TLS,TLS_JWT:param dnsIp: not necessary:param mappingState: allowed: 'AGGREGATION' or 'REALTIME':param harmonicsVoltage: bool:param harmonicsCurrent:bool:param harmonicsPower: bool:param energyCounters: bool:param energyCountersTransmitCycle: number ,type int:return:None"""conf = self.get_mqtt_configuration()brokerIp = Utils.get_own_ipAddress() if brokerIp is None else brokerIptopicName = topicName if topicName is not None else conf.topicNameconnectionMode = Utils.to_enum(connectionMode, BrokerConnectMode) if connectionMode is not None else conf.connectionModednsIp = dnsIp if dnsIp is not None else conf.dnsIpmappingState = Utils.to_enum(mappingState, MappingState) if mappingState is not None else conf.mappingStateharmonicsVoltage = robottypes.is_truthy(harmonicsVoltage) if harmonicsVoltage is not None else conf.harmonicsVoltageharmonicsCurrent = robottypes.is_truthy(harmonicsCurrent) if harmonicsCurrent is not None else conf.harmonicsCurrentharmonicsPower = robottypes.is_truthy(harmonicsPower) if harmonicsPower is not None else conf.harmonicsPowerenergyCounters = robottypes.is_truthy(energyCounters) if energyCounters is not None else conf.energyCountersenergyCountersTransmitCycle = int(energyCountersTransmitCycle) if energyCountersTransmitCycle is not None else conf.energyCountersTransmitCyclecaPath = pathlib.Path.cwd().parent / 'TestResources' / 'MQTT' / 'SICAM_PQ_NKG_Cert'deviceCAPath = deviceCAPath if deviceCAPath is not None else caPath / 'SICAM_PQ_NKG_Client_2k_2020.p12'passwordCAPath = passwordCAPath if passwordCAPath is not None else caPath / 'SICAM_PQ_4All.pin'brokerCAPath = brokerCAPath if brokerCAPath is not None else caPath / 'SICAM_PQ_NKG_CA_4k_2020.der'self._sel.go_to(self._device.get_device_address('/ConfCommAMQP.html'))self._sel.wait_until_page_contains_element('id:footer')if brokerIp != conf.brokerIp:self._sel.input_text('name:IpAddrBroker', str(brokerIp))if topicName != conf.topicName:self._sel.input_text('name:Target0Name', str(topicName))if connectionMode != conf.connectionMode:self._sel.select_from_list_by_value('name:ConnMode', connectionMode)if dnsIp != conf.dnsIp:self._sel.input_text('IpAddrDNSSrv', str(dnsIp))if harmonicsVoltage != conf.harmonicsVoltage:self._sel.select_radio_button('XmitHarm_0', str(int(harmonicsVoltage) + 1))if harmonicsCurrent != conf.harmonicsCurrent:self._sel.select_radio_button('XmitHarmI_0', str(int(harmonicsCurrent) + 1))if harmonicsPower != conf.harmonicsPower:self._sel.select_radio_button('XmitHarmPwr_0', str(int(harmonicsPower) + 1))if energyCounters != conf.energyCounters:self._sel.select_radio_button('XmitIT_0', str(int(energyCounters) + 1))if energyCountersTransmitCycle != conf.energyCountersTransmitCycle:self._sel.input_text('name:XmitCyclIT', '{:s}'.format(str(energyCountersTransmitCycle)))with self._seleniumext.wait_until_page_contains_element_after_pageload('id:footer'):self._sel.find_element('css:input[type="submit"][value="Send"]').send_keys(Keys.ENTER)with self._seleniumext.wait_until_page_contains_element_after_pageload('id:footer'):if self._seleniumext.is_any_text_present('modified'):self._sel.find_element('css:input[type="button"][value="Upload certificates"]').send_keys(Keys.ENTER)with self._seleniumext.wait_until_page_contains_element_after_pageload('id:footer'):self._seleniumext.wait_until_page_contains_any_element(locators=['name:Datei'])self._sel.choose_file('name:Datei', str(deviceCAPath))self._seleniumext.wait_until_page_contains_any_element(locators=['name:Datei2'])self._sel.choose_file('name:Datei2', str(passwordCAPath))self._seleniumext.wait_until_page_contains_any_element(locators=['name:Datei1'])self._sel.choose_file('name:Datei1', str(brokerCAPath))self._seleniumext.wait_until_page_contains_any_element(locators=['name:LoadBtn'])self._sel.find_element('name:LoadBtn').send_keys(Keys.ENTER)self._seleniumext.wait_until_page_contains_element_after_pageload('id:footer')logger.info('Upload certificate success!')if self._seleniumext.is_text_present(Utils.STR_MODIFIED):self._configuration.activate_configuration()self._sel.go_to(self._device.get_device_address('/ConfCommAMQP.html'))self._sel.wait_until_page_contains_element('id:footer')if mappingState == MappingState.REALTIME:mapFilePath = self._common.download_file(None, self._device.get_device_address('/ConfCommAMQPMapExport.html'), params={'Connection': 'close'})[1]if pathlib.Path(mapFilePath).is_file():self._modify_data_transmission_mode(mapFilePath)else:logger.info('IoTMapEx.csv not exist !!')self._sel.go_to(self._device.get_device_address('/ConfCommAMQP.html'))self._sel.wait_until_page_contains_element('id:footer')self._sel.wait_until_page_contains_element('name:BIm')self._sel.find_element('name:BIm').send_keys(Keys.ENTER)with self._seleniumext.wait_until_page_contains_element_after_pageload('name:Datei'):self._seleniumext.wait_until_page_contains_any_element(locators=['name:Datei'])self._sel.choose_file('name:Datei', str(mapFilePath))self._seleniumext.wait_until_page_contains_element_after_pageload('id:footer')self._sel.find_element('css:input[type="submit"]').send_keys(Keys.ENTER)self._seleniumext.wait_until_page_contains_any([Utils.SUCCESS_IMPORT_MAPPING])else:if conf.mappingState == MappingState.AGGREGATION:logger.info('State is already aggregation !')else:self._seleniumext.wait_until_page_contains_any_element(locators=['name:BDf'])self._sel.find_element('name:BDf').send_keys(Keys.ENTER)def _modify_data_transmission_mode(self, filePath):modifiedContent = []with open(filePath, 'r+', newline='') as csvFile:for row in list(csv.reader(csvFile, delimiter=';')):if row[0].startswith(('PQA2', 'PQA0')):row[-1] = '0'modifiedContent.append(row)else:modifiedContent.append(row)with open(filePath, 'w', newline='') as csvFile:csv.writer(csvFile, delimiter=';').writerows(modifiedContent)def validate_mqtt_configuration(self, brokerIp=None, topicName=None, connectionMode=BrokerConnectMode.TLS_JWT, dnsIp='0.0.0.0', mappingState=MappingState.AGGREGATION, harmonicsVoltage=False, harmonicsCurrent=False, harmonicsPower=False, energyCounters=False, energyCountersTransmitCycle=1):"""validate the mqtt configuration:param brokerIp: the ip address of the pc which you will used as server:param topicName: anymane:param connectionMode: allowed:TLS,TLS_JWT:param dnsIp: 0.0.0.0:param mappingState: allowed: 'AGGREGATION' or 'REALTIME':param harmonicsVoltage: bool:param harmonicsCurrent: bool:param harmonicsPower: bool:param energyCounters: bool:param energyCountersTransmitCycle: number ,type int:return:None"""brokerIp = Utils.get_own_ipAddress() if brokerIp is None else brokerIptopicName = CNFMqtt.topicName if topicName is None else topicNameconnectionMode = Utils.to_enum(connectionMode, BrokerConnectMode)dnsIp = str(dnsIp)mappingState = Utils.to_enum(mappingState, MappingState)harmonicsVoltage = robottypes.is_truthy(harmonicsVoltage)harmonicsCurrent = robottypes.is_truthy(harmonicsCurrent)harmonicsPower = robottypes.is_truthy(harmonicsPower)energyCounters = robottypes.is_truthy(energyCounters)energyCountersTransmitCycle = str(energyCountersTransmitCycle)logger.info('Validating configuration of mqtt configuration !!')self._sel.go_to(self._device.get_device_address('/ConfCommAMQP.html'))self._sel.wait_until_page_contains_element('id:footer')self._sel.textfield_value_should_be('name:IpAddrBroker', brokerIp)self._sel.textfield_value_should_be('name:Target0Name', topicName)self._sel.list_selection_should_be('name:ConnMode', str(connectionMode.value))self._sel.textfield_value_should_be('name:IpAddrDNSSrv', dnsIp)if mappingState == MappingState.AGGREGATION:self._sel.element_should_be_disabled('xpath://table[2]/tbody/tr[2]/td[3]/*[@class="button"]')else:self._sel.element_should_be_enabled('xpath://table[2]/tbody/tr[2]/td[3]/*[@class="button"]')self._sel.radio_button_should_be_set_to('XmitHarm_0', str(int(harmonicsVoltage) + 1))self._sel.radio_button_should_be_set_to('XmitHarmI_0', str(int(harmonicsCurrent) + 1))self._sel.radio_button_should_be_set_to('XmitHarmPwr_0', str(int(harmonicsPower) + 1))self._sel.radio_button_should_be_set_to('XmitIT_0', str(int(energyCounters) + 1))self._sel.textfield_value_should_be('name:XmitCyclIT', str(energyCountersTransmitCycle))# endregion# region change mapping filedef convert_mapping_file_to_10_12cycle_values(self, fileName=None):"""Change mapping file of MQTT Tool to 10_12cycle (prefix PAQ0):param fileName:like D:/MapDefFile_Q200.csv:return:None"""logger.info('Start change the Mapdefile.csv to 10-12 cycle !')if pathlib.Path(fileName).is_file():with open(fileName, 'r') as f:content = f.read()modifiedContent1 = re.sub('PQA2M', 'PQA0M', content)modifiedContent2 = re.sub('PQA4M', 'PQA0M', modifiedContent1)modifiedContent = re.sub('PQA0MFLK', 'PQA2MFLK', modifiedContent2)with open(fileName, 'w') as f:f.write(modifiedContent)else:logger.error('Maping Definition file is not exist!!')logger.info('Maping Defile :{} convert to 10-12 cycle success !'.format(fileName))def convert_mapping_file_to_aggregation_values(self, fileName=None):"""change the IOT maping defile to aggregation(prefix PQA2):param fileName:like D:/MapDefFile_Q200.csv:return:"""logger.info('Start change the Mapdefile.csv to aggregation !')if pathlib.Path(fileName).is_file():with open(fileName, 'r') as f:content = f.read()modifiedContent1 = re.sub('PQA0M', 'PQA2M', content)modifiedContent2 = re.sub(r';PQA2MMXU1.*?Hz', ';PQA4MMXU1$MX$Hz', modifiedContent1)modifiedContent = re.sub(r';PQA2MMXN1.*?Hz', ';PQA4MMXN1$MX$Hz', modifiedContent2)with open(fileName, 'w') as f:f.write(modifiedContent)else:logger.error('Maping Definition file is not exist!!')logger.info('Maping Defile: {} changed to aggregation success !'.format(fileName))# endregion

磕磕绊绊也算完成了吧,虽然有瑕疵,在此做个记录,希望下个项目顺顺利利,没有解决不了问题,一切都是时间问题!!

加油!

Python实现 IOT(物联网) 自动化测试相关推荐

  1. IoT物联网平台「设备影子」开发实战

    IoT物联网平台提供设备影子功能,在云端通过一个JSON文件持久化存储设备上报状态值和业务系统的期望值.每个设备有且只有一个设备影子,设备可以通过MQTT协议获取期望值desired和设置设备状态re ...

  2. HaaS学习笔记 | 终端设备接入和断开阿里云IoT物联网平台的明细教程

    [1]题目要求 [本教程视频]:终端设备连接阿里云物联网平台 [2]理论基础 aliyunIoT是HaaS轻应用扩展库中模块,能帮助厂商将设备安全地接入到阿里云IoT物联网平台,继而让设备可以被物联网 ...

  3. MQTT协议与阿里云IoT物联网平台

    1.MQTT协议介绍 1.1 MQTT协议 MQTT(消息队列遥测传输) 是基于 TCP/IP 协议栈而构建的支持在各方之间异步通信的消息协议.MQTT在空间和时间上将消息发送者与接收者分离,因此可以 ...

  4. 年度最佳 | 国产开源、多租户、数字孪生 IoT 物联网平台

    IoTSharp 是一个开源的物联网基础平台,集设备属性数据管理.遥测数据监测.RPC多模式远程控制.规则链设计引擎等强大能力,依据数字孪生概念将可见与不可见的物理设备统一孪生到数字世界,透过资产.产 ...

  5. 流计算风云再起 - PostgreSQL携PipelineDB力挺IoT(物联网)

    标签 PostgreSQL , pipelinedb , 流计算 , patch , bug , libcheck , zeromq , kafka , kinesis , IoT , 物联网 背景 ...

  6. 流计算风云再起 - PostgreSQL携PipelineDB力挺IoT(物联网), 大幅提升性能和开发效率...

    标签 PostgreSQL , pipelinedb , 流计算 , patch , bug , libcheck , zeromq , kafka , kinesis , IoT , 物联网 背景 ...

  7. 树莓派 Pico仅4美元, IoT物联网开发实战

    树莓派基金会发布了基于一款全新的 RP2040 芯片构建的首款微控制器级产品:Raspberry Pi Pico,售价仅 4 美元. 如果你使用过 Arduino 或支持 MicroPython 的开 ...

  8. iot物联网_物联网(IoT)简介

    iot物联网 Smart Homes 智能家居 How our life in the future may look like? Let us look at one possible scenar ...

  9. IDC MarketScape:华为云IoT物联网平台位居领导者象限

    [摘要] 华为云IoT物联网平台在能力.战略和市场表现方面,均位列业界领导者阵营. 在全球权威信息与分析咨询公司IDC最新发布的<IDC MarketScape: 中国公有云物联网平台2019年 ...

  10. 从0到1入门:7天玩转IoT物联网实战营丨IoT喊你加入学习之旅!

    "看不见"的物联网是何方神圣? 在1991年,美国MIT提出物联网概念,早期的物联网是指依托RFID技术和设备,按约定的通信协议与互联网结合,使物品信息实现智能化识别和管理,实现物 ...

最新文章

  1. python使用方法-Python的使用方法
  2. 转载:JSON技术的调研报告(四种常见的JSON格式对比及分析)
  3. qgraphicsitem鼠标移动事件阻塞_常用的DOM事件
  4. 建立和使用Maven项目骨架Archetype
  5. python高手能做什么_python高手们、能不能给新手写点心得,迷茫过、好方法、过渡期等等...
  6. 让数据库无惧灾难,华为云GaussDB同城双集群高可用方案正式发布!
  7. 缓存学习中未命中的缓存情况的处理
  8. 网易云课堂Java模拟面试笔记(31-40)
  9. C++小游戏 双人贪吃蛇
  10. java日期格式化 类_Java日期格式化(DateFormat类)
  11. LaTeX简历模版,自己瞎做的,请多指教
  12. csdn网友提出关于expdp exclude及impdp问题解答
  13. 华为轮值董事长郭平新年致辞:不经艰难困苦,何来玉汝于成
  14. java bitmap api,RoaringBitmap的使用
  15. 数据结构题目收录(一)
  16. cad渐变线怎么画_CAD中的图案渐变功能怎么使用 涨知识了
  17. zookeeper为什么是CP原则
  18. 应用层的HTTP和HTTPS
  19. HTC-VIVE手势识别
  20. [AcWing], 蒙德里安的梦想

热门文章

  1. scViewerX ActiveX 多功能文件查看器控件
  2. go libp2p kad dht
  3. 2021-05-10 关于vant按钮小程序点击后出现灰色背景去除方式
  4. java基于eclipse.swt实现内嵌浏览器
  5. Error: Flutter plugin not installed; this adds Flutter specific functionality. - Flutter
  6. ArcGIS基础实验操作100例--实验65按字段调整点符号方向
  7. Ubuntu mysql 重置密码
  8. 计算机软考高项(信息系统项目管理师)、中项(系统集成项目管理工程师),统计师中级的一些备考经验
  9. 计算机网络系列之集线器、交换机和路由器
  10. 从零开始实现基于go-zero框架的微服务电商项目(三)——gorm、redis、腾讯云SMS、validate、md5加密、日志输入到kafka的添加