diff --git a/Base/model/scada_6.0.png b/Base/model/scada_6.0.png
new file mode 100644
index 000000000..9e1b393ca
Binary files /dev/null and b/Base/model/scada_6.0.png differ
diff --git a/Base/model/scada_6.0.xml b/Base/model/scada_6.0.xml
new file mode 100644
index 000000000..4e5b27ffe
--- /dev/null
+++ b/Base/model/scada_6.0.xml
@@ -0,0 +1,1430 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Config/DemoProject.en-GB/BaseXML/KPType.xml b/Config/DemoProject.en-GB/BaseXML/KPType.xml
index 0b080d9a8..222ffbb83 100644
--- a/Config/DemoProject.en-GB/BaseXML/KPType.xml
+++ b/Config/DemoProject.en-GB/BaseXML/KPType.xml
@@ -180,4 +180,16 @@
KpSimulator.dll
Device simulator
+
+ 231
+ MQTT
+ KpMqtt.dll
+ Interacting with controllers via MQTT protocol
+
+
+ 232
+ OPC UA
+ KpOpcUa.dll
+ Interacting with controllers using OPC UA specification
+
\ No newline at end of file
diff --git a/Config/DemoProject.ru-RU/BaseXML/KPType.xml b/Config/DemoProject.ru-RU/BaseXML/KPType.xml
index 06f83a4f8..d01cb4d0e 100644
--- a/Config/DemoProject.ru-RU/BaseXML/KPType.xml
+++ b/Config/DemoProject.ru-RU/BaseXML/KPType.xml
@@ -180,4 +180,16 @@
KpSimulator.dll
Симулятор устройства
+
+ 231
+ MQTT
+ KpMqtt.dll
+ Взаимодействие с контроллерами по протоколу MQTT
+
+
+ 232
+ OPC UA
+ KpOpcUa.dll
+ Взаимодействие с контроллерами по спецификации OPC UA
+
\ No newline at end of file
diff --git a/Config/EmptyProject.en-GB/BaseXML/KPType.xml b/Config/EmptyProject.en-GB/BaseXML/KPType.xml
index 0b080d9a8..222ffbb83 100644
--- a/Config/EmptyProject.en-GB/BaseXML/KPType.xml
+++ b/Config/EmptyProject.en-GB/BaseXML/KPType.xml
@@ -180,4 +180,16 @@
KpSimulator.dll
Device simulator
+
+ 231
+ MQTT
+ KpMqtt.dll
+ Interacting with controllers via MQTT protocol
+
+
+ 232
+ OPC UA
+ KpOpcUa.dll
+ Interacting with controllers using OPC UA specification
+
\ No newline at end of file
diff --git a/Config/EmptyProject.ru-RU/BaseXML/KPType.xml b/Config/EmptyProject.ru-RU/BaseXML/KPType.xml
index 06f83a4f8..d01cb4d0e 100644
--- a/Config/EmptyProject.ru-RU/BaseXML/KPType.xml
+++ b/Config/EmptyProject.ru-RU/BaseXML/KPType.xml
@@ -180,4 +180,16 @@
KpSimulator.dll
Симулятор устройства
+
+ 231
+ MQTT
+ KpMqtt.dll
+ Взаимодействие с контроллерами по протоколу MQTT
+
+
+ 232
+ OPC UA
+ KpOpcUa.dll
+ Взаимодействие с контроллерами по спецификации OPC UA
+
\ No newline at end of file
diff --git a/Config/HelloWorld/BaseXML/KPType.xml b/Config/HelloWorld/BaseXML/KPType.xml
index 0b080d9a8..222ffbb83 100644
--- a/Config/HelloWorld/BaseXML/KPType.xml
+++ b/Config/HelloWorld/BaseXML/KPType.xml
@@ -180,4 +180,16 @@
KpSimulator.dll
Device simulator
+
+ 231
+ MQTT
+ KpMqtt.dll
+ Interacting with controllers via MQTT protocol
+
+
+ 232
+ OPC UA
+ KpOpcUa.dll
+ Interacting with controllers using OPC UA specification
+
\ No newline at end of file
diff --git a/Config/HelloWorld_Linux/BaseXML/KPType.xml b/Config/HelloWorld_Linux/BaseXML/KPType.xml
index 0b080d9a8..222ffbb83 100644
--- a/Config/HelloWorld_Linux/BaseXML/KPType.xml
+++ b/Config/HelloWorld_Linux/BaseXML/KPType.xml
@@ -180,4 +180,16 @@
KpSimulator.dll
Device simulator
+
+ 231
+ MQTT
+ KpMqtt.dll
+ Interacting with controllers via MQTT protocol
+
+
+ 232
+ OPC UA
+ KpOpcUa.dll
+ Interacting with controllers using OPC UA specification
+
\ No newline at end of file
diff --git a/HowToBuild.txt b/HowToBuild.txt
index 78778afb7..9da144106 100644
--- a/HowToBuild.txt
+++ b/HowToBuild.txt
@@ -10,12 +10,16 @@ Microsoft Visual Studio 2017 is needed. Visual Studio Community is OK.
Log,
Report,
ScadaData,
+ ScadaAgent,
ScadaComm,
OpenKPs,
ScadaServer,
- ScadaAdmin,
+ OpenModules,
ScadaWeb,
- ScadaScheme.
+ OpenPlugins,
+ ScadaScheme,
+ ScadaTable,
+ ScadaAdmin.
Switch to the Release configuration so that the references are correct.
diff --git a/LangPack/readme.txt b/LangPack/readme.txt
index 3a7a3c91b..daafd4bb9 100644
--- a/LangPack/readme.txt
+++ b/LangPack/readme.txt
@@ -2,14 +2,15 @@ Language Installation
---------------------
1. Choose the language and open the appropriate folder of the language pack.
2. Copy the language files from the folder SCADA of the language pack to the installation directory of Rapid SCADA maintaining the hierarchy of the directories.
-3. Run SCADA-Administrator application.
-4. Go to Settings -> Language and type the culture name, e.g. es-LA, and click OK button.
+3. Run the Administrator application.
+4. Go to Tools -> Language and type the culture name, e.g. es-ES, and click OK button.
5. Restart Rapid SCADA applications to apply changes.
How to Localize UI to Any Language
----------------------------------
Most of Rapid SCADA applications contain special localization files. Usually they are located in a Lang folder:
+C:\SCADA\ScadaAgent\Lang
C:\SCADA\ScadaAdmin\Lang
C:\SCADA\ScadaComm\Lang
C:\SCADA\ScadaSchemeEditor\Lang
@@ -17,5 +18,9 @@ C:\SCADA\ScadaServer\Lang
C:\SCADA\ScadaTableEditor\Lang
C:\SCADA\ScadaWeb\lang
-To add a new language support, create copies of *.en-GB.xml files and give the file names according to your culture. In this example en means English language and GB is Great Britain.
-Using your favorite text editor (Notepad++ is OK) translate the phrases in the created xml files. Then open the Administrator application, go to Settings > Language menu and enter your localization name, for example, es-LA
+To add a new language support, create copies of *.en-GB.xml files and give the file names according to your culture. In this example "en" means English language and "GB" is Great Britain.
+Using your favorite text editor (Notepad++ is OK) translate the phrases in the created xml files. Then open the Administrator application, go to Tools -> Language menu and enter your localization name, for example, es-ES
+
+Keep unchanged in XML:
+* Numbers in curly braces, for example, {0}
+* Text prefixed with "&", for example, " >
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/lang/ScadaData.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/lang/ScadaData.pt-BR.xml
new file mode 100644
index 000000000..eef6d54d3
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/lang/ScadaData.pt-BR.xml
@@ -0,0 +1,105 @@
+
+
+
+ Rapid SCADA
+ Informação
+ Pergunta
+ Erro
+ Advertência
+ Erro
+ Erro:
+ Exceção sem tratamento
+ As conigurações foram modificadas. Salvar alterações?
+ Arquivo não encontrado.
+ O diretório não existe.
+ O arquivo {0} não foi encontrado.
+ O diretório {0} não existe.
+ A base de dados da configuração no diretório de formato DAT
+ A base de dados da configuração no diretório de formato DAT não existe.
+ Selecionar base de dados da configuração no diretório de formato DAT
+ Erro ao carregar configurações da aplicação
+ Erro ao salvar configurações de aplicação
+ Erro ao carregar configurações de conexão do servidor
+ Erro ao salvar configurações de conexão do servidor
+ Erro de dados
+ É requerido um número inteiro.
+ É requerido um número inteiro entre {0} e {1}.
+ É requerido um número real.
+ É requerido um valor não vazio.
+ É necessário informar data e hora.
+ O comprimento da string deve ser de {0} símbolos ou menos.
+ "{0}" não é um número.
+ String não é hexadecimal.
+ Erro ao carregar imagem de arquivo:{0}
+ Erro ao carregar hiperlink de arquivo:{0}
+ Formato de arquivo incorreto.
+ Nenhum dado
+ Privilégios insuficientes.
+ Valor incorreto de nó XML "{0}".
+ Valor incorreto de atributo de XML "{0}".
+ Valor incorreto do parâmetro "{0}".
+ Nó de XML "{0}" não encontrado dentro do nó "{1}".
+ Sim
+ Não
+ Valor de comando incorreto.
+ Dados de comando incorretos.
+ Tipos de Comandos
+ Valores de Comando
+ Tipos de canal
+ Linhas de comunicação
+ Canais de saída
+ Tipos de evento
+ Formatos de número
+ Fórmulas
+ Canais de entrada
+ Interface
+ Dispositivos
+ Tipos de dispositivo
+ Objetos
+ Quantidades
+ Direitos
+ Papéis
+ Unidades
+ Usuários
+ Continuar pendente
+ Pausado
+ pausa pendente
+ Em funcionamento
+ Começar pendente
+ Parado
+ Parado pendente
+ não instalado
+
+
+ Sequência não é hexadecimal.
+ O código do computador contém registro de erro..
+ Erro ao decodificar código do computador
+ Erro ao recuperar informação da chave de registro
+ Comprimento da chave de registro incorreta.
+ Informação da chave de registro incorreta.
+ Chave de registro válida
+ Chave de registro válida. Data de expiração {0}
+ Chave de registro inválida
+ A chave de registro expirou {0}
+ Chave de registro vazia
+ Chave de registro contém registro de erros
+ Chave de registro incorreta
+ Não foi encontrado o arquivo da chave de registro {0}.
+ Erro ao carregar o código do computador
+ Erro ao salvar o código do computador
+ Erro ao carregar a chave de registro
+ Erro ao salvar a chave de registro
+ Verificar " {0} " registro:
+ Código do computador: {0}
+ O registro falhou.
+ O registro falhou para "{0}".
+
+
+ Código do computador
+ Reinicie o serviço para obter o código do computador.
+ Chave de registro
+ Compre uma chave permanente
+ Obter uma chave de avaliação
+ Status da chave
+
+
\ No newline at end of file
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/lang/ScadaWeb.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/lang/ScadaWeb.pt-BR.xml
new file mode 100644
index 000000000..db5fd0763
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/lang/ScadaWeb.pt-BR.xml
@@ -0,0 +1,127 @@
+
+
+
+ Usuário não está logado.
+ Data dos dados solicitados incorreta.
+
+
+ O servidor não está disponível.
+ Nome de usuário ou senha incorreta.
+ Papel de usuário ilegal.
+
+
+ Confimação
+ Você está certo disso?
+
+
+ OK
+ Sim
+ Não
+ Executar
+ Cancelar
+ Fechar
+
+
+ Ativar som
+ Mudo
+ Nenhuma notificação
+
+
+ Notificações
+ Encerrar sessão
+ Tela cheia
+ Menu principal
+ Visualizações
+ Colapsar Menu
+ Visualização normal
+
+
+ Gerar relatório "{0}" pelo usuário {1}
+ Tela de informação incorreta.
+ Data de início incorreta.
+ Data de finalização incorreta.
+ Data de início precisa ser menor ou igual a data de finalização.
+ A duração máxima do período é de {0} dias.
+ A duração máxima do período é de {0} meses.
+
+
+ Números de canal não estão especificados.
+ Incompatibilidade entre o número de canais e ID de visualizações (Views).
+
+
+ Erro ao carregar a configuração da visualização (View)
+ Erro ao salvar a configuração da visualização (View)
+ Erro ao carregar ajustes da visualização (View) da base de dados de configuração
+
+
+ Erro ao carregar a configuração da aplicação Web
+ Erro ao salvar a configuração da aplicação Web
+
+
+ Sobre - Rapid SCADA
+ Webstation
+ http://rapidscada.org
+
+
+ Erro da aplicação - Rapid SCADA
+ Erro da aplicação
+ Ocorreu um erro de aplicação. Caso se repita com frequência, favor entrar em contacto con o suporte.
+ Detalhes:
+
+
+ Iniciar sessão - Rapid SCADA
+ http://rapidscada.org
+ Nome do usuário
+ Senha
+ Lembrar-me
+ Iniciar sessão
+
+
+ Seu navegador está desatualizado..<br/>Favor atualizar para um navegador mais moderno.
+ Os cookies estão desabilitados. Por favor, habilite-os.
+
+
+ Ninguna vista - Rapid SCADA
+ A visualização(View) solicitada não existe ou o usuário não possui direitos suficientes para acessá-la.
+
+
+ Relatórios - Rapid SCADA
+ Relatórios disponíveis
+ Nenhum relatório disponível
+
+
+ Perfil do Usuário - Rapid SCADA
+ Perfil do Usuário
+ Nome do Usuário:
+ Papel:
+ Direitos Gerais
+ Ver todos os dados:
+ Controlar todos os dispositivos:
+ Configuração do sistema:
+ Sim
+ Não
+
+
+ Visualização - Rapid SCADA
+
+
+ Link externo - Rapid SCADA
+
+
+ Relatórios
+ Adminisração
+ Configurações
+ Registro
+ Plugins
+ Sobre
+
+
+ Uma violação de segurança foi detectada. Entre em contato imediatamente com o administrador do sistema.
+
+
+ Relatório com ID= {0} não encontrado.
+ Relatório com ID= {0} possui tipo de especificação inesperado.
+ Janela de dados com ID= {0} não encontrada.
+ Janela de dados com ID= {0} possui tipo de especificação inesperado.
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Chart/lang/PlgChart.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Chart/lang/PlgChart.pt-BR.xml
new file mode 100644
index 000000000..622d125ff
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Chart/lang/PlgChart.pt-BR.xml
@@ -0,0 +1,40 @@
+
+
+
+ Atenção! Canais em excesso podem afetar o rendimento.
+ Adicionar
+ Remover
+ Informação
+ Objeto:
+ Dispositivo:
+ Ver:
+
+
+ Dados minutos
+ Gerado:
+ Canais de entrada:
+ Tempo
+ Canal {0}
+
+
+ Chart - Rapid SCADA
+ Gerado
+
+
+ Relatório de dados por minuto- Rapid SCADA
+ Relatório de dados por minuto
+ De
+ Para
+ Adicionar canais
+ Canais selecionados:
+ Os canais de entrada não estão selecionados
+ Baixar Relatório
+ Gerando relatório. Por favor aguarde...
+
+
+ Selecionar Canais - Rapid SCADA
+ Carregando...
+ Não é possível carregar a visualização
+ Canais de entrada não encontrados
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/ChartPro/lang/PlgChartPro.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/ChartPro/lang/PlgChartPro.pt-BR.xml
new file mode 100644
index 000000000..ad6b23e9b
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/ChartPro/lang/PlgChartPro.pt-BR.xml
@@ -0,0 +1,64 @@
+
+
+
+ Data de início incorreta.
+ Data de término incorreta.
+ A data de início deve ser menor ou igual à data de término.
+ A duração máxima do período é de {0} dias.
+ Os números dos canais não são especificados.
+ Incompatibilidade no número de canais e visualizar IDs.
+ Aviso! Muitos canais podem afetar o desempenho.
+ Adicionar
+ Remover
+ Info
+ Objeto:
+ Dispositivo:
+ Visualização:
+
+
+ Canais do gráfico - Rapid SCADA
+ Adicionar canais
+ Canais selecionados:
+ Canais de entrada não estão selecionados
+ Voltar aos canais selecionados
+ Carregando...
+ Não foi possível carregar a visualização
+ Canais de entrada não encontrados
+
+
+ Exportação de gráficos - Rapid SCADA
+ Exportar gráfico para PDF
+ Exportar gráfico para PNG
+ Exportar dados para o Excel
+
+
+ Período do gráfico - Rapid SCADA
+ Das
+ Às
+ Dia anterior
+ Próximo dia
+ Semana anterior
+ Semana que vem
+ Mês anterior
+ Próximo mês
+
+
+ Chart Pro - Rapid SCADA
+ Gerado
+ Selecionar canais
+ Período de mudança
+ Mostrar dados
+ Ocultar dados
+ Exportar
+ Fechar
+
+
+ Carregamento de dados...
+ Erro ao carregar dados
+ Gerado
+ Tempo
+ Ampliar zoom
+ Reduzir zoom
+ Redefinir zoom
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Config/lang/PlgConfig.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Config/lang/PlgConfig.pt-BR.xml
new file mode 100644
index 000000000..14d97d760
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Config/lang/PlgConfig.pt-BR.xml
@@ -0,0 +1,58 @@
+
+
+
+ Plugins instalados - Rapid SCADA
+ Plugins instalados
+ Nome
+ Descrição
+ Estado
+ Ativar
+ Desativar
+ Inativo
+ Ativo
+ Não carregado
+ Plugin ativado com êxito.
+ Plugin desativado com êxito.
+ Versão
+
+
+ Configuração da aplicação Web - Rapid SCADA
+ Configuração da aplicação Web
+ Conexão ao servidor
+ Servidor
+ Porta
+ Timeout
+ ms
+ Usuário
+ Senha
+ Deixar em branco para manter inalterado.
+ Parâmetros de aplicação
+ Cultura
+ Exemplo: pt-BR. Vazio é o valor preestabelecido.
+ Frequência de atualização de dados
+ ms
+ Frequência de atualização do arquivo
+ ms
+ Mostrar conteúdo de eventos
+ Chart gap
+ sec
+ Página de início
+ Exemplo: ~/plugins/MyPlugin/MyPage.aspx
+ Habilitar comandos
+ Senha requerida para enviar comando
+ Permitir salvar usuário registrado
+ Carregar a configuração da visualização da base de dados
+ Compartilhar estatísticas despersonalizadas com os desenvolvedores
+ Chart plugin
+ Plugin de comandos
+ Plugin de reconhecimento
+ Salvar configuração
+ Plugin desconhecido
+ Os seguintes campos estão incorretos:
+ Configuração salva com êxito.
+
+
+ Aplicação Web
+ Plugins
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Dashboard/lang/PlgDashboard.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Dashboard/lang/PlgDashboard.pt-BR.xml
new file mode 100644
index 000000000..c44db6442
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Dashboard/lang/PlgDashboard.pt-BR.xml
@@ -0,0 +1,28 @@
+
+
+
+ Erro ao carregar a lista do painel
+ Erro ao carregar a configuração do painel
+ Erro ao carregar o nome do painel
+ Chave de painel inválida.
+
+
+ Erro ao carregar dados
+
+
+ Exportar para PDF
+ Painel com a chave especificada não encontrada.
+
+
+ Erro ao receber a configuração do painel
+ Nenhum widget suporta exportação para PDF
+
+
+ Dashboards - Rapid SCADA
+ Dashboards
+ Nenhum painel disponível
+
+
+ Dashboards
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/ElasticReport/lang/PlgElasticReport.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/ElasticReport/lang/PlgElasticReport.pt-BR.xml
new file mode 100644
index 000000000..58d1ff5bc
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/ElasticReport/lang/PlgElasticReport.pt-BR.xml
@@ -0,0 +1,46 @@
+
+
+
+ Erro ao carregar a configuração de relatório elástico
+ Não foi possível receber a configuração elástica do relatório.
+ A configuração está ausente no XML.
+ O ID da seção está ausente.
+ O ID da seção está duplicado.
+
+
+ Período de trabalho:
+ Gerado:
+ Erro ao gerar seção com ID "{0}"
+ N/a
+ Índice de coluna incorreto
+ Encontro
+ Tempo
+ Nome
+ Total
+ Média
+
+
+ Pelo menos um dos seguintes arquivos de fonte não encontrado:
+
+
+ Erro ao carregar estilos de documento PDF
+ Erro ao carregar o estilo "{0}"
+
+
+ Relatório elástico - Rapid SCADA
+ Relatório elástico
+ De
+ À
+ De
+ À
+ Formato de saída:
+ Excel
+ PDF
+ HTML
+ Obter relatório
+ Gerando o relatório. Por favor, espere...
+
+
+ Fechar
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Map/lang/PlgMap.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Map/lang/PlgMap.pt-BR.xml
new file mode 100644
index 000000000..ecc0018a7
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Map/lang/PlgMap.pt-BR.xml
@@ -0,0 +1,8 @@
+
+
+
+ Clicado em
+ Mostrar detalhes
+ Erro ao atualizar os dados atuais
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Notification/lang/PlgNotification.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Notification/lang/PlgNotification.pt-BR.xml
new file mode 100644
index 000000000..92b1c39ba
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Notification/lang/PlgNotification.pt-BR.xml
@@ -0,0 +1,14 @@
+
+
+
+ Instruções
+ Reconhecer
+ Registo
+ O plug-in de notificação requer registro.
+
+
+ Instruções do evento - Rapid SCADA
+ Evento não encontrado.
+ Nenhuma informação disponível.
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Registration/lang/PlgRegistration.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Registration/lang/PlgRegistration.pt-BR.xml
new file mode 100644
index 000000000..98cd3b829
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Registration/lang/PlgRegistration.pt-BR.xml
@@ -0,0 +1,15 @@
+
+
+
+ Registro - Rapid SCADA
+ {0} Registro
+ Codigo do computador
+ Chave de registro
+ Compre uma chave permanente
+ Obter uma chave de teste
+ Estado da chave
+ Salvar chave
+ Dados de registro perdidos.
+ Tipo de dados de registro não coincidente.
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Scheme/lang/PlgScheme.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Scheme/lang/PlgScheme.pt-BR.xml
new file mode 100644
index 000000000..bcab9b272
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Scheme/lang/PlgScheme.pt-BR.xml
@@ -0,0 +1,17 @@
+
+
+
+ Esquema - Rapid SCADA
+ Ajustar à tela
+ Ajustar à largura
+ Reduzir zoom
+ Ampliar zoom
+
+
+ Rapid SCADA
+ Erro ao carregar esquema. Tente recarregá-lo
+ Recarregar
+ Não foi possível enviar o comando
+ Erro ao atualizar dados do esquema
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Store/lang/PlgStore.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Store/lang/PlgStore.pt-BR.xml
new file mode 100644
index 000000000..b6048f30f
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Store/lang/PlgStore.pt-BR.xml
@@ -0,0 +1,43 @@
+
+
+
+ Baixar
+
+
+ Informações do plug-in - Rapid SCADA
+ Geral
+ Screenshots
+ Descrição
+ Preço
+ Clique aqui para comprar
+ http://rapidscada.org/download-all-files/purchase-module/
+ Autor
+ Paginas web
+ Pagina inicial
+ Documentação
+ Baixar
+
+
+ Não foi possível receber informações do plug-in
+ Grátis
+ Pessoal
+ O negócio
+
+
+ Download de plugins - Rapid SCADA
+ Download de plugins
+ Nome
+ Descrição
+ Preço
+ Autor
+
+
+ Não foi possível receber a lista de plugins
+ Nenhum plug-in encontrado
+ Novo
+ Mais informações
+ Grátis
+ Pessoal
+ O negócio
+
+
diff --git a/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Table/lang/PlgTable.pt-BR.xml b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Table/lang/PlgTable.pt-BR.xml
new file mode 100644
index 000000000..03c25f0fc
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/ScadaWeb/plugins/Table/lang/PlgTable.pt-BR.xml
@@ -0,0 +1,149 @@
+
+
+
+ Erro ao carregar visualização da tabela do arquivo
+ Erro ao salvar visualização da tabela no arquivo
+
+
+ Item Information
+ Input Channel
+ Channel
+ Object
+ Device
+ Output Channel
+ Channel
+ Object
+ Device
+ Close
+
+
+ &File
+ New
+ Open...
+ Save
+ Save As...
+ Exit
+ &Help
+ About
+ New Table (Ctrl+N)
+ Open Table (Ctrl+O)
+ Save Table (Ctrl+S)
+ Refresh Configuration Database
+ Add Item
+ Add Empty Item
+ Move Item Up
+ Move Item Down
+ Delete Selected Items
+ Show Item Info
+ Add All Items
+ Input channels
+ Output channels
+ Press Enter or double-click a node to add it to the table. Right-click a device node to display the context menu.
+ Input Channel
+ Output Channel
+ Caption
+ Hidden
+ {0} - Table Editor
+ Table Title
+ Table Views (*.tbl)|*.tbl|All Files (*.*)|*.*
+ The table view has been modified. Save the changes?
+ The configuration database not found
+ Empty device
+ Error loading the configuration database
+ Error filling channel tree
+ Error loading form state
+ Error saving form state
+
+
+
+ Eventos
+ Todos os Eventos, {0}
+ {0}, {1}
+ Gerado
+ Número
+ Data e Hora
+ Objetos
+ Dispositivo
+ Canal
+ Descrição
+ Ack
+
+
+ Dados por hora
+ {0}, {1}, {2}
+ Gerado
+
+
+ Eventos
+
+
+ Comando - Rapid SCADA
+ Canal de Saída {0} não encontrado.
+ Senha incorreta.
+ Privilégios insuficientes.
+ Valor de comando incorreto.
+ Dados de comando incorretos.
+ Canal de saída:
+ Objeto:
+ Dispositivo:
+ Senha
+ Valor
+ Comando
+ Dados
+ String
+ Hexadecimal
+ O comando está em fila para sua execução.
+ A janela será encerra após
+ sec.
+ Não é possível enviar o comando. O servidor não está disponível.
+ Comando rejeitado pelo servidor.
+
+
+ Reconhecimento do evento - Rapid SCADA
+ Evento não encontrado.
+ Não é possível enviar o reconhecimento. Servidor não disponível.
+ Reconhecimento rejeitado pelo servidor.
+ Número:
+ Data e hora:
+ Objeto:
+ Dispositivo:
+ Canal:
+ Descrição:
+ Reconhecido:
+ por {0}
+ Clique em OK para confirmar o evento.
+
+
+ Todos os eventos
+ Eventos por visualização
+ Número
+ Data e hora
+ Objeto
+ Dispositivo
+ Canal
+ Descrição
+ Reconhecido
+ Não há eventos
+ Carregando...
+
+
+ Erro ao atualizar eventos
+
+
+ Dia selecionado:
+ Dia anterior:
+ Anterior.
+ Item
+ Atual
+ Canal de entrada:
+ Canal de saída:
+ Objeto:
+ Dispositivo:
+ Quantidade:
+ Unidade:
+
+
+ Erro ao atualizar dados atuais
+ Erro ao atualizar dados por hora
+
+
diff --git a/LangPack/v5.7/Portuguese/info.txt b/LangPack/v5.7/Portuguese/info.txt
new file mode 100644
index 000000000..53a682604
--- /dev/null
+++ b/LangPack/v5.7/Portuguese/info.txt
@@ -0,0 +1,6 @@
+Language : Portuguese
+Culture name: : pt-BR
+Rapid SCADA version : 5.7.1
+Rapid SCADA applications : Webstation
+Author : AGPR5, agpr5@agpr5.com
+Updated by : Joao Vitor Cesar
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/lang/ScadaData.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/lang/ScadaData.es-ES.xml
new file mode 100644
index 000000000..6f9cfc201
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/lang/ScadaData.es-ES.xml
@@ -0,0 +1,105 @@
+
+
+
+ Rapid SCADA
+ Información
+ Pregunta
+ Error
+ Advertencia
+ Error
+ Error:
+ Excepción no soportada
+ Los ajustes se han modificado. ¿Salve los cambios?
+ Archivo no encontrado.
+ El directorio no existe.
+ Archivo {0} no encontrado.
+ Directorio {0} no existe.
+ Base de datos de configuración en directorio de formato DAT
+ Base de datos de configuración en directorio de formato DAT no existe.
+ Seleccione la base de datos de configuración en el directorio del formato DAT
+ Error al cargar los ajustes de la aplicación
+ Error al guardar los ajustes de la aplicación
+ Error al cargar los ajustes de conexión del servidor
+ Error al guardar los ajustes de conexión del servidor
+ Error de datos
+ Se requiere un número entero.
+ Se requiere un número entero de {0} a {1}.
+ Se requiere un número real.
+ Se requiere un valor, no puede quedar vacío.
+ Se requieren fecha y hora.
+ La longitud de la cadena debe ser {0} símbolos o menos.
+ "{0}" no es un número.
+ La cadena no es hexadecimal.
+ Error al cargar imagen de archivo:{0}
+ Error que carga hipervínculo de archivo:{0}
+ Formato del archivo incorrecto.
+ No hay datos
+ Derechos insuficientes.
+ Valor incorrecto de nodo XML "{0}".
+ Valor incorrecto de atributo de XML "{0}".
+ Valor incorrecto del parámetro "{0}".
+ Nodo de XML "{0}" no encontrado dentro del nodo "{1}".
+ Sí
+ No
+ Valor del comando incorrecto.
+ Datos del comando incorrectos.
+ Tipos de Comando
+ Valores de Comando
+ Tipos del canal
+ Líneas de comunicación
+ Canales de la salida
+ Tipos de eventos
+ Formatos del número
+ Fórmulas
+ Canales de la entrada
+ Interfaz
+ Dispositivos
+ Tipos del dispositivo
+ Objetos
+ Cantidades
+ Derechos
+ Roles
+ Unidades
+ Usuarios
+ sigue pendiente
+ pausado
+ pausa pendiente
+ corriendo
+ comienza pendiente
+ detenido
+ detención pendiente
+ no instalado
+
+
+ La secuencia no es hexadecimal.
+ El código de computadora contiene registro de error
+ Error al descodificar código de computadora
+ Error al recuperar la información de la clave de registración
+ La longitud de la clave de registración es incorrecta.
+ La información de clave de registración es incorrecta.
+ La clave de registración es válida
+ La clave de registración es válida. La fecha de expiración es {0}
+ La clave de registración no es válida
+ La clave de registración ha caducado {0}
+ La clave de registración está vacía
+ La clave de registración contiene registro de errores
+ La clave de registración es incorrecta
+ No se encontró el archivo de clave de registración {0}.
+ Error al cargar el código del equipo
+ Error al guardar el código de la computadora
+ Error al cargar la clave de registración
+ Error al guardar la clave de registración
+ Compruebe registración "{0}":
+ Código de la computadora: {0}
+ La registración falló.
+ La registración falló para "{0}".
+
+
+ Codigo de computadora
+ Reinicie el servicio para obtener el código de la computadora.
+ Clave de registración
+ Compre una llave permanente
+ Obtenga una clave de prueba
+ Estado de la clave
+
+
\ No newline at end of file
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/lang/ScadaWeb.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/lang/ScadaWeb.es-ES.xml
new file mode 100644
index 000000000..d7b2bd50f
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/lang/ScadaWeb.es-ES.xml
@@ -0,0 +1,127 @@
+
+
+
+ Usuario no logeado.
+ Fecha incorrecta de los datos solicitados.
+
+
+ El servidor no está disponible.
+ Nombre de usuario o contraseña incorrecta.
+ Rol de usuario ilegal.
+
+
+ Confirmación
+ ¿Esta Ud. seguro?
+
+
+ OK
+ Sí
+ No
+ Ejecutar
+ Cancelar
+ Cerrar
+
+
+ Activar sonido
+ Silencioso
+ No hay notificaciones
+
+
+ Notificaciones
+ Cerrar sesión
+ Pantalla completa
+ Menú principal
+ Vistas
+ Colapsar Menú
+ Vista normal
+
+
+ Generar "{0}" informe por el usuario {1}
+ Plantilla de informe incorrecta.
+ Fecha de inicio incorrecta.
+ Fecha de finalización incorrecta.
+ La fecha de inicio debe ser menor o igual que la fecha de finalización.
+ La duración máxima del período es de {0} días.
+ La duración máxima del período es de {0} meses.
+
+
+ Los números de canal no están especificados .
+ Incompatibilidad entre número de canales e ID de vistas.
+
+
+ Error al cargar la configuración de la vista
+ Error al guardar la configuración de la vista
+ Error al cargar la configuración de la vista desde la base de datos de configuración
+
+
+ Error al cargar la configuración de la aplicación web
+ Error al guardar la configuración de la aplicación web
+
+
+ Acerca - Rapid SCADA
+ Webstation
+ http://rapidscada.org
+
+
+ Error de la aplicación - Rapid SCADA
+ Error de la aplicación
+ Se ha producido un error de aplicación. Si se repite con frecuencia, póngase en contacto con el soporte.
+ Detalles:
+
+
+ Iniciar sesión - Rapid SCADA
+ http://rapidscada.org
+ Nombre de usuario
+ Contraseña
+ Recuérdeme
+ Login
+
+
+ Su navegador no está actualizado.<br/>Actualícese a un navegador moderno.
+ Las cookies están deshabilitadas. Por favor, habilítelas.
+
+
+ Ninguna vista - Rapid SCADA
+ La vista solicitada no existe o Ud. no tiene derechos de acceso suficientes.
+
+
+ Informes - Rapid SCADA
+ Informes disponibles
+ No hay informe/s disponibles
+
+
+ Perfil de usuario - Rapid SCADA
+ Perfil de usuario
+ Nombre de usuario:
+ Rol:
+ Derechos Generales
+ Vea todos los datos:
+ Controle todos los dispositivos:
+ Configuración del sistema:
+ Sí
+ No
+
+
+ Vista - Rapid SCADA
+
+
+ Enlace externo - Rapid SCADA
+
+
+ Informes
+ Administración
+ Configuración
+ Registro
+ Plugins
+ Acerca de
+
+
+ Se ha detectado una violación de seguridad. Póngase inmediatamente en contacto con el administrador del sistema.
+
+
+ Informe con ID={0} no encontrado.
+ El informe con ID={0} tiene un tipo de especificación inesperado.
+ Ventana de datos con ID={0} no encontrado.
+ Ventana de datos con ID={0} tiene un tipo de especificación inesperado.
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Chart/lang/PlgChart.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Chart/lang/PlgChart.es-ES.xml
new file mode 100644
index 000000000..9c301c95d
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Chart/lang/PlgChart.es-ES.xml
@@ -0,0 +1,40 @@
+
+
+
+ ¡Advertencia! Demasiados canales pueden afectar el rendimiento.
+ Añadir
+ Borrar
+ Información
+ Objeto:
+ Dispositivo:
+ Ver:
+
+
+ Datos minutos
+ Generado:
+ Canales de entrada:
+ Tiempo
+ Canal {0}
+
+
+ Chart - Rapid SCADA
+ Generado
+
+
+ Informe de datos por minuto - Rapid SCADA
+ Informe de datos por minuto
+ De
+ Para
+ Añadir canales
+ Canales seleccionados:
+ Los canales de entrada no están seleccionados
+ Descargar Reporte
+ Generando del reporte. Por favor espera...
+
+
+ Seleccionar Canales - Rapid SCADA
+ Cargando...
+ No se puede cargar la vista
+ Canales de entrada no encontrados
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/ChartPro/lang/PlgChartPro.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/ChartPro/lang/PlgChartPro.es-ES.xml
new file mode 100644
index 000000000..440d60187
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/ChartPro/lang/PlgChartPro.es-ES.xml
@@ -0,0 +1,64 @@
+
+
+
+ Fecha de inicio incorrecta.
+ Fecha de finalización incorrecta.
+ La fecha de inicio debe ser menor o igual que la fecha de finalización.
+ La duración máxima del período es {0} días.
+ Los números de canal no están especificados.
+ No coincide el número de canales y los IDs de vistas.
+ ¡Advertencia! Demasiados canales pueden afectar el rendimiento.
+ Agregar
+ Quitar
+ Informacion
+ Objeto:
+ Dispositivo:
+ Vista:
+
+
+ Canales de gráficos - Rapid SCADA
+ Agregar canales
+ Canales seleccionados:
+ Los canales de entrada no están seleccionados
+ Volver a los canales seleccionados
+ Cargando...
+ No se puede cargar la vista
+ Canales de entrada no encontrados
+
+
+ Exportar gráficos - Rapid SCADA
+ Exportar gráfico a PDF
+ Exportar gráfico a PNG
+ Exportar datos a Excel
+
+
+ Período del gráfico - Rapid SCADA
+ Desde
+ hasta
+ Día previo
+ Día siguiente
+ Semana pasada
+ Semana próxima
+ Mes previo
+ Mes siguiente
+
+
+ Chart Pro - Rapid SCADA
+ Generado
+ Seleccionar canales
+ Cambiar período
+ Mostrar datos
+ Ocultar datos
+ Exportar
+ Cerrar
+
+
+ Cargando datos...
+ Error al cargar datos
+ Generado
+ Tiempo
+ Acercarse
+ Alejarse
+ Reiniciar zoom
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Config/lang/PlgConfig.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Config/lang/PlgConfig.es-ES.xml
new file mode 100644
index 000000000..c3582620f
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Config/lang/PlgConfig.es-ES.xml
@@ -0,0 +1,58 @@
+
+
+
+ Plugins instalados - Rapid SCADA
+ Plugins instalados
+ Nombre
+ Descripción
+ Estado
+ Activar
+ Desactivar
+ Inactivo
+ Activo
+ No cargado
+ Plugin activado satisfactoriamente.
+ Plugin desactivado satisfactoriamente.
+ Versión
+
+
+ Configuración de la aplicación Web - Rapid SCADA
+ Configuración de la aplicación Web
+ Conexión al servidor
+ Servidor
+ Puerto
+ Tiempo excedido
+ ms
+ Usuario
+ Contraseña
+ Dejar en blanco para no cambiar.
+ Parámetros de la aplicación
+ Cultura
+ Ejemplo: en-GB. Lo predeterminado es dejarlo vacío.
+ Frecuencia de actualización de datos
+ ms
+ Frecuencia de actualización de archivos
+ ms
+ Mostrar conteo de eventos
+ Intervalo en Gráfica
+ seg
+ Página de inicio
+ Ejemplo: ~/plugins/MyPlugin/MyPage.aspx
+ Habilitar comandos
+ Requerir contraseña para enviar comandos
+ Permitir recordar al usuario registrado
+ Cargar la configuración de la vista desde la base de datos
+ Compartir estadísticas despersonalizadas con los desarrolladores
+ Plugin de Gráficos
+ Plugin de Comandos
+ Plugin de acusar recibo
+ Guardar configuración
+ Plugin desconocido
+ Los siguientes campos son incorrectos:
+ Configuración guardada correctamente.
+
+
+ Aplicación Web
+ Instalado
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Dashboard/lang/PlgDashboard.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Dashboard/lang/PlgDashboard.es-ES.xml
new file mode 100644
index 000000000..99f2fd2bc
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Dashboard/lang/PlgDashboard.es-ES.xml
@@ -0,0 +1,28 @@
+
+
+
+ Error al cargar la lista del tablero
+ Error al cargar la configuración del tablero
+ Error al cargar el nombre del tablero
+ Clave de tablero inválida.
+
+
+ Error al cargar datos
+
+
+ Exportar a PDF
+ Tablero con la clave especificada no encontrado.
+
+
+ Error al recibir la configuración del tablero
+ No hay widgets compatibles con la exportación a PDF
+
+
+ Tableros - Rapid SCADA
+ Tableros (Dashboards)
+ Ningún tablero disponible
+
+
+ Tableros
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/ElasticReport/lang/PlgElasticReport.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/ElasticReport/lang/PlgElasticReport.es-ES.xml
new file mode 100644
index 000000000..af62947e0
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/ElasticReport/lang/PlgElasticReport.es-ES.xml
@@ -0,0 +1,46 @@
+
+
+
+ Error al cargar la configuración de informe elástico
+ No se puede recibir la configuración de informe elástico.
+ Falta la configuración en XML.
+ Identificación de sección ausente.
+ Identificador de sección duplicado.
+
+
+ Período de trabajo:
+ Generado:
+ Error al generar sección con ID "{0}"
+ No disponible
+ Índice de columna incorrecto
+ Fecha
+ Hora
+ Nombre
+ Total
+ Promedio
+
+
+ Al menos uno de los siguientes archivos de fuentes no encontrado:
+
+
+ Error al cargar estilos de documentos PDF
+ Error al cargar estilo "{0}"
+
+
+ Informe elástico - Rapid SCADA
+ Informe elástico
+ Desde
+ hasta
+ Desde
+ hasta
+ Formato de salida:
+ Excel
+ PDF
+ HTML
+ Obtener informe
+ Generando el informe. Por favor espere...
+
+
+ Cerrar
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Map/lang/PlgMap.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Map/lang/PlgMap.es-ES.xml
new file mode 100644
index 000000000..b9f985ea0
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Map/lang/PlgMap.es-ES.xml
@@ -0,0 +1,8 @@
+
+
+
+ Hizo clic en
+ Mostrar detalles
+ Error al actualizar los datos actuales
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Notification/lang/PlgNotification.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Notification/lang/PlgNotification.es-ES.xml
new file mode 100644
index 000000000..e9e240fe7
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Notification/lang/PlgNotification.es-ES.xml
@@ -0,0 +1,14 @@
+
+
+
+ Instrucciones
+ Reconocer
+ Registro
+ El plugin de notificación requiere registración.
+
+
+ Instrucciones del evento - Rapid SCADA
+ Evento no encontrado.
+ No hay información disponible.
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Registration/lang/PlgRegistration.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Registration/lang/PlgRegistration.es-ES.xml
new file mode 100644
index 000000000..05ffb7b72
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Registration/lang/PlgRegistration.es-ES.xml
@@ -0,0 +1,15 @@
+
+
+
+ Registración - Rapid SCADA
+ {0} Registración
+ Codigo de la computadora
+ Clave de registración
+ Compre una clave permanente
+ Obtener una clave de prueba
+ Estado de la clave
+ Guardar clave
+ Datos de registración perdidos.
+ Los tipos de datos de registración no coinciden.
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Scheme/lang/PlgScheme.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Scheme/lang/PlgScheme.es-ES.xml
new file mode 100644
index 000000000..e017cf59d
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Scheme/lang/PlgScheme.es-ES.xml
@@ -0,0 +1,17 @@
+
+
+
+ Esquema - Rapid SCADA
+ Ajustar a la pantalla
+ Ajustar al ancho
+ Alejar zoom
+ Acercar zoom
+
+
+ Rapid SCADA
+ Error al cargar esquema. Trate de recargarlo
+ Recargar
+ No se puedo enviar el comando
+ Error al actualizar los datos del esquema
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Store/lang/PlgStore.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Store/lang/PlgStore.es-ES.xml
new file mode 100644
index 000000000..2fb8542e2
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Store/lang/PlgStore.es-ES.xml
@@ -0,0 +1,43 @@
+
+
+
+ Descargar
+
+
+ Información del plugin - Rapid SCADA
+ General
+ Capturas de pantalla
+ Descripción
+ Precio
+ Haga clic aquí para comprar
+ http://rapidscada.org/download-all-files/purchase-module/
+ Autor
+ Páginas web
+ Página de inicio
+ Documentación
+ Descargar
+
+
+ No se puede recibir información del plugin
+ Gratis
+ Personal
+ Comercial
+
+
+ Descargar plugins - Rapid SCADA
+ Descargar plugins
+ Nombre
+ Descripción
+ Precio
+ Autor
+
+
+ No se pudo recibir la lista de plugins
+ No se encontraron plugins
+ Nuevo
+ Más información
+ Gratis
+ Personal
+ Comercial
+
+
diff --git a/LangPack/v5.7/Spanish/ScadaWeb/plugins/Table/lang/ScadaTable.es-ES.xml b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Table/lang/ScadaTable.es-ES.xml
new file mode 100644
index 000000000..abd163d32
--- /dev/null
+++ b/LangPack/v5.7/Spanish/ScadaWeb/plugins/Table/lang/ScadaTable.es-ES.xml
@@ -0,0 +1,149 @@
+
+
+
+ Error loading table view from file
+ Error saving table view to file
+
+
+ Item Information
+ Input Channel
+ Channel
+ Object
+ Device
+ Output Channel
+ Channel
+ Object
+ Device
+ Close
+
+
+ &File
+ New
+ Open...
+ Save
+ Save As...
+ Exit
+ &Help
+ About
+ New Table (Ctrl+N)
+ Open Table (Ctrl+O)
+ Save Table (Ctrl+S)
+ Refresh Configuration Database
+ Add Item
+ Add Empty Item
+ Move Item Up
+ Move Item Down
+ Delete Selected Items
+ Show Item Info
+ Add All Items
+ Input channels
+ Output channels
+ Press Enter or double-click a node to add it to the table. Right-click a device node to display the context menu.
+ Input Channel
+ Output Channel
+ Caption
+ Hidden
+ {0} - Table Editor
+ Table Title
+ Table Views (*.tbl)|*.tbl|All Files (*.*)|*.*
+ The table view has been modified. Save the changes?
+ The configuration database not found
+ Empty device
+ Error loading the configuration database
+ Error filling channel tree
+ Error loading form state
+ Error saving form state
+
+
+
+ Eventos
+ Todos los Eventos, {0}
+ {0}, {1}
+ Generado
+ Número
+ Fecha y Hora
+ Objetos
+ Dispositivo
+ Canal
+ Descripción
+ Ack
+
+
+ Datos por hora
+ {0}, {1}, {2}
+ Generado
+
+
+ Eventos
+
+
+ Comando - Rapid SCADA
+ Canal de Salida {0} no encontrado.
+ Contraseña incorrecta.
+ Privilegios insuficientes.
+ Valor de comando incorrecto.
+ Datos de comando incorrectos.
+ Canal de salida:
+ Objeto:
+ Dispositivo:
+ Contraseña
+ Valor
+ Comando
+ Datos
+ Cadena
+ Hexadecimal
+ El comando se pone en cola para su ejecución.
+ La ventana se cerrará después de
+ seg.
+ No se puede enviar el comando. El servidor no está disponible.
+ El comando es rechazado por el servidor.
+
+
+ Reconocimiento del evento - Rapid SCADA
+ Evento no encontrado.
+ No se puede enviar el acuse de recibo. El servidor no está disponible.
+ Acuse de recibo rechazado por el servidor.
+ Número:
+ Fecha y hora:
+ Objeto:
+ Dispositivo:
+ Canal:
+ Descripcion:
+ Admitido:
+ por {0}
+ Haga clic en OK para confirmar el evento.
+
+
+ Todos los eventos
+ Eventos por vista
+ Número
+ Fecha y hora
+ Objeto
+ Dispositivo
+ Canal
+ Descripcion
+ Ack
+ No hay eventos
+ Cargando...
+
+
+ Error al actualizar eventos
+
+
+ Día seleccionado:
+ Día previo:
+ Prev.
+ Ítem
+ Actual
+ Canal de entrada:
+ Canal de salida:
+ Objeto:
+ Dispositivo:
+ Cantidad:
+ Unidad:
+
+
+ Error al actualizar los datos actuales
+ Error al actualizar los datos por hora
+
+
diff --git a/LangPack/v5.7/Spanish/info.txt b/LangPack/v5.7/Spanish/info.txt
new file mode 100644
index 000000000..91bc9ce49
--- /dev/null
+++ b/LangPack/v5.7/Spanish/info.txt
@@ -0,0 +1,6 @@
+Language : Spanish
+Culture name: : es-ES
+Rapid SCADA version : 5.7.1
+Rapid SCADA applications : Websatation
+Author : Jesus Escoto Lopez, jeeslo@gmail.com
+Updated by : Horacio Venturino, hventuri@vera.com.uy
diff --git a/Report/RepBuilder/AssemblyInfo.cs b/Report/RepBuilder/AssemblyInfo.cs
index 7bf7d2bf5..6a16bebdf 100644
--- a/Report/RepBuilder/AssemblyInfo.cs
+++ b/Report/RepBuilder/AssemblyInfo.cs
@@ -1,5 +1,4 @@
using System.Reflection;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -10,7 +9,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Rapid SCADA")]
-[assembly: AssemblyCopyright("Copyright © 2005-2016")]
+[assembly: AssemblyCopyright("Copyright © 2005-2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -32,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("5.0.0.0")]
+[assembly: AssemblyFileVersion("5.0.0.0")]
diff --git a/Report/RepBuilder/RepBuilder.csproj b/Report/RepBuilder/RepBuilder.csproj
index ad56c49b2..1148b5095 100644
--- a/Report/RepBuilder/RepBuilder.csproj
+++ b/Report/RepBuilder/RepBuilder.csproj
@@ -93,6 +93,9 @@
Code
+
+
+
diff --git a/Report/RepBuilder/RepPeriodUnit.cs b/Report/RepBuilder/RepPeriodUnit.cs
new file mode 100644
index 000000000..482fc3339
--- /dev/null
+++ b/Report/RepBuilder/RepPeriodUnit.cs
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : Report Builder
+ * Summary : Specifies the units of report period
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+ #pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace Utils.Report
+{
+ ///
+ /// Specifies the units of report period.
+ /// Задает единицы измерения длительности периода отчёта.
+ ///
+ public enum RepPeriodUnit
+ {
+ Day,
+ Month
+ }
+}
diff --git a/Report/RepBuilder/RepStartDate.cs b/Report/RepBuilder/RepStartDate.cs
new file mode 100644
index 000000000..f6b7c3e1d
--- /dev/null
+++ b/Report/RepBuilder/RepStartDate.cs
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : Report Builder
+ * Summary : Specifies the possible report start dates
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+#pragma warning disable 1591 // Missing XML comment for publicly visible type or member
+
+namespace Utils.Report
+{
+ ///
+ /// Specifies the possible report start dates.
+ /// Задает возможные даты начала отчёта.
+ ///
+ public enum RepStartDate
+ {
+ Today,
+ Yesterday
+ }
+}
diff --git a/Report/RepBuilder/RepUtils.cs b/Report/RepBuilder/RepUtils.cs
new file mode 100644
index 000000000..0627ef492
--- /dev/null
+++ b/Report/RepBuilder/RepUtils.cs
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : Report Builder
+ * Summary : The class contains utility methods for reports
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+
+namespace Utils.Report
+{
+ ///
+ /// The class contains utility methods for reports.
+ /// Класс, содержащий вспомогательные методы для отчётов.
+ ///
+ public static class RepUtils
+ {
+ ///
+ /// Gets the actual report start date.
+ ///
+ public static DateTime GetStartDate(RepStartDate repStartDate, DateTime currentDate)
+ {
+ switch (repStartDate)
+ {
+ case RepStartDate.Yesterday:
+ return currentDate.AddDays(-1.0).Date;
+ default: // RepStartDate.Today:
+ return currentDate.Date;
+ }
+ }
+
+ ///
+ /// Normalizes the time range.
+ ///
+ ///
+ /// Makes the startDate a left point of the time range, and makes the period positive.
+ ///
+ public static void NormalizeTimeRange(ref DateTime startDate, ref int period,
+ RepPeriodUnit unit = RepPeriodUnit.Day)
+ {
+ startDate = startDate > DateTime.MinValue ? startDate.Date : DateTime.Today;
+
+ if (unit == RepPeriodUnit.Month)
+ {
+ if (period < 0)
+ {
+ startDate = startDate.AddMonths(period).Date;
+ period = -period;
+ }
+ }
+ else
+ {
+ // Examples:
+ // If the period is -1, 0 or 1, it means the single day, the startDate.
+ // If the period is 2, it means 2 days starting from the startDate.
+ // If the period is -2, it means 2 days ending with the startDate and including it.
+ if (period <= -2)
+ {
+ startDate = startDate.AddDays(period + 1).Date;
+ period = -period;
+ }
+ else if (period < 1)
+ {
+ period = 1;
+ }
+ }
+ }
+
+ ///
+ /// Normalizes the time range.
+ ///
+ public static void NormalizeTimeRange(ref DateTime startDate, ref DateTime endDate, ref int period,
+ RepPeriodUnit unit = RepPeriodUnit.Day)
+ {
+ bool periodInMonths = unit == RepPeriodUnit.Month;
+
+ if (startDate > DateTime.MinValue && endDate > DateTime.MinValue)
+ {
+ if (endDate < startDate)
+ endDate = startDate;
+ period = periodInMonths ?
+ ((endDate.Year - startDate.Year) * 12) + endDate.Month - startDate.Month :
+ (int)(endDate - startDate).TotalDays + 1;
+ }
+ else if (startDate > DateTime.MinValue)
+ {
+ NormalizeTimeRange(ref startDate, ref period, unit);
+ endDate = periodInMonths ?
+ startDate.AddMonths(period) :
+ startDate.AddDays(period - 1);
+ }
+ else if (endDate > DateTime.MinValue)
+ {
+ period = Math.Abs(period);
+ NormalizeTimeRange(ref endDate, ref period, unit);
+ startDate = periodInMonths ?
+ endDate.AddMonths(-period) :
+ endDate.AddDays(-period + 1);
+ }
+ else
+ {
+ NormalizeTimeRange(ref startDate, ref period, unit);
+ endDate = periodInMonths ?
+ startDate.AddMonths(period) :
+ startDate.AddDays(period - 1);
+ }
+ }
+ }
+}
diff --git a/ScadaAdmin/Res/gen2/menu/copy_path.png b/ScadaAdmin/Res/gen2/menu/copy_path.png
new file mode 100644
index 000000000..8ff40a0fc
Binary files /dev/null and b/ScadaAdmin/Res/gen2/menu/copy_path.png differ
diff --git a/ScadaAdmin/Res/gen2/menu/filter.png b/ScadaAdmin/Res/gen2/menu/filter.png
new file mode 100644
index 000000000..940f8a384
Binary files /dev/null and b/ScadaAdmin/Res/gen2/menu/filter.png differ
diff --git a/ScadaAdmin/Res/gen2/menu/filter_set.png b/ScadaAdmin/Res/gen2/menu/filter_set.png
new file mode 100644
index 000000000..09d3fc8a2
Binary files /dev/null and b/ScadaAdmin/Res/gen2/menu/filter_set.png differ
diff --git a/ScadaAdmin/Res/gen2/menu/remove_from_list.png b/ScadaAdmin/Res/gen2/menu/remove_from_list.png
new file mode 100644
index 000000000..d09c9be54
Binary files /dev/null and b/ScadaAdmin/Res/gen2/menu/remove_from_list.png differ
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/AppData.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/AppData.cs
index 7317f15cd..03171eb9f 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/AppData.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/AppData.cs
@@ -23,6 +23,7 @@
* Modified : 2018
*/
+using Scada.Admin.Config;
using Scada.UI;
using System;
using System.IO;
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/AppPhrases.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/AppPhrases.cs
index 62f9fc432..d26097681 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/AppPhrases.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/AppPhrases.cs
@@ -125,6 +125,9 @@ internal static class AppPhrases
public static string DataNotExist { get; private set; }
public static string DataChangeError { get; private set; }
+ // Scada.Admin.App.Forms.Tables.FrmFilter
+ public static string IncorrectTableFilter { get; private set; }
+
// Scada.Admin.App.Forms.Tables.FrmFind
public static string ValueNotFound { get; private set; }
public static string SearchComplete { get; private set; }
@@ -317,6 +320,9 @@ public static void Init()
DataNotExist = dict.GetPhrase("DataNotExist");
DataChangeError = dict.GetPhrase("DataChangeError");
+ dict = Localization.GetDictionary("Scada.Admin.App.Forms.Tables.FrmFilter");
+ IncorrectTableFilter = dict.GetPhrase("IncorrectTableFilter");
+
dict = Localization.GetDictionary("Scada.Admin.App.Forms.Tables.FrmFind");
ValueNotFound = dict.GetPhrase("ValueNotFound");
SearchComplete = dict.GetPhrase("SearchComplete");
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/ColumnInfo.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/ColumnInfo.cs
new file mode 100644
index 000000000..3700cac04
--- /dev/null
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/ColumnInfo.cs
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : Administrator
+ * Summary : Represents information associated with a column
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+using System.Data;
+using System.Windows.Forms;
+
+namespace Scada.Admin.App.Code
+{
+ ///
+ /// Represents information associated with a column.
+ /// Представляет информацию, связанную со столбцом.
+ ///
+ internal class ColumnInfo
+ {
+ private DataTable dataSource1;
+ private DataTable dataSource2;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ColumnInfo(DataGridViewColumn column)
+ {
+ Column = column ?? throw new ArgumentNullException("column");
+ dataSource1 = null;
+ dataSource2 = null;
+ }
+
+
+ ///
+ /// Gets the column.
+ ///
+ public DataGridViewColumn Column { get; private set; }
+
+ ///
+ /// Gets the column header.
+ ///
+ public string Header
+ {
+ get
+ {
+ return Column.HeaderText ?? "";
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the column contains text.
+ ///
+ public bool IsText
+ {
+ get
+ {
+ return Column is DataGridViewTextBoxColumn;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the text column contains numbers.
+ ///
+ public bool IsNumber
+ {
+ get
+ {
+ return IsText && (Column.ValueType == typeof(int) || Column.ValueType == typeof(double));
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the column contains combo boxes.
+ ///
+ public bool IsComboBox
+ {
+ get
+ {
+ return Column is DataGridViewComboBoxColumn;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the column contains check boxes.
+ ///
+ public bool IsCheckBox
+ {
+ get
+ {
+ return Column is DataGridViewCheckBoxColumn;
+ }
+ }
+
+ ///
+ /// Gets the column from which to retrieve strings for display in the combo box.
+ ///
+ public string DisplayMember
+ {
+ get
+ {
+ return Column is DataGridViewComboBoxColumn comboBoxColumn ? comboBoxColumn.DisplayMember : "";
+ }
+ }
+
+ ///
+ /// Gets the column from which to get values that correspond to the selections in the combo box.
+ ///
+ public string ValueMember
+ {
+ get
+ {
+ return Column is DataGridViewComboBoxColumn comboBoxColumn ? comboBoxColumn.ValueMember : "";
+ }
+ }
+
+ ///
+ /// Gets the data source #1 contains columns values.
+ ///
+ public DataTable DataSource1
+ {
+ get
+ {
+ if (dataSource1 == null)
+ {
+ dataSource1 = Column is DataGridViewComboBoxColumn comboBoxColumn ?
+ CopyTable(comboBoxColumn.DataSource as DataTable) : null;
+ }
+
+ return dataSource1;
+ }
+ }
+
+ ///
+ /// Gets the data source #2 contains columns values.
+ ///
+ public DataTable DataSource2
+ {
+ get
+ {
+ if (dataSource2 == null)
+ {
+ dataSource2 = Column is DataGridViewComboBoxColumn comboBoxColumn ?
+ CopyTable(comboBoxColumn.DataSource as DataTable) : null;
+ }
+
+ return dataSource2;
+ }
+ }
+
+
+ ///
+ /// Makes a table copy having disabled constraints.
+ ///
+ private DataTable CopyTable(DataTable dataTable)
+ {
+ if (dataTable == null)
+ {
+ return null;
+ }
+ else
+ {
+ DataTable tableCopy = new DataTable(dataTable.TableName);
+ tableCopy.BeginLoadData();
+ tableCopy.Merge(dataTable, false, MissingSchemaAction.Add);
+ tableCopy.DefaultView.Sort = dataTable.DefaultView.Sort;
+ return tableCopy;
+ }
+ }
+
+ ///
+ /// Returns a string that represents the current object.
+ ///
+ public override string ToString()
+ {
+ return Header;
+ }
+ }
+}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/MainFormState.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/MainFormState.cs
index b4ac73d3d..ec3108e27 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/MainFormState.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Code/MainFormState.cs
@@ -24,6 +24,7 @@
*/
using Scada.Admin.App.Forms;
+using Scada.UI;
using System;
using System.Drawing;
using System.Windows.Forms;
@@ -141,15 +142,16 @@ public void Apply(FrmMain form)
if (form == null)
throw new ArgumentNullException("form");
- if (!IsEmpty)
+ if (!IsEmpty && ScadaUiUtils.AreaIsVisible(Left, Top, Width, Height))
{
- form.Left = Left;
- form.Top = Top;
- form.Width = Width;
- form.Height = Height;
+ form.SetBounds(Left, Top, Width, Height);
form.WindowState = Maximized ? FormWindowState.Maximized : FormWindowState.Normal;
form.ExplorerWidth = ExplorerWidth;
}
+ else
+ {
+ form.WindowState = FormWindowState.Maximized;
+ }
}
///
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Deployment/CtrlProfileSelector.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Deployment/CtrlProfileSelector.cs
index 4619a563e..179ead3d5 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Deployment/CtrlProfileSelector.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Deployment/CtrlProfileSelector.cs
@@ -28,6 +28,7 @@
using Scada.Admin.Deployment;
using Scada.Admin.Project;
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
@@ -39,6 +40,11 @@ namespace Scada.Admin.App.Controls.Deployment
///
public partial class CtrlProfileSelector : UserControl
{
+ ///
+ /// The default profile name format.
+ ///
+ private const string ProfileNameFormat = "{0} Profile";
+
private AppData appData; // the common data of the application
private DeploymentSettings deploymentSettings; // the deployment settings to select or edit
private Instance instance; // the instance which profile is selected
@@ -77,13 +83,17 @@ private void FillProfileList()
cbProfile.Items.Add(AppPhrases.ProfileNotSet);
int selectedIndex = 0;
+ int instanceID = instance.ID;
string selectedName = instance.DeploymentProfile;
foreach (DeploymentProfile profile in deploymentSettings.Profiles.Values)
{
- int index = cbProfile.Items.Add(profile);
- if (profile.Name == selectedName)
- selectedIndex = index;
+ if (profile.InstanceID <= 0 || profile.InstanceID == instanceID)
+ {
+ int index = cbProfile.Items.Add(profile);
+ if (profile.Name == selectedName)
+ selectedIndex = index;
+ }
}
cbProfile.SelectedIndex = selectedIndex;
@@ -103,12 +113,19 @@ private void AddProfileToLists(DeploymentProfile profile)
deploymentSettings.Profiles.Add(profile.Name, profile);
// add to the combo box
- int index = deploymentSettings.Profiles.IndexOfKey(profile.Name) + 1;
- if (index > 0)
+ int indexToInsert = cbProfile.Items.Count;
+
+ for (int i = 1, cnt = cbProfile.Items.Count; i < cnt; i++)
{
- cbProfile.Items.Insert(index, profile);
- cbProfile.SelectedIndex = index;
+ if (string.Compare(cbProfile.Items[i].ToString(), profile.Name) > 0)
+ {
+ indexToInsert = i;
+ break;
+ }
}
+
+ cbProfile.Items.Insert(indexToInsert, profile);
+ cbProfile.SelectedIndex = indexToInsert;
}
///
@@ -159,12 +176,19 @@ private void cbProfile_SelectedIndexChanged(object sender, EventArgs e)
private void btnCreateProfile_Click(object sender, EventArgs e)
{
// create a new profile
- DeploymentProfile profile = new DeploymentProfile();
+ HashSet existingNames = deploymentSettings.GetExistingProfileNames();
+ string defaultProfileName = string.Format(ProfileNameFormat, instance.Name);
- FrmProfileEdit frmProfileEdit = new FrmProfileEdit()
+ DeploymentProfile profile = new DeploymentProfile
+ {
+ InstanceID = instance.ID,
+ Name = existingNames.Contains(defaultProfileName) ? "" : defaultProfileName
+ };
+
+ FrmProfileEdit frmProfileEdit = new FrmProfileEdit
{
Profile = profile,
- ExistingProfileNames = deploymentSettings.GetExistingProfileNames()
+ ExistingProfileNames = existingNames
};
if (frmProfileEdit.ShowDialog() == DialogResult.OK)
@@ -205,6 +229,10 @@ private void btnEditProfile_Click(object sender, EventArgs e)
}
}
+ // fix the instance reference
+ if (profile.InstanceID <= 0)
+ profile.InstanceID = instance.ID;
+
// save the deployment settings
SaveDeploymentSettings();
}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Tools/CtrlCnlCreate1.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Tools/CtrlCnlCreate1.cs
index 365540c8c..a241056a4 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Tools/CtrlCnlCreate1.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Tools/CtrlCnlCreate1.cs
@@ -239,7 +239,8 @@ private void cbDevice_SelectedIndexChanged(object sender, EventArgs e)
{
if (deviceItem.KPView == null)
{
- CommDirs commDirs = new CommDirs(appData.AppSettings.CommDir, deviceItem.Instance);
+ CommDirs commDirs = new CommDirs(
+ appData.AppSettings.PathOptions.CommDir, deviceItem.Instance);
deviceItem.KPView = KPFactory.GetKPView(
Path.Combine(commDirs.KPDir, deviceItem.KPSettings.Dll), kp.KPNum);
deviceItem.KPView.KPProps = new KPView.KPProperties(
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Tools/CtrlCnlCreate3.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Tools/CtrlCnlCreate3.cs
index 03f4ab5d5..bd23280e5 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Tools/CtrlCnlCreate3.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Controls/Tools/CtrlCnlCreate3.cs
@@ -24,6 +24,7 @@
*/
using Scada.Admin.App.Code;
+using Scada.Admin.Config;
using Scada.Admin.Project;
using Scada.Data.Tables;
using Scada.UI;
@@ -109,9 +110,10 @@ public int StartOutCnl
///
private bool CalcStartCnlNum(IBaseTable cnlTable, int cnlCnt, out int startCnlNum)
{
- int cnlMult = appSettings.CnlMult;
- int cnlGap = appSettings.CnlGap;
- startCnlNum = cnlMult + appSettings.CnlShift;
+ ChannelOptions channelOptions = appSettings.ChannelOptions;
+ int cnlMult = channelOptions.CnlMult;
+ int cnlGap = channelOptions.CnlGap;
+ startCnlNum = cnlMult + channelOptions.CnlShift;
int prevCnlNum = 0;
foreach (int cnlNum in cnlTable.EnumerateKeys())
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmFileNew.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmFileNew.cs
index 10310e7ed..2977bcb75 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmFileNew.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmFileNew.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
*
* Author : Mikhail Shiryaev
* Created : 2018
- * Modified : 2018
+ * Modified : 2019
*/
using Scada.Admin.App.Code;
@@ -149,8 +149,6 @@ private void lbFileType_SelectedIndexChanged(object sender, EventArgs e)
private void btnOK_Click(object sender, EventArgs e)
{
- FixFileExtenstion();
-
if (ValidateFields())
DialogResult = DialogResult.OK;
}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.Designer.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.Designer.cs
index 428b2dc81..677ef30dd 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.Designer.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.Designer.cs
@@ -358,7 +358,7 @@ private void InitializeComponent()
this.miToolsOptions,
this.miToolsCulture});
this.miTools.Name = "miTools";
- this.miTools.Size = new System.Drawing.Size(47, 20);
+ this.miTools.Size = new System.Drawing.Size(46, 20);
this.miTools.Text = "&Tools";
//
// miToolsAddLine
@@ -479,7 +479,7 @@ private void InitializeComponent()
//
this.miHelpDoc.Image = ((System.Drawing.Image)(resources.GetObject("miHelpDoc.Image")));
this.miHelpDoc.Name = "miHelpDoc";
- this.miHelpDoc.Size = new System.Drawing.Size(169, 22);
+ this.miHelpDoc.Size = new System.Drawing.Size(168, 22);
this.miHelpDoc.Text = "Documentation";
this.miHelpDoc.Click += new System.EventHandler(this.miHelpDoc_Click);
//
@@ -487,20 +487,20 @@ private void InitializeComponent()
//
this.miHelpSupport.Image = ((System.Drawing.Image)(resources.GetObject("miHelpSupport.Image")));
this.miHelpSupport.Name = "miHelpSupport";
- this.miHelpSupport.Size = new System.Drawing.Size(169, 22);
+ this.miHelpSupport.Size = new System.Drawing.Size(168, 22);
this.miHelpSupport.Text = "Technical Support";
this.miHelpSupport.Click += new System.EventHandler(this.miHelpSupport_Click);
//
// miHelpSep1
//
this.miHelpSep1.Name = "miHelpSep1";
- this.miHelpSep1.Size = new System.Drawing.Size(166, 6);
+ this.miHelpSep1.Size = new System.Drawing.Size(165, 6);
//
// miHelpAbout
//
this.miHelpAbout.Image = ((System.Drawing.Image)(resources.GetObject("miHelpAbout.Image")));
this.miHelpAbout.Name = "miHelpAbout";
- this.miHelpAbout.Size = new System.Drawing.Size(169, 22);
+ this.miHelpAbout.Size = new System.Drawing.Size(168, 22);
this.miHelpAbout.Text = "About";
this.miHelpAbout.Click += new System.EventHandler(this.miHelpAbout_Click);
//
@@ -866,14 +866,14 @@ private void InitializeComponent()
this.miInstanceRename,
this.miInstanceProperties});
this.cmsInstance.Name = "cmsCommLine";
- this.cmsInstance.Size = new System.Drawing.Size(219, 280);
+ this.cmsInstance.Size = new System.Drawing.Size(220, 280);
this.cmsInstance.Opening += new System.ComponentModel.CancelEventHandler(this.cmsInstance_Opening);
//
// miInstanceAdd
//
this.miInstanceAdd.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceAdd.Image")));
this.miInstanceAdd.Name = "miInstanceAdd";
- this.miInstanceAdd.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceAdd.Size = new System.Drawing.Size(219, 22);
this.miInstanceAdd.Text = "Add Instance...";
this.miInstanceAdd.Click += new System.EventHandler(this.miInstanceAdd_Click);
//
@@ -881,7 +881,7 @@ private void InitializeComponent()
//
this.miInstanceMoveUp.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceMoveUp.Image")));
this.miInstanceMoveUp.Name = "miInstanceMoveUp";
- this.miInstanceMoveUp.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceMoveUp.Size = new System.Drawing.Size(219, 22);
this.miInstanceMoveUp.Text = "Move Instance Up";
this.miInstanceMoveUp.Click += new System.EventHandler(this.miInstanceMoveUp_Click);
//
@@ -889,7 +889,7 @@ private void InitializeComponent()
//
this.miInstanceMoveDown.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceMoveDown.Image")));
this.miInstanceMoveDown.Name = "miInstanceMoveDown";
- this.miInstanceMoveDown.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceMoveDown.Size = new System.Drawing.Size(219, 22);
this.miInstanceMoveDown.Text = "Move Instance Down";
this.miInstanceMoveDown.Click += new System.EventHandler(this.miInstanceMoveDown_Click);
//
@@ -897,20 +897,20 @@ private void InitializeComponent()
//
this.miInstanceDelete.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceDelete.Image")));
this.miInstanceDelete.Name = "miInstanceDelete";
- this.miInstanceDelete.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceDelete.Size = new System.Drawing.Size(219, 22);
this.miInstanceDelete.Text = "Delete Instance";
this.miInstanceDelete.Click += new System.EventHandler(this.miInstanceDelete_Click);
//
// miInstanceSep1
//
this.miInstanceSep1.Name = "miInstanceSep1";
- this.miInstanceSep1.Size = new System.Drawing.Size(215, 6);
+ this.miInstanceSep1.Size = new System.Drawing.Size(216, 6);
//
// miInstanceProfile
//
this.miInstanceProfile.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceProfile.Image")));
this.miInstanceProfile.Name = "miInstanceProfile";
- this.miInstanceProfile.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceProfile.Size = new System.Drawing.Size(219, 22);
this.miInstanceProfile.Text = "Deployment Profile...";
this.miInstanceProfile.Click += new System.EventHandler(this.miDeployInstanceProfile_Click);
//
@@ -918,7 +918,7 @@ private void InitializeComponent()
//
this.miInstanceDownloadConfig.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceDownloadConfig.Image")));
this.miInstanceDownloadConfig.Name = "miInstanceDownloadConfig";
- this.miInstanceDownloadConfig.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceDownloadConfig.Size = new System.Drawing.Size(219, 22);
this.miInstanceDownloadConfig.Text = "Download Configuration...";
this.miInstanceDownloadConfig.Click += new System.EventHandler(this.miDeployDownloadConfig_Click);
//
@@ -926,7 +926,7 @@ private void InitializeComponent()
//
this.miInstanceUploadConfig.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceUploadConfig.Image")));
this.miInstanceUploadConfig.Name = "miInstanceUploadConfig";
- this.miInstanceUploadConfig.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceUploadConfig.Size = new System.Drawing.Size(219, 22);
this.miInstanceUploadConfig.Text = "Upload Configuration...";
this.miInstanceUploadConfig.Click += new System.EventHandler(this.miDeployUploadConfig_Click);
//
@@ -934,20 +934,20 @@ private void InitializeComponent()
//
this.miInstanceStatus.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceStatus.Image")));
this.miInstanceStatus.Name = "miInstanceStatus";
- this.miInstanceStatus.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceStatus.Size = new System.Drawing.Size(219, 22);
this.miInstanceStatus.Text = "Instance Status...";
this.miInstanceStatus.Click += new System.EventHandler(this.miDeployInstanceStatus_Click);
//
// miInstanceSep2
//
this.miInstanceSep2.Name = "miInstanceSep2";
- this.miInstanceSep2.Size = new System.Drawing.Size(215, 6);
+ this.miInstanceSep2.Size = new System.Drawing.Size(216, 6);
//
// miInstanceOpenInExplorer
//
this.miInstanceOpenInExplorer.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceOpenInExplorer.Image")));
this.miInstanceOpenInExplorer.Name = "miInstanceOpenInExplorer";
- this.miInstanceOpenInExplorer.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceOpenInExplorer.Size = new System.Drawing.Size(219, 22);
this.miInstanceOpenInExplorer.Text = "Open Folder in File Explorer";
this.miInstanceOpenInExplorer.Click += new System.EventHandler(this.miDirectoryOpenInExplorer_Click);
//
@@ -955,7 +955,7 @@ private void InitializeComponent()
//
this.miInstanceOpenInBrowser.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceOpenInBrowser.Image")));
this.miInstanceOpenInBrowser.Name = "miInstanceOpenInBrowser";
- this.miInstanceOpenInBrowser.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceOpenInBrowser.Size = new System.Drawing.Size(219, 22);
this.miInstanceOpenInBrowser.Text = "Open in Web Browser";
this.miInstanceOpenInBrowser.Click += new System.EventHandler(this.miInstanceOpenInBrowser_Click);
//
@@ -963,7 +963,7 @@ private void InitializeComponent()
//
this.miInstanceRename.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceRename.Image")));
this.miInstanceRename.Name = "miInstanceRename";
- this.miInstanceRename.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceRename.Size = new System.Drawing.Size(219, 22);
this.miInstanceRename.Text = "Rename Instance";
this.miInstanceRename.Click += new System.EventHandler(this.miInstanceRename_Click);
//
@@ -971,7 +971,7 @@ private void InitializeComponent()
//
this.miInstanceProperties.Image = ((System.Drawing.Image)(resources.GetObject("miInstanceProperties.Image")));
this.miInstanceProperties.Name = "miInstanceProperties";
- this.miInstanceProperties.Size = new System.Drawing.Size(218, 22);
+ this.miInstanceProperties.Size = new System.Drawing.Size(219, 22);
this.miInstanceProperties.Text = "Properties";
this.miInstanceProperties.Click += new System.EventHandler(this.miInstanceProperties_Click);
//
@@ -982,13 +982,13 @@ private void InitializeComponent()
this.miProjectRename,
this.miProjectProperties});
this.cmsProject.Name = "cmsCommLine";
- this.cmsProject.Size = new System.Drawing.Size(219, 70);
+ this.cmsProject.Size = new System.Drawing.Size(220, 70);
//
// miProjectOpenInExplorer
//
this.miProjectOpenInExplorer.Image = ((System.Drawing.Image)(resources.GetObject("miProjectOpenInExplorer.Image")));
this.miProjectOpenInExplorer.Name = "miProjectOpenInExplorer";
- this.miProjectOpenInExplorer.Size = new System.Drawing.Size(218, 22);
+ this.miProjectOpenInExplorer.Size = new System.Drawing.Size(219, 22);
this.miProjectOpenInExplorer.Text = "Open Folder in File Explorer";
this.miProjectOpenInExplorer.Click += new System.EventHandler(this.miDirectoryOpenInExplorer_Click);
//
@@ -996,7 +996,7 @@ private void InitializeComponent()
//
this.miProjectRename.Image = ((System.Drawing.Image)(resources.GetObject("miProjectRename.Image")));
this.miProjectRename.Name = "miProjectRename";
- this.miProjectRename.Size = new System.Drawing.Size(218, 22);
+ this.miProjectRename.Size = new System.Drawing.Size(219, 22);
this.miProjectRename.Text = "Rename Project";
this.miProjectRename.Click += new System.EventHandler(this.miProjectRename_Click);
//
@@ -1004,7 +1004,7 @@ private void InitializeComponent()
//
this.miProjectProperties.Image = ((System.Drawing.Image)(resources.GetObject("miProjectProperties.Image")));
this.miProjectProperties.Name = "miProjectProperties";
- this.miProjectProperties.Size = new System.Drawing.Size(218, 22);
+ this.miProjectProperties.Size = new System.Drawing.Size(219, 22);
this.miProjectProperties.Text = "Properties";
this.miProjectProperties.Click += new System.EventHandler(this.miProjectProperties_Click);
//
@@ -1020,14 +1020,14 @@ private void InitializeComponent()
this.miDirectoryOpenInExplorer,
this.miDirectoryRefresh});
this.cmsDirectory.Name = "cmsDirectory";
- this.cmsDirectory.Size = new System.Drawing.Size(219, 148);
+ this.cmsDirectory.Size = new System.Drawing.Size(220, 148);
this.cmsDirectory.Opening += new System.ComponentModel.CancelEventHandler(this.cmsDirectory_Opening);
//
// miDirectoryNewFile
//
this.miDirectoryNewFile.Image = ((System.Drawing.Image)(resources.GetObject("miDirectoryNewFile.Image")));
this.miDirectoryNewFile.Name = "miDirectoryNewFile";
- this.miDirectoryNewFile.Size = new System.Drawing.Size(218, 22);
+ this.miDirectoryNewFile.Size = new System.Drawing.Size(219, 22);
this.miDirectoryNewFile.Text = "New File...";
this.miDirectoryNewFile.Click += new System.EventHandler(this.miDirectoryNewFile_Click);
//
@@ -1035,20 +1035,20 @@ private void InitializeComponent()
//
this.miDirectoryNewFolder.Image = ((System.Drawing.Image)(resources.GetObject("miDirectoryNewFolder.Image")));
this.miDirectoryNewFolder.Name = "miDirectoryNewFolder";
- this.miDirectoryNewFolder.Size = new System.Drawing.Size(218, 22);
+ this.miDirectoryNewFolder.Size = new System.Drawing.Size(219, 22);
this.miDirectoryNewFolder.Text = "New Folder...";
this.miDirectoryNewFolder.Click += new System.EventHandler(this.miDirectoryNewFolder_Click);
//
// miDirectorySep1
//
this.miDirectorySep1.Name = "miDirectorySep1";
- this.miDirectorySep1.Size = new System.Drawing.Size(215, 6);
+ this.miDirectorySep1.Size = new System.Drawing.Size(216, 6);
//
// miDirectoryDelete
//
this.miDirectoryDelete.Image = ((System.Drawing.Image)(resources.GetObject("miDirectoryDelete.Image")));
this.miDirectoryDelete.Name = "miDirectoryDelete";
- this.miDirectoryDelete.Size = new System.Drawing.Size(218, 22);
+ this.miDirectoryDelete.Size = new System.Drawing.Size(219, 22);
this.miDirectoryDelete.Text = "Delete";
this.miDirectoryDelete.Click += new System.EventHandler(this.miDirectoryDelete_Click);
//
@@ -1056,20 +1056,20 @@ private void InitializeComponent()
//
this.miDirectoryRename.Image = ((System.Drawing.Image)(resources.GetObject("miDirectoryRename.Image")));
this.miDirectoryRename.Name = "miDirectoryRename";
- this.miDirectoryRename.Size = new System.Drawing.Size(218, 22);
+ this.miDirectoryRename.Size = new System.Drawing.Size(219, 22);
this.miDirectoryRename.Text = "Rename";
this.miDirectoryRename.Click += new System.EventHandler(this.miDirectoryRename_Click);
//
// miDirectorySep2
//
this.miDirectorySep2.Name = "miDirectorySep2";
- this.miDirectorySep2.Size = new System.Drawing.Size(215, 6);
+ this.miDirectorySep2.Size = new System.Drawing.Size(216, 6);
//
// miDirectoryOpenInExplorer
//
this.miDirectoryOpenInExplorer.Image = ((System.Drawing.Image)(resources.GetObject("miDirectoryOpenInExplorer.Image")));
this.miDirectoryOpenInExplorer.Name = "miDirectoryOpenInExplorer";
- this.miDirectoryOpenInExplorer.Size = new System.Drawing.Size(218, 22);
+ this.miDirectoryOpenInExplorer.Size = new System.Drawing.Size(219, 22);
this.miDirectoryOpenInExplorer.Text = "Open Folder in File Explorer";
this.miDirectoryOpenInExplorer.Click += new System.EventHandler(this.miDirectoryOpenInExplorer_Click);
//
@@ -1077,7 +1077,7 @@ private void InitializeComponent()
//
this.miDirectoryRefresh.Image = ((System.Drawing.Image)(resources.GetObject("miDirectoryRefresh.Image")));
this.miDirectoryRefresh.Name = "miDirectoryRefresh";
- this.miDirectoryRefresh.Size = new System.Drawing.Size(218, 22);
+ this.miDirectoryRefresh.Size = new System.Drawing.Size(219, 22);
this.miDirectoryRefresh.Text = "Refresh";
this.miDirectoryRefresh.Click += new System.EventHandler(this.miDirectoryRefresh_Click);
//
@@ -1090,14 +1090,14 @@ private void InitializeComponent()
this.miFileItemDelete,
this.miFileItemRename});
this.cmsFileItem.Name = "cmsFileItem";
- this.cmsFileItem.Size = new System.Drawing.Size(232, 98);
+ this.cmsFileItem.Size = new System.Drawing.Size(233, 98);
this.cmsFileItem.Opening += new System.ComponentModel.CancelEventHandler(this.cmsFileItem_Opening);
//
// miFileItemOpen
//
this.miFileItemOpen.Image = ((System.Drawing.Image)(resources.GetObject("miFileItemOpen.Image")));
this.miFileItemOpen.Name = "miFileItemOpen";
- this.miFileItemOpen.Size = new System.Drawing.Size(231, 22);
+ this.miFileItemOpen.Size = new System.Drawing.Size(232, 22);
this.miFileItemOpen.Text = "Open";
this.miFileItemOpen.Click += new System.EventHandler(this.miFileItemOpen_Click);
//
@@ -1105,20 +1105,20 @@ private void InitializeComponent()
//
this.miFileItemOpenLocation.Image = ((System.Drawing.Image)(resources.GetObject("miFileItemOpenLocation.Image")));
this.miFileItemOpenLocation.Name = "miFileItemOpenLocation";
- this.miFileItemOpenLocation.Size = new System.Drawing.Size(231, 22);
+ this.miFileItemOpenLocation.Size = new System.Drawing.Size(232, 22);
this.miFileItemOpenLocation.Text = "Open Location in File Explorer";
this.miFileItemOpenLocation.Click += new System.EventHandler(this.miFileItemOpenLocation_Click);
//
// miFileItemSep1
//
this.miFileItemSep1.Name = "miFileItemSep1";
- this.miFileItemSep1.Size = new System.Drawing.Size(228, 6);
+ this.miFileItemSep1.Size = new System.Drawing.Size(229, 6);
//
// miFileItemDelete
//
this.miFileItemDelete.Image = ((System.Drawing.Image)(resources.GetObject("miFileItemDelete.Image")));
this.miFileItemDelete.Name = "miFileItemDelete";
- this.miFileItemDelete.Size = new System.Drawing.Size(231, 22);
+ this.miFileItemDelete.Size = new System.Drawing.Size(232, 22);
this.miFileItemDelete.Text = "Delete";
this.miFileItemDelete.Click += new System.EventHandler(this.miFileItemDelete_Click);
//
@@ -1126,7 +1126,7 @@ private void InitializeComponent()
//
this.miFileItemRename.Image = ((System.Drawing.Image)(resources.GetObject("miFileItemRename.Image")));
this.miFileItemRename.Name = "miFileItemRename";
- this.miFileItemRename.Size = new System.Drawing.Size(231, 22);
+ this.miFileItemRename.Size = new System.Drawing.Size(232, 22);
this.miFileItemRename.Text = "Rename";
this.miFileItemRename.Click += new System.EventHandler(this.miFileItemRename_Click);
//
@@ -1135,13 +1135,13 @@ private void InitializeComponent()
this.cmsServer.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.miServerOpenInExplorer});
this.cmsServer.Name = "cmsServer";
- this.cmsServer.Size = new System.Drawing.Size(219, 26);
+ this.cmsServer.Size = new System.Drawing.Size(220, 26);
//
// miServerOpenInExplorer
//
this.miServerOpenInExplorer.Image = ((System.Drawing.Image)(resources.GetObject("miServerOpenInExplorer.Image")));
this.miServerOpenInExplorer.Name = "miServerOpenInExplorer";
- this.miServerOpenInExplorer.Size = new System.Drawing.Size(218, 22);
+ this.miServerOpenInExplorer.Size = new System.Drawing.Size(219, 22);
this.miServerOpenInExplorer.Text = "Open Folder in File Explorer";
this.miServerOpenInExplorer.Click += new System.EventHandler(this.miDirectoryOpenInExplorer_Click);
//
@@ -1150,13 +1150,13 @@ private void InitializeComponent()
this.cmsComm.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.miCommOpenInExplorer});
this.cmsComm.Name = "cmsServer";
- this.cmsComm.Size = new System.Drawing.Size(219, 26);
+ this.cmsComm.Size = new System.Drawing.Size(220, 26);
//
// miCommOpenInExplorer
//
this.miCommOpenInExplorer.Image = ((System.Drawing.Image)(resources.GetObject("miCommOpenInExplorer.Image")));
this.miCommOpenInExplorer.Name = "miCommOpenInExplorer";
- this.miCommOpenInExplorer.Size = new System.Drawing.Size(218, 22);
+ this.miCommOpenInExplorer.Size = new System.Drawing.Size(219, 22);
this.miCommOpenInExplorer.Text = "Open Folder in File Explorer";
this.miCommOpenInExplorer.Click += new System.EventHandler(this.miDirectoryOpenInExplorer_Click);
//
@@ -1228,7 +1228,6 @@ private void InitializeComponent()
this.Name = "FrmMain";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Administrator";
- this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmMain_FormClosing);
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FrmMain_FormClosed);
this.Load += new System.EventHandler(this.FrmMain_Load);
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.cs
index df313f9e0..22d80c334 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.cs
@@ -28,6 +28,7 @@
using Scada.Admin.App.Forms.Tables;
using Scada.Admin.App.Forms.Tools;
using Scada.Admin.App.Properties;
+using Scada.Admin.Config;
using Scada.Admin.Deployment;
using Scada.Admin.Project;
using Scada.Agent.Connector;
@@ -350,21 +351,22 @@ private void ExecOpenFileAction(TreeNode treeNode)
if (tag.ExistingForm == null)
{
KnownFileType fileType = fileItem.FileType;
+ PathOptions pathOptions = appData.AppSettings.PathOptions;
- if (fileType == KnownFileType.SchemeView && File.Exists(appData.AppSettings.SchemeEditorPath))
+ if (fileType == KnownFileType.SchemeView && File.Exists(pathOptions.SchemeEditorPath))
{
// run Scheme Editor
- Process.Start(appData.AppSettings.SchemeEditorPath, string.Format("\"{0}\"", fileItem.Path));
+ Process.Start(pathOptions.SchemeEditorPath, string.Format("\"{0}\"", fileItem.Path));
}
- else if (fileType == KnownFileType.TableView && File.Exists(appData.AppSettings.TableEditorPath))
+ else if (fileType == KnownFileType.TableView && File.Exists(pathOptions.TableEditorPath))
{
// run Table Editor
- Process.Start(appData.AppSettings.TableEditorPath, string.Format("\"{0}\"", fileItem.Path));
+ Process.Start(pathOptions.TableEditorPath, string.Format("\"{0}\"", fileItem.Path));
}
- else if (fileType != KnownFileType.None && File.Exists(appData.AppSettings.TextEditorPath))
+ else if (fileType != KnownFileType.None && File.Exists(pathOptions.TextEditorPath))
{
// run text editor
- Process.Start(appData.AppSettings.TextEditorPath, string.Format("\"{0}\"", fileItem.Path));
+ Process.Start(pathOptions.TextEditorPath, string.Format("\"{0}\"", fileItem.Path));
}
else
{
@@ -745,7 +747,7 @@ private void RefreshInstanceNode(TreeNode instanceNode, LiveInstance liveInstanc
///
private ServerEnvironment CreateServerEnvironment(Instance instance, IAgentClient agentClient)
{
- return new ServerEnvironment(new ServerDirs(appData.AppSettings.ServerDir, instance), log)
+ return new ServerEnvironment(new ServerDirs(appData.AppSettings.PathOptions.ServerDir, instance), log)
{
AgentClient = agentClient
};
@@ -756,7 +758,7 @@ private ServerEnvironment CreateServerEnvironment(Instance instance, IAgentClien
///
private CommEnvironment CreateCommEnvironment(Instance instance, IAgentClient agentClient)
{
- return new CommEnvironment(new CommDirs(appData.AppSettings.CommDir, instance), log)
+ return new CommEnvironment(new CommDirs(appData.AppSettings.PathOptions.CommDir, instance), log)
{
AgentClient = agentClient
};
@@ -1700,11 +1702,13 @@ private void miProjectProperties_Click(object sender, EventArgs e)
FrmProjectProps frmProjectProps = new FrmProjectProps
{
ProjectName = project.Name,
+ Version = project.Version,
Description = project.Description
};
if (frmProjectProps.ShowDialog() == DialogResult.OK && frmProjectProps.Modified)
{
+ project.Version = frmProjectProps.Version;
project.Description = frmProjectProps.Description;
SaveProjectSettings();
}
@@ -2139,6 +2143,10 @@ private void miInstanceDelete_Click(object sender, EventArgs e)
if (!liveInstance.Instance.DeleteInstanceFiles(out string errMsg))
appData.ProcError(errMsg);
+ project.DeploymentSettings.RemoveProfilesByInstance(liveInstance.Instance.ID, out bool affected);
+ if (affected && !project.DeploymentSettings.Save(out errMsg))
+ appData.ProcError(errMsg);
+
SetDeployMenuItemsEnabled();
SaveProjectSettings();
}
@@ -2280,8 +2288,8 @@ private void miCommLineImport_Click(object sender, EventArgs e)
if (selectedNode != null &&
FindClosestInstance(selectedNode, out LiveInstance liveInstance))
{
- FrmCommImport frmCommImport = new FrmCommImport(project, liveInstance.Instance);
CommEnvironment commEnv = liveInstance.CommEnvironment;
+ FrmCommImport frmCommImport = new FrmCommImport(project, liveInstance.Instance, commEnv);
TreeNode lastAddedNode = null;
if (selectedNode.TagIs(CommNodeType.CommLines))
@@ -2307,9 +2315,6 @@ private void miCommLineImport_Click(object sender, EventArgs e)
{
foreach (Comm.Settings.KP kpSettings in frmCommImport.ImportedDevices)
{
- if (commEnv.TryGetKPView(kpSettings, true, null, out KPView kpView, out string errMsg))
- kpSettings.SetReqParams(kpView.DefaultReqParams);
-
TreeNode kpNode = commShell.CreateDeviceNode(kpSettings, commLineSettings, commEnv);
selectedNode.Nodes.Add(kpNode);
lastAddedNode = kpNode;
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.resx b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.resx
index 58c2ada83..cd12807dd 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.resx
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmMain.resx
@@ -400,26 +400,26 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACuSURBVDhP5ZHBDQIhEEU5erQES7Abr17oyoQe6MADZ0ux
- AAgjH+brJLtrUI++ZAKZ3f+YZR0RkWPO+Y7CXttz1FpPLSTnq++1Rinl1pa9Rl60Jk6uCLrLrhf2KSXx
- 3j8rxkjJQaMDjGzDVkIoCSHI4hPxAgM2jJXYSSjR+BDwNArA2gQsoPEhwB30phFssRBgHP6FrwRt3y+k
- dydYCIh9+I5/EMzUpuATNPYrzj0A8BNdRyD/CXYAAAAASUVORK5CYII=
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACtSURBVDhP5ZExDgIhEEUpLT2CR/A2tjbcyoQ7cAMLao9i
+ TwgjH+brJLtrUEtfMoHM7n/Mso6IyDHnfEdhr+05aq2nFpLz1fdao5Rya8teIy9aEydXBN1l1wv7lJJ4
+ 758VY6TkoNEBRrZhKyGUhBBk8Yl4gQEbxkrsJJRofAh4GgVgbQIW0PgQ4A560wi2WAgwDv/CV4K27xfS
+ uxMsBMQ+fMc/CGZqU/AJGvsV5x6jZl0vfDROAwAAAABJRU5ErkJggg==
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJrSURBVDhPtVJLTBNRFK3WqKx068KFCz8LExdu3LgzMW5M
- JC5MkJBoDMYPGo2KxEhs6gIDWhK+0xYoRaEYULF8ym9YFLBBGgGtFSgWpKUF6VSmX9qZ43uPIUCiOz3J
- Sebee855b+6M6p8CgFqW5Qyl/COIZrvyuBXNzVBXWIcruY7h2SqrQyyz8pKu7QO4bidKWuyRx/Vdzgf6
- t/mFhvdHFcsGKmyOHK73468BdxAzwRWI8STITSBGk0ikJSxGUxgPhGHhHZLmVf+X3Ofm84pVpTL0fLrU
- yA9LVEhN63xUNoLjF1pxKteK1wNeLJAQyqHvAdCbZWv0mSzAxI8uL8ZSW8wpElbT5oaYSCGroA+Z97ox
- uRTFFKF3JY5+tw/aBtsCCyiydMubT09tYsfgHA6efQlT5xQzuwNRTIeiGPcLuKprjLCAwrp22UWGQizN
- KCbTzBwUYth/ug5NfTNsB3Nikp1OOe4Pg+xhLUBr7nO2j3nZYIm8ihhPs8XREKdHYGbKUCINn7gW1Oua
- R/YTI88C7nCth26Vt7SXWwdl+hW8QgLxlASXV8CRc02o7/KwgB+hCIamA3jW1rN65orGcPLi3QMsYB2l
- 72yV2lpLz2Wdft4+9lWOrkr45hMxH0liwh/Cizc2PKw2efKrG8z3uaYTim0D5A/bRpa4y9DFVxWazIlV
- ScaCkITdNYs8XW1Y32rLIZq9VEO1im0DtEm4gwgyrpUZlyVSjE77cL2oxt9ptx8m5R46oxqqVWxbQQeE
- 6tvF3E9+ZFLKK9YHNSXGY6S3m3AnnVGNIv87bpbqP994ygWzCrT7lNb/gkr1G4ayEoPjwdLoAAAAAElF
+ JC5MkJBoDMYPGo2KxEhsaiIJaEkqn2kLlqIUDKhQPi2UIbHyUWkQtSnysSD9gXSQ6Zd25vjeYwiQ6E5P
+ cpK5955z3ps7o/qnAKCWZTlLKf8IotmsPG5EUxPUFbaBSq5jYLrKNiTqbbykax0E53CjrNkVvV/X5b5j
+ eF1YbGw7qFjWUGEfyuN6Pv7q84YxFV6CmEiB3ARiLIVkRsJcLI3R0CIa+UFJ86L3a/5jy2nFqlIZu0fO
+ NfD9EhVS0yrv6T/g8JkWHMu34WWfD0ESQtn/PQR6s1yNIZsFmPnhhbl4eoM5TcJqWr0Qk2nkFDmRfcuB
+ b/MxjBP6lhLo9fqhrbcHWcAjq11ef3p6HTvezWDvyecwd44zszcUw0QkhtGAgIu6higLKK61yR4yFOIZ
+ RjGVYeawEMfu489gdU6xHcyIKXY65WhgEWQPKwFai9Pd/snHBvPkVcREhi2OhrgnBWamjCQz8IsrQT2e
+ WeQ+MPEs4AbXsu/a0+Z2fZtLpl/BJySRSEvw+AQcOGVFXdckC/gRiaJ/IoSSVsfyiQsa49GzN/ewgFWU
+ v7FXamsbu8/rDLNvRzxybFnCmF/EbDSFz4EInryy4261ebKwut5ym7MeUWxrIH/YJrLEbcYuvqrYbEku
+ SzKCQgouzzQKdLWLhhZ7HtHspBqqVWxroE3CLUSQdUlvWpBIMTzhx+WSmkCny7WflDvojGqoVrFtBB0Q
+ qq+Xcj/592NSQakhrCkzHSK97YRb6YxqFPnfcbXc8OXKQy6cU6TdpbT+F1Sq32ZoEnKoGQSMAAAAAElF
TkSuQmCC
@@ -435,13 +435,13 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFaSURBVDhP7ZFNSwJhFIX9US2CAqVVS6WIok3USiZsCIOI
- hIlcmGlkizIkFUNFQvqyQDIas8ShrIgQlIg+KQnKtGbUsqMNTg5lUNSyA8/qfc9zL1yJEJZlIYbjOFn5
- 6Xt5K9Xqa3h+JBBPFQsEyt++jrj4kT8RaIyzUGst6FQb0NathbxrEFK5skBozOS7QEBcFDi7ySFxyWEv
- 8YhILI3NgzTGbEE0tJCV7dwrkU+Cq9scj2eVgXWORg81hY5ePTb2UzBY6eoCMYLg5DqL+DmL3XgG20fp
- 6gLnUhj551c8ZQu4z7wgeZfHRTLLc1xaP3bKgollEDp8QCCawugMDWmTqiJwLGxhPRSFn97BcoDBvJ+B
- xxfhcS6GYfeGMekKYcIRhLE0fcRCo15BVATT7jWqBEx2H3RmL6hxF/p1Nh5yyAzlgAmtxDB/gcb2Psia
- VahTEFS5/p/fRSIpAjZDfk4E1A3yAAAAAElFTkSuQmCC
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFbSURBVDhP7ZFNSwJhFIX9US2CAqVVu5IiijZRm2LCJAwi
+ EiZyYaaRLcqQVAwVCenLAslozBKHsiJCUCL6pCQo05pRS082OM1QBkUtO/Cs3vc898KV8GEYBmJYlpWV
+ nr6Xt1KlroLjRwLxVLGAp/Tt64iLH/kTgdowC5XGjHaVHi3dGtR3DEJa15kn1Cblu4BHXOQ5u8kifsli
+ L/6IcDSFzYMUxqwB1DQphe1cK+FPgqvbLId7lYZljkIPOYW2Xh029pPQW6jyAjG84OQ6g9g5g91YGttH
+ qfICx1IIuecCnjJ53KdfkLjL4SKR4Tgurh89ZUBH0wgePsAfSWJ0hoK0QSEI7AtbWA9G4KN2sOynMe+j
+ 4faGORyLIdg8IUw6g5iwB2AoTh8xU6iWE4Jg2rVGFoHR5oXW5AE57kS/1sqhHDKha8CIZmKYu0Btax9k
+ jQpUyQmyVP/P7yKRvAIwmH5MuiM0ewAAAABJRU5ErkJggg==
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmProjectProps.Designer.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmProjectProps.Designer.cs
index ae9f140d6..7a6492203 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmProjectProps.Designer.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmProjectProps.Designer.cs
@@ -34,6 +34,11 @@ private void InitializeComponent()
this.txtDescr = new System.Windows.Forms.TextBox();
this.btnCancel = new System.Windows.Forms.Button();
this.btnOK = new System.Windows.Forms.Button();
+ this.lblVersion = new System.Windows.Forms.Label();
+ this.numMajorVersion = new System.Windows.Forms.NumericUpDown();
+ this.numMinorVersion = new System.Windows.Forms.NumericUpDown();
+ ((System.ComponentModel.ISupportInitialize)(this.numMajorVersion)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numMinorVersion)).BeginInit();
this.SuspendLayout();
//
// lblName
@@ -56,54 +61,92 @@ private void InitializeComponent()
// lblDescr
//
this.lblDescr.AutoSize = true;
- this.lblDescr.Location = new System.Drawing.Point(9, 48);
+ this.lblDescr.Location = new System.Drawing.Point(9, 87);
this.lblDescr.Name = "lblDescr";
this.lblDescr.Size = new System.Drawing.Size(60, 13);
- this.lblDescr.TabIndex = 2;
+ this.lblDescr.TabIndex = 5;
this.lblDescr.Text = "Description";
//
// txtDescr
//
this.txtDescr.AcceptsReturn = true;
- this.txtDescr.Location = new System.Drawing.Point(12, 64);
+ this.txtDescr.Location = new System.Drawing.Point(12, 103);
this.txtDescr.Multiline = true;
this.txtDescr.Name = "txtDescr";
this.txtDescr.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtDescr.Size = new System.Drawing.Size(400, 100);
- this.txtDescr.TabIndex = 3;
- this.txtDescr.TextChanged += new System.EventHandler(this.txtDescr_TextChanged);
+ this.txtDescr.TabIndex = 6;
+ this.txtDescr.TextChanged += new System.EventHandler(this.control_Changed);
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.btnCancel.Location = new System.Drawing.Point(337, 180);
+ this.btnCancel.Location = new System.Drawing.Point(337, 219);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
- this.btnCancel.TabIndex = 5;
+ this.btnCancel.TabIndex = 8;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
//
// btnOK
//
- this.btnOK.Location = new System.Drawing.Point(256, 180);
+ this.btnOK.Location = new System.Drawing.Point(256, 219);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(75, 23);
- this.btnOK.TabIndex = 4;
+ this.btnOK.TabIndex = 7;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
//
+ // lblVersion
+ //
+ this.lblVersion.AutoSize = true;
+ this.lblVersion.Location = new System.Drawing.Point(9, 48);
+ this.lblVersion.Name = "lblVersion";
+ this.lblVersion.Size = new System.Drawing.Size(42, 13);
+ this.lblVersion.TabIndex = 2;
+ this.lblVersion.Text = "Version";
+ //
+ // numMajorVersion
+ //
+ this.numMajorVersion.Location = new System.Drawing.Point(12, 64);
+ this.numMajorVersion.Maximum = new decimal(new int[] {
+ 1000000,
+ 0,
+ 0,
+ 0});
+ this.numMajorVersion.Name = "numMajorVersion";
+ this.numMajorVersion.Size = new System.Drawing.Size(100, 20);
+ this.numMajorVersion.TabIndex = 3;
+ this.numMajorVersion.ValueChanged += new System.EventHandler(this.control_Changed);
+ //
+ // numMinorVersion
+ //
+ this.numMinorVersion.Location = new System.Drawing.Point(118, 64);
+ this.numMinorVersion.Maximum = new decimal(new int[] {
+ 1000000,
+ 0,
+ 0,
+ 0});
+ this.numMinorVersion.Name = "numMinorVersion";
+ this.numMinorVersion.Size = new System.Drawing.Size(100, 20);
+ this.numMinorVersion.TabIndex = 4;
+ this.numMinorVersion.ValueChanged += new System.EventHandler(this.control_Changed);
+ //
// FrmProjectProps
//
this.AcceptButton = this.btnOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnCancel;
- this.ClientSize = new System.Drawing.Size(424, 215);
+ this.ClientSize = new System.Drawing.Size(424, 254);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOK);
this.Controls.Add(this.txtDescr);
this.Controls.Add(this.lblDescr);
+ this.Controls.Add(this.numMinorVersion);
+ this.Controls.Add(this.numMajorVersion);
+ this.Controls.Add(this.lblVersion);
this.Controls.Add(this.txtName);
this.Controls.Add(this.lblName);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
@@ -114,6 +157,8 @@ private void InitializeComponent()
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Project Properties";
this.Load += new System.EventHandler(this.FrmProjectProps_Load);
+ ((System.ComponentModel.ISupportInitialize)(this.numMajorVersion)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numMinorVersion)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@@ -127,5 +172,8 @@ private void InitializeComponent()
private System.Windows.Forms.TextBox txtDescr;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnOK;
+ private System.Windows.Forms.Label lblVersion;
+ private System.Windows.Forms.NumericUpDown numMajorVersion;
+ private System.Windows.Forms.NumericUpDown numMinorVersion;
}
}
\ No newline at end of file
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmProjectProps.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmProjectProps.cs
index fbf2a7eb1..93f55d581 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmProjectProps.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmProjectProps.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,18 +20,12 @@
*
* Author : Mikhail Shiryaev
* Created : 2018
- * Modified : 2018
+ * Modified : 2019
*/
+using Scada.Admin.Project;
using Scada.UI;
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Windows.Forms;
namespace Scada.Admin.App.Forms
@@ -66,6 +60,24 @@ public string ProjectName
}
}
+ ///
+ /// Gets or sets the project version.
+ ///
+ public ProjectVersion Version
+ {
+ get
+ {
+ return new ProjectVersion(
+ Convert.ToInt32(numMajorVersion.Value),
+ Convert.ToInt32(numMinorVersion.Value));
+ }
+ set
+ {
+ numMajorVersion.SetValue(value.Major);
+ numMinorVersion.SetValue(value.Minor);
+ }
+ }
+
///
/// Gets or sets the project description.
///
@@ -91,11 +103,10 @@ private void FrmProjectProps_Load(object sender, EventArgs e)
{
Translator.TranslateForm(this, GetType().FullName);
Modified = false;
- ActiveControl = txtDescr;
- txtDescr.Select(0, 0);
+ ActiveControl = numMajorVersion;
}
- private void txtDescr_TextChanged(object sender, EventArgs e)
+ private void control_Changed(object sender, EventArgs e)
{
Modified = true;
}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.Designer.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.Designer.cs
index ab66e4989..b345f3e13 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.Designer.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.Designer.cs
@@ -28,13 +28,19 @@ protected override void Dispose(bool disposing)
///
private void InitializeComponent()
{
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmStartPage));
this.pnlContent = new System.Windows.Forms.Panel();
this.lblNoRecentProjects = new System.Windows.Forms.Label();
this.lbRecentProjects = new System.Windows.Forms.ListBox();
this.btnOpenProject = new System.Windows.Forms.Button();
this.btnNewProject = new System.Windows.Forms.Button();
this.lblRecentProjects = new System.Windows.Forms.Label();
+ this.cmsProjectList = new System.Windows.Forms.ContextMenuStrip(this.components);
+ this.miRemoveFromList = new System.Windows.Forms.ToolStripMenuItem();
+ this.miCopyPath = new System.Windows.Forms.ToolStripMenuItem();
this.pnlContent.SuspendLayout();
+ this.cmsProjectList.SuspendLayout();
this.SuspendLayout();
//
// pnlContent
@@ -66,6 +72,7 @@ private void InitializeComponent()
this.lbRecentProjects.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.lbRecentProjects.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.lbRecentProjects.ContextMenuStrip = this.cmsProjectList;
this.lbRecentProjects.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
this.lbRecentProjects.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.lbRecentProjects.FormattingEnabled = true;
@@ -123,6 +130,31 @@ private void InitializeComponent()
this.lblRecentProjects.TabIndex = 0;
this.lblRecentProjects.Text = "Recent Projects";
//
+ // cmsProjectList
+ //
+ this.cmsProjectList.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.miRemoveFromList,
+ this.miCopyPath});
+ this.cmsProjectList.Name = "cmsProjectList";
+ this.cmsProjectList.Size = new System.Drawing.Size(181, 70);
+ this.cmsProjectList.Opening += new System.ComponentModel.CancelEventHandler(this.cmsProjectList_Opening);
+ //
+ // miRemoveFromList
+ //
+ this.miRemoveFromList.Image = ((System.Drawing.Image)(resources.GetObject("miRemoveFromList.Image")));
+ this.miRemoveFromList.Name = "miRemoveFromList";
+ this.miRemoveFromList.Size = new System.Drawing.Size(180, 22);
+ this.miRemoveFromList.Text = "Remove From List";
+ this.miRemoveFromList.Click += new System.EventHandler(this.miRemoveFromList_Click);
+ //
+ // miCopyPath
+ //
+ this.miCopyPath.Image = ((System.Drawing.Image)(resources.GetObject("miCopyPath.Image")));
+ this.miCopyPath.Name = "miCopyPath";
+ this.miCopyPath.Size = new System.Drawing.Size(180, 22);
+ this.miCopyPath.Text = "Copy Path";
+ this.miCopyPath.Click += new System.EventHandler(this.miCopyPath_Click);
+ //
// FrmStartPage
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -137,6 +169,7 @@ private void InitializeComponent()
this.Resize += new System.EventHandler(this.FrmStartPage_Resize);
this.pnlContent.ResumeLayout(false);
this.pnlContent.PerformLayout();
+ this.cmsProjectList.ResumeLayout(false);
this.ResumeLayout(false);
}
@@ -149,5 +182,8 @@ private void InitializeComponent()
private System.Windows.Forms.Button btnOpenProject;
private System.Windows.Forms.ListBox lbRecentProjects;
private System.Windows.Forms.Label lblNoRecentProjects;
+ private System.Windows.Forms.ContextMenuStrip cmsProjectList;
+ private System.Windows.Forms.ToolStripMenuItem miRemoveFromList;
+ private System.Windows.Forms.ToolStripMenuItem miCopyPath;
}
}
\ No newline at end of file
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.cs
index 91917b9f5..44d71a915 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.cs
@@ -27,6 +27,7 @@
using Scada.UI;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
@@ -140,12 +141,26 @@ private void OpenProject(ProjectItem item)
else
{
ScadaUiUtils.ShowWarning(string.Format(CommonPhrases.NamedFileNotFound, item.Path));
- lbRecentProjects.Items.Remove(item);
- appState.RemoveRecentProject(item.Path);
+ RemoveProjectFromList(item);
}
}
}
+ ///
+ /// Removes the project from the list of recent projects.
+ ///
+ private void RemoveProjectFromList(ProjectItem item)
+ {
+ lbRecentProjects.Items.Remove(item);
+ appState.RemoveRecentProject(item.Path);
+
+ if (lbRecentProjects.Items.Count == 0)
+ {
+ lbRecentProjects.Visible = false;
+ lblNoRecentProjects.Visible = true;
+ }
+ }
+
///
/// Saves the settings.
///
@@ -157,7 +172,7 @@ public void Save()
private void FrmStartPage_Load(object sender, EventArgs e)
{
- Translator.TranslateForm(this, GetType().FullName);
+ Translator.TranslateForm(this, GetType().FullName, null, cmsProjectList);
FillRecentProjectList();
}
@@ -173,6 +188,33 @@ private void FrmStartPage_Resize(object sender, EventArgs e)
pnlContent.Left = Math.Max(0, (Width - pnlContent.Width) / 2);
}
+
+ private void cmsProjectList_Opening(object sender, CancelEventArgs e)
+ {
+ if (hoverItemIndex >= 0)
+ {
+ cmsProjectList.Tag = lbRecentProjects.Items[hoverItemIndex];
+ }
+ else
+ {
+ cmsProjectList.Tag = null;
+ e.Cancel = true;
+ }
+ }
+
+ private void miRemoveFromList_Click(object sender, EventArgs e)
+ {
+ if (cmsProjectList.Tag is ProjectItem item)
+ RemoveProjectFromList(item);
+ }
+
+ private void miCopyPath_Click(object sender, EventArgs e)
+ {
+ if (cmsProjectList.Tag is ProjectItem item && !string.IsNullOrEmpty(item.Path))
+ Clipboard.SetText(item.Path);
+ }
+
+
private void lbRecentProjects_MouseMove(object sender, MouseEventArgs e)
{
int prevIndex = hoverItemIndex;
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.resx b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.resx
index 1af7de150..3c7486805 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.resx
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/FrmStartPage.resx
@@ -117,4 +117,27 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ 17, 17
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAAA8ElEQVQ4T2P4+v3f/++fPv5/
+ evrYfzIAA8OPn//+n5/a9X+ZOu//I1XZUHEIOHXoIFjs9bNn/2NiYv67uLjAMRQwMHz/8e//6xtX/q8x
+ kQUbcrKjGixz9cCe/5sctf+fXzL7f3p6OopmFANABIj14enj/9v9reGGgAy8tX4FSAofQBgAAqCwgLlk
+ Z5wPVBQvwO4CkNNBNC7no3jh9+9//+/v3Qa3GRaQIOeDxB6cOYE/DEDOBmlE1gwDoMAFuQpE4wAMDP/+
+ /QdrBEUlGQARBrj8igtDAcIAbIrwYShAjUYyABUNwOZMfBgKBjwM/jMAAFhoExHBHT1UAAAAAElFTkSu
+ QmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAABaSURBVDhP7ZJLCsAwCEQ9m/e/S9qN2ymWfEASOy1ZduAR
+ FOetIgAkxszAcHdXAlVNoQRHOafsF/jAkAp8mUEJ2kHkF4yCv41PgshrQf1nPfsFGY8ChtrrASAXC7HT
+ o827C3kAAAAASUVORK5CYII=
+
+
\ No newline at end of file
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.Designer.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.Designer.cs
index 36926c5cf..6a932ef01 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.Designer.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.Designer.cs
@@ -31,41 +31,45 @@ private void InitializeComponent()
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmBaseTable));
this.bindingNavigator = new System.Windows.Forms.BindingNavigator(this.components);
- this.btnAddNew = new System.Windows.Forms.ToolStripButton();
- this.bindingSource = new System.Windows.Forms.BindingSource(this.components);
this.lblCount = new System.Windows.Forms.ToolStripLabel();
- this.btnMoveFirst = new System.Windows.Forms.ToolStripButton();
- this.btnMovePrevious = new System.Windows.Forms.ToolStripButton();
this.sep1 = new System.Windows.Forms.ToolStripSeparator();
this.txtPosition = new System.Windows.Forms.ToolStripTextBox();
this.sep2 = new System.Windows.Forms.ToolStripSeparator();
+ this.sep3 = new System.Windows.Forms.ToolStripSeparator();
+ this.sep4 = new System.Windows.Forms.ToolStripSeparator();
+ this.sep5 = new System.Windows.Forms.ToolStripSeparator();
+ this.dataGridView = new System.Windows.Forms.DataGridView();
+ this.pnlError = new System.Windows.Forms.Panel();
+ this.btnCloseError = new System.Windows.Forms.Button();
+ this.lblError = new System.Windows.Forms.Label();
+ this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
+ this.folderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
+ this.cmsTable = new System.Windows.Forms.ContextMenuStrip(this.components);
+ this.bindingSource = new System.Windows.Forms.BindingSource(this.components);
+ this.btnAddNew = new System.Windows.Forms.ToolStripButton();
+ this.btnMoveFirst = new System.Windows.Forms.ToolStripButton();
+ this.btnMovePrevious = new System.Windows.Forms.ToolStripButton();
this.btnMoveNext = new System.Windows.Forms.ToolStripButton();
this.btnMoveLast = new System.Windows.Forms.ToolStripButton();
- this.sep3 = new System.Windows.Forms.ToolStripSeparator();
this.btnApplyEdit = new System.Windows.Forms.ToolStripButton();
this.btnCancelEdit = new System.Windows.Forms.ToolStripButton();
this.btnRefresh = new System.Windows.Forms.ToolStripButton();
this.btnDelete = new System.Windows.Forms.ToolStripButton();
this.btnClear = new System.Windows.Forms.ToolStripButton();
- this.sep4 = new System.Windows.Forms.ToolStripSeparator();
this.btnCut = new System.Windows.Forms.ToolStripButton();
this.btnCopy = new System.Windows.Forms.ToolStripButton();
this.btnPaste = new System.Windows.Forms.ToolStripButton();
- this.sep5 = new System.Windows.Forms.ToolStripSeparator();
this.btnFind = new System.Windows.Forms.ToolStripButton();
this.btnAutoSizeColumns = new System.Windows.Forms.ToolStripButton();
this.btnProperties = new System.Windows.Forms.ToolStripButton();
- this.dataGridView = new System.Windows.Forms.DataGridView();
- this.pnlError = new System.Windows.Forms.Panel();
- this.btnCloseError = new System.Windows.Forms.Button();
- this.lblError = new System.Windows.Forms.Label();
- this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
- this.folderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
+ this.miProperties = new System.Windows.Forms.ToolStripMenuItem();
+ this.btnFilter = new System.Windows.Forms.ToolStripButton();
((System.ComponentModel.ISupportInitialize)(this.bindingNavigator)).BeginInit();
this.bindingNavigator.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)(this.bindingSource)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit();
this.pnlError.SuspendLayout();
+ this.cmsTable.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.bindingSource)).BeginInit();
this.SuspendLayout();
//
// bindingNavigator
@@ -96,6 +100,7 @@ private void InitializeComponent()
this.btnPaste,
this.sep5,
this.btnFind,
+ this.btnFilter,
this.btnAutoSizeColumns,
this.btnProperties});
this.bindingNavigator.Location = new System.Drawing.Point(0, 40);
@@ -108,6 +113,109 @@ private void InitializeComponent()
this.bindingNavigator.Size = new System.Drawing.Size(584, 25);
this.bindingNavigator.TabIndex = 1;
//
+ // lblCount
+ //
+ this.lblCount.Name = "lblCount";
+ this.lblCount.Size = new System.Drawing.Size(35, 22);
+ this.lblCount.Text = "of {0}";
+ this.lblCount.ToolTipText = "Total Number of Items";
+ //
+ // sep1
+ //
+ this.sep1.Name = "sep1";
+ this.sep1.Size = new System.Drawing.Size(6, 25);
+ //
+ // txtPosition
+ //
+ this.txtPosition.AccessibleName = "Position";
+ this.txtPosition.AutoSize = false;
+ this.txtPosition.Name = "txtPosition";
+ this.txtPosition.Size = new System.Drawing.Size(50, 23);
+ this.txtPosition.Text = "0";
+ this.txtPosition.ToolTipText = "Current Position";
+ //
+ // sep2
+ //
+ this.sep2.Name = "sep2";
+ this.sep2.Size = new System.Drawing.Size(6, 25);
+ //
+ // sep3
+ //
+ this.sep3.Name = "sep3";
+ this.sep3.Size = new System.Drawing.Size(6, 25);
+ //
+ // sep4
+ //
+ this.sep4.Name = "sep4";
+ this.sep4.Size = new System.Drawing.Size(6, 25);
+ //
+ // sep5
+ //
+ this.sep5.Name = "sep5";
+ this.sep5.Size = new System.Drawing.Size(6, 25);
+ //
+ // dataGridView
+ //
+ this.dataGridView.AllowUserToOrderColumns = true;
+ this.dataGridView.AutoGenerateColumns = false;
+ this.dataGridView.DataSource = this.bindingSource;
+ this.dataGridView.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.dataGridView.Location = new System.Drawing.Point(0, 65);
+ this.dataGridView.Name = "dataGridView";
+ this.dataGridView.Size = new System.Drawing.Size(584, 296);
+ this.dataGridView.TabIndex = 2;
+ this.dataGridView.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView_CellClick);
+ this.dataGridView.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridView_CellFormatting);
+ this.dataGridView.CellMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dataGridView_CellMouseClick);
+ this.dataGridView.CellValidating += new System.Windows.Forms.DataGridViewCellValidatingEventHandler(this.dataGridView_CellValidating);
+ this.dataGridView.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.dataGridView_DataError);
+ this.dataGridView.EditingControlShowing += new System.Windows.Forms.DataGridViewEditingControlShowingEventHandler(this.dataGridView_EditingControlShowing);
+ this.dataGridView.RowValidating += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.dataGridView_RowValidating);
+ //
+ // pnlError
+ //
+ this.pnlError.AutoSize = true;
+ this.pnlError.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(242)))), ((int)(((byte)(222)))), ((int)(((byte)(222)))));
+ this.pnlError.Controls.Add(this.btnCloseError);
+ this.pnlError.Controls.Add(this.lblError);
+ this.pnlError.Dock = System.Windows.Forms.DockStyle.Top;
+ this.pnlError.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(169)))), ((int)(((byte)(68)))), ((int)(((byte)(66)))));
+ this.pnlError.Location = new System.Drawing.Point(0, 0);
+ this.pnlError.Name = "pnlError";
+ this.pnlError.Size = new System.Drawing.Size(584, 40);
+ this.pnlError.TabIndex = 0;
+ this.pnlError.Visible = false;
+ //
+ // btnCloseError
+ //
+ this.btnCloseError.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnCloseError.Location = new System.Drawing.Point(497, 8);
+ this.btnCloseError.Name = "btnCloseError";
+ this.btnCloseError.Size = new System.Drawing.Size(75, 23);
+ this.btnCloseError.TabIndex = 1;
+ this.btnCloseError.Text = "Close";
+ this.btnCloseError.UseVisualStyleBackColor = true;
+ this.btnCloseError.Click += new System.EventHandler(this.btnCloseError_Click);
+ //
+ // lblError
+ //
+ this.lblError.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.lblError.Location = new System.Drawing.Point(0, 0);
+ this.lblError.Name = "lblError";
+ this.lblError.Padding = new System.Windows.Forms.Padding(5);
+ this.lblError.Size = new System.Drawing.Size(479, 40);
+ this.lblError.TabIndex = 0;
+ this.lblError.Text = "Error message";
+ this.lblError.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // cmsTable
+ //
+ this.cmsTable.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.miProperties});
+ this.cmsTable.Name = "cmsTable";
+ this.cmsTable.Size = new System.Drawing.Size(128, 26);
+ //
// btnAddNew
//
this.btnAddNew.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -117,13 +225,6 @@ private void InitializeComponent()
this.btnAddNew.Size = new System.Drawing.Size(23, 22);
this.btnAddNew.Text = "Add New";
//
- // lblCount
- //
- this.lblCount.Name = "lblCount";
- this.lblCount.Size = new System.Drawing.Size(35, 22);
- this.lblCount.Text = "of {0}";
- this.lblCount.ToolTipText = "Total Number of Items";
- //
// btnMoveFirst
//
this.btnMoveFirst.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -142,25 +243,6 @@ private void InitializeComponent()
this.btnMovePrevious.Size = new System.Drawing.Size(23, 22);
this.btnMovePrevious.Text = "Move Previous";
//
- // sep1
- //
- this.sep1.Name = "sep1";
- this.sep1.Size = new System.Drawing.Size(6, 25);
- //
- // txtPosition
- //
- this.txtPosition.AccessibleName = "Position";
- this.txtPosition.AutoSize = false;
- this.txtPosition.Name = "txtPosition";
- this.txtPosition.Size = new System.Drawing.Size(50, 23);
- this.txtPosition.Text = "0";
- this.txtPosition.ToolTipText = "Current Position";
- //
- // sep2
- //
- this.sep2.Name = "sep2";
- this.sep2.Size = new System.Drawing.Size(6, 25);
- //
// btnMoveNext
//
this.btnMoveNext.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -179,11 +261,6 @@ private void InitializeComponent()
this.btnMoveLast.Size = new System.Drawing.Size(23, 22);
this.btnMoveLast.Text = "Move Last";
//
- // sep3
- //
- this.sep3.Name = "sep3";
- this.sep3.Size = new System.Drawing.Size(6, 25);
- //
// btnApplyEdit
//
this.btnApplyEdit.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -234,11 +311,6 @@ private void InitializeComponent()
this.btnClear.Text = "Clear Table";
this.btnClear.Click += new System.EventHandler(this.btnClear_Click);
//
- // sep4
- //
- this.sep4.Name = "sep4";
- this.sep4.Size = new System.Drawing.Size(6, 25);
- //
// btnCut
//
this.btnCut.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -269,11 +341,6 @@ private void InitializeComponent()
this.btnPaste.Text = "Paste (Ctrl+V)";
this.btnPaste.Click += new System.EventHandler(this.btnPaste_Click);
//
- // sep5
- //
- this.sep5.Name = "sep5";
- this.sep5.Size = new System.Drawing.Size(6, 25);
- //
// btnFind
//
this.btnFind.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -304,59 +371,23 @@ private void InitializeComponent()
this.btnProperties.Text = "Item Properties";
this.btnProperties.Click += new System.EventHandler(this.btnProperties_Click);
//
- // dataGridView
- //
- this.dataGridView.AllowUserToOrderColumns = true;
- this.dataGridView.AutoGenerateColumns = false;
- this.dataGridView.DataSource = this.bindingSource;
- this.dataGridView.Dock = System.Windows.Forms.DockStyle.Fill;
- this.dataGridView.Location = new System.Drawing.Point(0, 65);
- this.dataGridView.Name = "dataGridView";
- this.dataGridView.Size = new System.Drawing.Size(584, 296);
- this.dataGridView.TabIndex = 2;
- this.dataGridView.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView_CellClick);
- this.dataGridView.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridView_CellFormatting);
- this.dataGridView.CellValidating += new System.Windows.Forms.DataGridViewCellValidatingEventHandler(this.dataGridView_CellValidating);
- this.dataGridView.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.dataGridView_DataError);
- this.dataGridView.EditingControlShowing += new System.Windows.Forms.DataGridViewEditingControlShowingEventHandler(this.dataGridView_EditingControlShowing);
- this.dataGridView.RowValidating += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.dataGridView_RowValidating);
- //
- // pnlError
- //
- this.pnlError.AutoSize = true;
- this.pnlError.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(242)))), ((int)(((byte)(222)))), ((int)(((byte)(222)))));
- this.pnlError.Controls.Add(this.btnCloseError);
- this.pnlError.Controls.Add(this.lblError);
- this.pnlError.Dock = System.Windows.Forms.DockStyle.Top;
- this.pnlError.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(169)))), ((int)(((byte)(68)))), ((int)(((byte)(66)))));
- this.pnlError.Location = new System.Drawing.Point(0, 0);
- this.pnlError.Name = "pnlError";
- this.pnlError.Size = new System.Drawing.Size(584, 40);
- this.pnlError.TabIndex = 0;
- this.pnlError.Visible = false;
- //
- // btnCloseError
+ // miProperties
//
- this.btnCloseError.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.btnCloseError.Location = new System.Drawing.Point(497, 8);
- this.btnCloseError.Name = "btnCloseError";
- this.btnCloseError.Size = new System.Drawing.Size(75, 23);
- this.btnCloseError.TabIndex = 1;
- this.btnCloseError.Text = "Close";
- this.btnCloseError.UseVisualStyleBackColor = true;
- this.btnCloseError.Click += new System.EventHandler(this.btnCloseError_Click);
+ this.miProperties.Image = ((System.Drawing.Image)(resources.GetObject("miProperties.Image")));
+ this.miProperties.Name = "miProperties";
+ this.miProperties.Size = new System.Drawing.Size(127, 22);
+ this.miProperties.Text = "Properties";
+ this.miProperties.Click += new System.EventHandler(this.btnProperties_Click);
//
- // lblError
+ // btnFilter
//
- this.lblError.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.lblError.Location = new System.Drawing.Point(0, 0);
- this.lblError.Name = "lblError";
- this.lblError.Padding = new System.Windows.Forms.Padding(5);
- this.lblError.Size = new System.Drawing.Size(479, 40);
- this.lblError.TabIndex = 0;
- this.lblError.Text = "Error message";
- this.lblError.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ this.btnFilter.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.btnFilter.Image = ((System.Drawing.Image)(resources.GetObject("btnFilter.Image")));
+ this.btnFilter.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.btnFilter.Name = "btnFilter";
+ this.btnFilter.Size = new System.Drawing.Size(23, 22);
+ this.btnFilter.Text = "Filter";
+ this.btnFilter.Click += new System.EventHandler(this.btnFilter_Click);
//
// FrmBaseTable
//
@@ -376,9 +407,10 @@ private void InitializeComponent()
((System.ComponentModel.ISupportInitialize)(this.bindingNavigator)).EndInit();
this.bindingNavigator.ResumeLayout(false);
this.bindingNavigator.PerformLayout();
- ((System.ComponentModel.ISupportInitialize)(this.bindingSource)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dataGridView)).EndInit();
this.pnlError.ResumeLayout(false);
+ this.cmsTable.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.bindingSource)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@@ -417,5 +449,8 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripButton btnProperties;
private System.Windows.Forms.BindingSource bindingSource;
private System.Windows.Forms.DataGridView dataGridView;
+ private System.Windows.Forms.ContextMenuStrip cmsTable;
+ private System.Windows.Forms.ToolStripMenuItem miProperties;
+ private System.Windows.Forms.ToolStripButton btnFilter;
}
}
\ No newline at end of file
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.cs
index c23ddf612..4846c7e7a 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.cs
@@ -29,6 +29,7 @@
using Scada.Data.Tables;
using Scada.UI;
using System;
+using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
@@ -73,6 +74,7 @@ private class CellBuffer
private DataTable dataTable; // the table used by a grid view control
private int maxRowID; // the maximum ID in the table
private FrmFind frmFind; // the find and replace form
+ private FrmFilter frmFilter; // the filter form
///
@@ -97,6 +99,7 @@ public FrmBaseTable(IBaseTable baseTable, TableFilter tableFilter, ScadaProject
dataTable = null;
maxRowID = 0;
frmFind = null;
+ frmFilter = null;
Text = baseTable.Title + (tableFilter == null ? "" : " - " + tableFilter);
}
@@ -154,6 +157,9 @@ private void LoadTableData()
if (!project.ConfigBase.Load(out string errMsg))
appData.ProcError(errMsg);
+ // save the existing filter
+ string rowFilter = dataTable?.DefaultView.RowFilter ?? "";
+
// reset the binding source
bindingSource.DataSource = null;
@@ -164,6 +170,7 @@ private void LoadTableData()
dataTable.DefaultView.Sort = baseTable.PrimaryKey;
maxRowID = dataTable.DefaultView.Count > 0 ?
(int)dataTable.DefaultView[dataTable.DefaultView.Count - 1][baseTable.PrimaryKey] : 0;
+ dataTable.DefaultView.RowFilter = rowFilter;
// set the binding source before creating grid columns in case of work on Mono
if (ScadaUtils.IsRunningOnMono)
@@ -607,7 +614,12 @@ private void CopyCell(bool cut)
if (cell.IsInEditMode)
{
if (dataGridView.EditingControl is TextBox textBox)
- textBox.Cut();
+ {
+ if (cut)
+ textBox.Cut();
+ else
+ textBox.Copy();
+ }
}
else
{
@@ -645,7 +657,8 @@ private void PasteCell()
if (col is DataGridViewTextBoxColumn)
{
- if (dataGridView.BeginEdit(true))
+ // do nothing if the cell is already in edit mode
+ if (!cell.IsInEditMode && dataGridView.BeginEdit(true))
{
if (dataGridView.EditingControl is TextBox textBox)
textBox.Paste();
@@ -717,7 +730,7 @@ public void Save()
private void FrmBaseTable_Load(object sender, EventArgs e)
{
- Translator.TranslateForm(this, GetType().FullName);
+ Translator.TranslateForm(this, GetType().FullName, null, cmsTable);
if (lblCount.Text.Contains("{0}"))
bindingNavigator.CountItemFormat = lblCount.Text;
@@ -870,6 +883,25 @@ private void dataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
}
}
+ private void dataGridView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
+ {
+ int colInd = e.ColumnIndex;
+ int rowInd = e.RowIndex;
+
+ if (0 <= rowInd && rowInd < dataGridView.RowCount &&
+ 0 <= colInd && colInd < dataGridView.ColumnCount &&
+ e.Button == MouseButtons.Right && e.Clicks == 1 &&
+ (dataGridView.CurrentCell == null || !dataGridView.CurrentCell.IsInEditMode))
+ {
+ // select cell on right-click
+ dataGridView.CurrentCell = dataGridView[colInd, rowInd];
+
+ // show context menu
+ if (ProperiesAvailable)
+ cmsTable.Show(MousePosition);
+ }
+ }
+
private void dataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
// write and display a error
@@ -1048,8 +1080,8 @@ private void btnFind_Click(object sender, EventArgs e)
frmFind = new FrmFind(this, dataGridView);
// center the form within the bounds of its parent
- frmFind.Left = (Left + Right - frmFind.Width) / 2;
- frmFind.Top = (Top + Bottom - frmFind.Height) / 2;
+ frmFind.Left = (ParentForm.Left + ParentForm.Right - frmFind.Width) / 2;
+ frmFind.Top = (ParentForm.Top + ParentForm.Bottom - frmFind.Height) / 2;
frmFind.Show(this);
}
else
@@ -1058,6 +1090,19 @@ private void btnFind_Click(object sender, EventArgs e)
}
}
+ private void btnFilter_Click(object sender, EventArgs e)
+ {
+ frmFilter = frmFilter ?? new FrmFilter(dataGridView);
+ frmFilter.DataTable = dataTable;
+
+ if (frmFilter.ShowDialog() == DialogResult.OK)
+ {
+ btnFilter.Image = frmFilter.FilterIsEmpty ?
+ Properties.Resources.filter :
+ Properties.Resources.filter_set;
+ }
+ }
+
private void btnAutoSizeColumns_Click(object sender, EventArgs e)
{
dataGridView.AutoSizeColumns();
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.resx b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.resx
index 9128c5975..078beb9e9 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.resx
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmBaseTable.resx
@@ -195,21 +195,21 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANJSURBVDhPbZJrTFNnHMZPNpctmR/8oCHZvASibqOsapag
- 8YONCXgDNIvDZanEK50gOEy8IY0HKJhOtHUZRPBCQ+YNb2ARMU6tFyheVkq5SLg0bWk5VNrK4fTi2sM5
- z07LIe72S94v73l+/zznn5f4O7mVPTOzytuzC850NJG/WY2FOtuzvRpzW+7Phod5av2R/PL6OWL0v+w7
- 3RWXV2Wpr71HeZ47vX/aKC9rH/GxNqeH7R9+E77QaPLtUNa3fbuvNlVU3pOj6Uo4WtdrMfY6OZbj0WNn
- oLtvxzS8cNhJDr39Dn5n0cXAKrn2mKgSRP4vAx/nVnY2/DHMgOd50IEIMpSt2Hj0yZT9LyYmaGz4vjwi
- yzqVGRuQfcKYUtP8OiYPuBisLmzD0pN2JBc8QcsLCqO+d6IqtGA5RMIhdPYOYsmmslHZNt0nRFbx3boh
- Fw2DxQPJT4L8qxvLdQyWaFxYVNiNL3OfouaOVRwB0PQ4gn4v8kouBpPSS0ki57j+1STHoehCFxIOduLr
- Kh8k2lEkaSkkqh2QaCjEHzCj4mpfrCXD0PCMuXHuij4iSS9pJhRkoz36IYr68mt8tqcV8SorEvOfYauq
- DSsLHmPB4W7EbX8E51gAwYAfPo8bBoMhnJCi/J2QH7k+FI5MxgZE0RtHECe/h7RDBvEG2H/Ggtny+9Be
- 7xMaTAgDKDS1mCLzVhcaCNmOSkfPoFuMTmEaHMcpofI04/4wZm1qQPbJFxgT6lOuYejqGtiFa449INbn
- 6K7VNrwUo/+PP8RizsYbOKvvAzXiBOW0Yxd5LrR4HXmC2EXeSv6h+FKQ9odiYY57/zvTdNtoJP94FzaH
- CyZTB9/Y+pL9ZrPKtuK7ss8JhaLmo/WK6uqSqmZBZsGGhUUFAzExulsu+j4cHrSbrTB3mPno8rZsq2IT
- 00oVsYcUZd3us3NXyTWPlZqb8Lyh8C7gg5/2IsT4wLwdg5sSarscaDW289lF5yHIFZmZ1z4U9SlSsk7P
- T0ova1m2hWTU1U14auyA1TqEgf4BtL/qhPb8bUg3k96v0krU0tSKT0Xtn8hk5AxphipemlGmkmwofbBo
- jbL7i1SlJWGtUr94bXFNUka5lCDJD8S4AEH8BYISoUZ8laMPAAAAAElFTkSuQmCC
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANJSURBVDhPbZJrTFNnHMZPvEST7cM+aEjmLoFMNynWGRO2
+ 7IONCXgDNIZhslSizFGFgWPJpiKNh1IwnWirESJ4oTHzhuBwRdSoW5GrwBDKRSLQnBbqoVAqh9OLtodz
+ np2WQ7z+kvfLe57fP8/55yVeJ7Ok78PUotb0nDOPa8k/rS25RqrxZ31Xc+Yf5n+ydKZD2UWVS6Xou+w/
+ 2RORVWqprLhLux6NTr6kRpwcZR/nKGqMezoyHrhws9Odpq5s3r6/Il5SXpGh74k6fLHf0mCheI4X0Gdj
+ YbxnwxyCeLgZHpZ+q/Bj3iXvOqXhiKQSRPapwUWZJd01/42wEAQBjDeIJHUTth5+OGu/xfQ0gy3JmqAi
+ 9URKeED6sZa48ronYXnQwWJ9bjO+Pm5DbM5D3GmjMeZ+IaliC45HMOBHd/8QVm8rHFPsMi4mUvNvXxx2
+ MDBbXJD9IsqnnfjGyGK13oHlub34KrMB5bes0giAYabg80wiS3PJF5NYQBIZR00dMzyPvAs9iPq9G6tK
+ 3ZAZxhBjoBGts0OmpxH5WxeKrw2EW7IsA9eEE+eumoKyRE0doSJv2kIfQuiuPMHH+5oQqbUiOrsRO7XN
+ +C6nHp8f7EXE7n8xOuGFz+uB2+WE2WwORMWp7xPKQ1XDgeBMeEAIU8szRCjvIuGAWboBfj1jwRLlPRiq
+ BsQG0+IAGqbazuCn63PNhCKtxN435JSis3QOTeGEWHmOKU8AH22rQfrxNkyI9WnHCIzGKu6LDUceEJsz
+ jNcratql6Pvx+Dks3VqNs6YB0M9GQY/asIc851+xiTxG7CH/iv0h/7KP8fjDYZ5/9Ttz9FIMYvfeBmV3
+ oK2tXahuaufWJmupb78vXEaoVOULN6vKyjSldaLMgQuIi/J5w2Jot3zofdhdaO2yoqO9Qwgtb8euUi46
+ oUAVfkghNv109pN1Sn29Wn8DrnEaL7xueJhJ+Fk32OcTcNJibYcd9Q2NQnreeYhycUrK9fmSPktc6snP
+ YhIL76zZQbK6slo0tDyG1TqMwaeDaO3ohuH835Ank5MrEzQ6eXzxB5L2JgoFuUCepI2UJxVqZVsKHizf
+ oO79Ml5tidqoNq3YmF8ek1QkJ0hynhQXIYj/AYYYoNqJzpcEAAAAAElFTkSuQmCC
@@ -255,14 +255,14 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHSSURBVDhPYyAG/P//nwXKJAxWbNjXPXvpuidQLsO/f//0
- b958+q66ddIGqBB+MHnOapPW/jn/qlpmSIL4X79/b12yeuu/5JzKGLACYkD37JW/Js9ZtRjodP5HL968
- TMuv+SfOz68IlSYMlm3c9alr8qyfx09fbDh47Nxv/4iUv0JCnNJQacJg/sptm1t6Z//rnbpwf3l9+z8f
- v+hXUCniQOfEBY4lQI3JeXX//MKS/rp5B62CShEPEvLq34UlFP73Tav/4xffGgEVJgyi2t/qRrS/3uzb
- /OCnd8mJtT4FZ3/7FZ//7l16er5L1i4vqDLsIKL9lWFk17vP/g1Xv1uGTP9i6td11y1v3xv/6qv/XbN3
- /reKmT8BqhQ7CG19tiGs7el/x7QN/02D2n+YBPVu8S45/Mur5IqEU/rGBBOfjl9QpdiBX/2jX8FN9w3t
- U7YY6Pi0/LII6RXyqb76y6vqvIFZxLRsFYfk31Cl2IFP/b1f/iVXDOySVidp+dSDbfMsOn7WLmnVf02b
+ b958+q66ddIGqBB+MHnOapPG7hn/qlpmSIL4X79/b12wfOO/5JzKGLACYkD77JW/Js9ZtRjodP5HL968
+ TMuv+SfOz68IlSYMlm3c9alr8rSfx09fbDh47Nxv75CEv0JCnNJQacJg/sptmxs6p//rnbpwf3l98z8f
+ v+hXUCniQOfEBY4lQI3JeXX/vILi/rp5B62CShEPEvLq34UlFP73TKv/4xffGgEVJgyi2t/qRrS/3uzZ
+ /OCnd8mJtT45Z3/75Z//7l16er5L1i4vqDLsIKL9lWFk17vP/g0Xv1sGTvti6td11y1v3xv/6qv/XbN3
+ /reKmT8BqhQ7CG19tiGs7el/x7QN/02Dmn+YBPVu8S7Z/8ur5IqEU/rGBBOfll9QpdiBT/2jX8FN9w3t
+ U7YYaPg0/LII6RXyqb76y6vqvIFZxLRsBYf431Cl2IFP/b1f/iVXDOySViep+dSDbfMsOn7WLmnVf02b
5P8y8kpfwApxAe/Ki+fsk1b/Nw7o+q9uFPYYKMRsn7I6UtUk4Z26efh7Y7+2DIhKHMApY3OEUcikx6om
- se8MvKryoMK0BAwMAGiLyzrFA3hsAAAAAElFTkSuQmCC
+ se8MvKryoMK0BAwMADNZyrQzS9VcAAAAAElFTkSuQmCC
@@ -299,17 +299,30 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAItSURBVDhPzZNdSFNhGMdPQl9gIBIY1EWBWArBRErGFnYR
- axpthM6VXawadOOCzdiWbpzTxeZhH1ZHZ7DMGVrRmuUafWxS2HBUWAMZFBXYcpUNF35QWdPWv52zRQwN
- grrod/l7nuf/vjy8L/FPsXYFoxqLd3K7zDqWVRyW7uDQUn4RggMdntlPSeTztDGCoPIylsrL9b+BPaWF
- uQNpYzd4UhvKROYE65lLISzlF0GdHcTX5AJuDL+ClhnGpl1mpPWyn94XioLuHUWxqJX1uWja/IVUZwBK
- sh/VR7uwVWqHoMGBinrHS6XhCtQWH+RNfdhSQ6eDTVBS7sLsKEE4nU+WG9sDmJz5gpm5FEJPZ6CgbqFE
- TKNgW8uIrs0Hlo9zCwhGplCvu4bi3fSvW1h77huaLF6u6WHkPWy9j7FXdRlFQhJ6u+8EGxB5MYHw8w+w
- XBiBTNePdQIjjttuklwA7fRPq8xeSBp7UF7LoLSaRonIhCK+EQUVhqiavp6p1aVre6zYsMOItZV6VMrP
- JLmA1nP3kEp9x3j8M/yP3sLkCmPjTjJnUUdID97FZxGNJTAwNAaV5S5Ka7LLNDC3MT//LT38Bva+MBQn
- A/9ZQJ3GvZoNiE1MY/DBa3S4R1GrHcDm9K64hkPNrmcN+ouoUnSCv/80+PJTWC8k42VVVD5bl1FXVyia
- PQnxYce4ROWC8GA7yvdZsYqnnuIC/hSKyvwN9t2IjzErJdrza7jC30EQPwAY/5NEpMaO7AAAAABJRU5E
- rkJggg==
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIsSURBVDhPzZNfSFNhGIdPQklgIBIY1EWBWArBRErGFnYR
+ axpthM6VXawadOOCzdiWbpzTxeZhf6yOzsLMGVrRmuUalU4KG44KayCDogJbrrLhQieVNW392jlbxNAg
+ qIuey+d939/38fJ9xD/F2uUPayye6e0y60RGcVi6/SPL+SUIDrS75z4lkMfTRgiCyklbKifb/wb2lGZm
+ ENKGbvCkNpSKzDHWM5cDWM4vgTo7jK+JRdwcfQUtM4pNu8xI6RU/vTcQBt07jiJRC+uz0bQOFVAdPijJ
+ flQd7cJWqR2CegfK6xwvlYarUFu8kDf2YUs1nQo2QUm5CjKjBNHZ+WSlsc2H6fgXxOeTCDyNQ0HdRrGY
+ Rv625jFdqxcsH+cX4Q/NoE53HUW76V+3sPbcNzRaPFzTw9B72HofY6/qCgqFJPR27wk2IPRiCsHnH2C5
+ OAaZrh/rBEYct90iuQD63OCsyuyBpKEHZTUMSqpoFItMKOQbkV9uCKvpG+labaq2x4oNO4xYW6FHhfxM
+ ggtoOX8PyeR3TEY/Y+jRW5icQWzcSWYt6gjpxrvoHMKRGAZGJqCy3EVJdWaZBuYOFha+pYbfwN4XhOKk
+ 7z8LqNW4VrMBkalZDD94jXbXOGq0A9ic2hXXcKjJ+axefwmVig7w958GX34K64VktLSSymPrMuraKkWT
+ OyY+7JiUqJwQHmxD2T4rcnnqGS7gT6Go9N9g3434GJMr0V5YwxX+DoL4ARJkk0CcNwWHAAAAAElFTkSu
+ QmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGGSURBVDhPzZFLSwJRGIZn3T9oihbRH4h+QGszFCWwG4Vh
+ GgRBRJvClILIbqRpQUI3IoiyqMiyVEpBC2pTdFm07GbNjJN5oWnxdsYUI6J0Ez1wNgfe53zn/aj/gW6C
+ g9bKodnKQm1m0Dj6hPqRB6gGbiDpvkZ5xwVKtScoVvlQqPSgUOEGLd9DKk5RTRYO/ksBgas3HJx/HO+Z
+ gO1jAUv+BOyuGMwbUZgcEXTNP0M3zqFA5swIRGqHGRxeCVg/SmDWE4NlM4qB5Qj0Czza7WHobBzUYyxU
+ Jga0bBuVhtu8VDSDsp+B71zAjDuGodUXGBcj6Jzm0ToZToZrBsXwl5e/Iu8LwXv6CttWFN3zPNqmwtBY
+ WNSRCWnZzs/hNFLjAxyBBPkvjxYyesMoS0rLMpxGor/FnDeWFNDy3dzCIpKeO6wE4tCMs8iXu3MXVBju
+ 4QjGyfgMEXzaebaIPawFE6jqf0yuLXWdPVJjCKtEIOsVBb+s7jvKWs5QUr2PIoWTlOjKXfBHUNQ7CSv9
+ hoHEAn4AAAAASUVORK5CYII=
@@ -328,16 +341,16 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAI6SURBVDhPrZBdSJNRHMZHEEHdGCQEFlE35Y3UvAti201E
- r+6bqWilgTMLSiaCjmJCbiJJRqPZMKPWWmnLIorQCJwpTvvQNjI03dzWopkrM5n7fnp3trcNnHRRD/w4
- /3Px/M7hz/qv0ev1G3u6tDP9fZp5TaP8yFODKnqnsxm3dM3QdyihVddDVVce17U3KSUPuK1iM28wVU3n
- We9t0+q82nRGxpnDt7NgCLsVWPlYg9mXUlxSiNB2oxbqN2qkaunc7WotPFfCccF/Cn3XS0k55FJgde40
- lidk8I9SCA/sgclwAhIzD6wL3U4w9A7MwGzsQMijwrUWCRISwcH9CHyqxS9bKfyvuFh4cQCL97ei/YoQ
- vJvspMA4AvSPeUk54LqIuioK7rdK1FSKiUR4iAfnEzas+p0Y1ubi8sltEMm54HTmJwXPx3+SsteuIeW2
- q/fo8ygcliqUFZcAk7tQuDcXmzfloGD3BlTLBChvLAO7fTtYFut7JOLzudHScBzez05yfzc+RCRTPQXI
- y9mCyopj0HUb/yxtX3VeU758R1qAWCRJKtF4HOaHj6Go4ME+7YHHDxzmS9dufXB0rSBGl4OROJYDMXxd
- ihJmfesIhsbspGR4NEyIZ8yZTHnXEVisNiJgfhClDcFwDD9WIvjyPQKHLwybO4hJ918EzEsh2pP5MsNr
- Ryy7wDoxTQTMDwL0yLAYAlxLwIcFwEaTVVDfcB4UxUexQJpGJEORUAJKIEYRTeKk+KLsgn8Li/UbtxIX
- meUitloAAAAASUVORK5CYII=
+ r+6bqWilgTMLSiaCjmJCbiKNjEazYUrZWmbLIorQCJwZTvvQNjI03dzWopkrM5n7fnp3trcNnHRRD/w4
+ /3Px/M7hz/qvMRgMG3s7dTMD/Zp5TaP8yJNuVfR2RzNu6pthaFdCp66Hqq48rtc2KSX3ua1iM28oVU3n
+ ad8t0+q82nRGxpnDt7NgCLsVWPlYg9kXUlxSiNB2oxbqN2qkaunc6WwtPFfCccF/Cv3XS0k55FJgde40
+ lidk8I9SCA/ugannBCRmHlgXupxg6BucgdnYjpBHhWstEiQkgoP7EfhUi1+2UvhfcrHw/AAWe7dCe0UI
+ Xjc7KTC+AgbGvKQccF1EXRUF91slairFRCI8xIPzMRtWw06M6HJx+eQ2iORccDryk4Jn4z9J2WvXkHLb
+ 1bv0eRQOSxXKikuAyV0o3JuLzZtyULB7A6plApQ3loGt3Q6Wxfoeifh8brQ0HIf3s5Pc340PE8nUvQLk
+ 5WxBZcUx6LuMf5a2rzqvKV++Iy1ALJIklWg8DvODR1BU8GCf9sDjBw7zpWu3PjS6VhCjy8FIHMuBGL4u
+ RQmzvnUEw2N2Uup5OEKIZ8yZTHnXEVisNiJgfhClDcFwDD9WIvjyPQKHLwybO4hJ918EzEsh2pP5MsNr
+ Ryy7wDoxTQTMDwL0yLAYAlxLwIcFwEaTVVDfcB4UxUexQJpGJEORUAJKIEYRTeKk+KLsgn8Li/UbpJAX
+ lOpbcF8AAAAASUVORK5CYII=
@@ -346,6 +359,25 @@
435, 17
+
+ 598, 17
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1
+ MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAJCSURBVDhPpZBtSFNRGMcvQQT1
+ xSAhqAj7En6Rmt+CyNuHCK/uxY2paKVBLgtKJoKNokFuIUpGo5mUEmutGsskAskKnBlOe9E2MjTd3Nai
+ mSszmXu7+3fuXdOtJhEd+HHOeQ7/33nOoQD8F2mbHrN+raVLN/m0WzujaVQcfHRbHbvVrsZNvRodbSro
+ NPVQ11XE9S0qldRCXyT0pwk4es0G09KM1nRSvn8aX04hScSjxOJ7BaaeydCilKD5Ri00rzTpYQ7jdW3+
+ 6VLajcBxdF8r48NhtxJL0yewMCpHYIhBpG8HTIYjIB2AOtfpQhJz3yQsxjaEvedxtUkKTiLaswvBD7X4
+ YS9D4HkBZp/sxtzdjWi9LAbdJUgIjC+Ax8M+Phx0X0BdNQPPaxUUVSW8RLyXhuuhALaObRjUZePS0U2Q
+ 1BRgX3tuQtA78p0P+xxaPtx85Q6ZC+G0VqO8uBQY2478ndlYvy4LeTlrcEwuQkVjOQStm0FZbW/J0wG/
+ 34OmhsPwfXTx+zcjA7xk/F4etmRtQFXlIeg7jdwR/1e5NVvPEFYEYKMJfo1YPA7L/R4oK2k4JrzwBoAD
+ Qhl3lPbpVP/QnwKWhEPROBaCLD7Px3im/KsIBoYdXBGGB4M88ZR1KuO+VQRWm50rLncQI4ZQhMW3xSg+
+ fY3C6Y/A7glhzPMXQfKmMPGk3pzkpZPNLLCNTnDF5Q6CZJlkLgy454F3s4CdkFFQ33AWDCNEsUi2gkSO
+ IrEUjKgERQRuZoSSzILfC/8GqJ+iv/H+QwHX2gAAAABJRU5ErkJggg==
+
+
65
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.Designer.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.Designer.cs
new file mode 100644
index 000000000..65ceb4d4a
--- /dev/null
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.Designer.cs
@@ -0,0 +1,215 @@
+namespace Scada.Admin.App.Forms.Tables
+{
+ partial class FrmFilter
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.btnClearFilter = new System.Windows.Forms.Button();
+ this.btnOK = new System.Windows.Forms.Button();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.lblColumn = new System.Windows.Forms.Label();
+ this.cbColumn = new System.Windows.Forms.ComboBox();
+ this.lblOperation = new System.Windows.Forms.Label();
+ this.cbStringOperation = new System.Windows.Forms.ComboBox();
+ this.lblValue = new System.Windows.Forms.Label();
+ this.txtValue = new System.Windows.Forms.TextBox();
+ this.cbMathOperation = new System.Windows.Forms.ComboBox();
+ this.cbValue = new System.Windows.Forms.ComboBox();
+ this.cbBoolean = new System.Windows.Forms.ComboBox();
+ this.SuspendLayout();
+ //
+ // btnClearFilter
+ //
+ this.btnClearFilter.Location = new System.Drawing.Point(12, 102);
+ this.btnClearFilter.Name = "btnClearFilter";
+ this.btnClearFilter.Size = new System.Drawing.Size(75, 23);
+ this.btnClearFilter.TabIndex = 9;
+ this.btnClearFilter.Text = "Clear Filter";
+ this.btnClearFilter.UseVisualStyleBackColor = true;
+ this.btnClearFilter.Click += new System.EventHandler(this.btnClearFilter_Click);
+ //
+ // btnOK
+ //
+ this.btnOK.Location = new System.Drawing.Point(176, 102);
+ this.btnOK.Name = "btnOK";
+ this.btnOK.Size = new System.Drawing.Size(75, 23);
+ this.btnOK.TabIndex = 10;
+ this.btnOK.Text = "OK";
+ this.btnOK.UseVisualStyleBackColor = true;
+ this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
+ //
+ // btnCancel
+ //
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.Location = new System.Drawing.Point(257, 102);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.Size = new System.Drawing.Size(75, 23);
+ this.btnCancel.TabIndex = 11;
+ this.btnCancel.Text = "Cancel";
+ this.btnCancel.UseVisualStyleBackColor = true;
+ //
+ // lblColumn
+ //
+ this.lblColumn.AutoSize = true;
+ this.lblColumn.Location = new System.Drawing.Point(9, 9);
+ this.lblColumn.Name = "lblColumn";
+ this.lblColumn.Size = new System.Drawing.Size(42, 13);
+ this.lblColumn.TabIndex = 0;
+ this.lblColumn.Text = "Column";
+ //
+ // cbColumn
+ //
+ this.cbColumn.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbColumn.FormattingEnabled = true;
+ this.cbColumn.Location = new System.Drawing.Point(12, 25);
+ this.cbColumn.Name = "cbColumn";
+ this.cbColumn.Size = new System.Drawing.Size(320, 21);
+ this.cbColumn.TabIndex = 1;
+ this.cbColumn.SelectedIndexChanged += new System.EventHandler(this.cbColumn_SelectedIndexChanged);
+ //
+ // lblOperation
+ //
+ this.lblOperation.AutoSize = true;
+ this.lblOperation.Location = new System.Drawing.Point(9, 49);
+ this.lblOperation.Name = "lblOperation";
+ this.lblOperation.Size = new System.Drawing.Size(53, 13);
+ this.lblOperation.TabIndex = 2;
+ this.lblOperation.Text = "Operation";
+ //
+ // cbStringOperation
+ //
+ this.cbStringOperation.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbStringOperation.FormattingEnabled = true;
+ this.cbStringOperation.Items.AddRange(new object[] {
+ "Equals",
+ "Contains"});
+ this.cbStringOperation.Location = new System.Drawing.Point(12, 65);
+ this.cbStringOperation.Name = "cbStringOperation";
+ this.cbStringOperation.Size = new System.Drawing.Size(114, 21);
+ this.cbStringOperation.TabIndex = 3;
+ //
+ // lblValue
+ //
+ this.lblValue.AutoSize = true;
+ this.lblValue.Location = new System.Drawing.Point(129, 49);
+ this.lblValue.Name = "lblValue";
+ this.lblValue.Size = new System.Drawing.Size(34, 13);
+ this.lblValue.TabIndex = 5;
+ this.lblValue.Text = "Value";
+ //
+ // txtValue
+ //
+ this.txtValue.Location = new System.Drawing.Point(132, 65);
+ this.txtValue.Name = "txtValue";
+ this.txtValue.Size = new System.Drawing.Size(200, 20);
+ this.txtValue.TabIndex = 6;
+ //
+ // cbMathOperation
+ //
+ this.cbMathOperation.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbMathOperation.FormattingEnabled = true;
+ this.cbMathOperation.Items.AddRange(new object[] {
+ "=",
+ "<>",
+ "<",
+ "<=",
+ ">",
+ ">="});
+ this.cbMathOperation.Location = new System.Drawing.Point(12, 75);
+ this.cbMathOperation.Name = "cbMathOperation";
+ this.cbMathOperation.Size = new System.Drawing.Size(114, 21);
+ this.cbMathOperation.TabIndex = 4;
+ //
+ // cbValue
+ //
+ this.cbValue.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbValue.FormattingEnabled = true;
+ this.cbValue.Location = new System.Drawing.Point(132, 75);
+ this.cbValue.Name = "cbValue";
+ this.cbValue.Size = new System.Drawing.Size(200, 21);
+ this.cbValue.TabIndex = 7;
+ //
+ // cbBoolean
+ //
+ this.cbBoolean.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbBoolean.FormattingEnabled = true;
+ this.cbBoolean.Items.AddRange(new object[] {
+ "False",
+ "True"});
+ this.cbBoolean.Location = new System.Drawing.Point(132, 85);
+ this.cbBoolean.Name = "cbBoolean";
+ this.cbBoolean.Size = new System.Drawing.Size(200, 21);
+ this.cbBoolean.TabIndex = 8;
+ //
+ // FrmFilter
+ //
+ this.AcceptButton = this.btnOK;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.btnCancel;
+ this.ClientSize = new System.Drawing.Size(344, 137);
+ this.Controls.Add(this.btnCancel);
+ this.Controls.Add(this.btnOK);
+ this.Controls.Add(this.btnClearFilter);
+ this.Controls.Add(this.cbBoolean);
+ this.Controls.Add(this.cbValue);
+ this.Controls.Add(this.txtValue);
+ this.Controls.Add(this.lblValue);
+ this.Controls.Add(this.cbMathOperation);
+ this.Controls.Add(this.cbStringOperation);
+ this.Controls.Add(this.lblOperation);
+ this.Controls.Add(this.cbColumn);
+ this.Controls.Add(this.lblColumn);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "FrmFilter";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Filter";
+ this.Load += new System.EventHandler(this.FrmFilter_Load);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button btnClearFilter;
+ private System.Windows.Forms.Button btnOK;
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Label lblColumn;
+ private System.Windows.Forms.ComboBox cbColumn;
+ private System.Windows.Forms.Label lblOperation;
+ private System.Windows.Forms.ComboBox cbStringOperation;
+ private System.Windows.Forms.Label lblValue;
+ private System.Windows.Forms.TextBox txtValue;
+ private System.Windows.Forms.ComboBox cbMathOperation;
+ private System.Windows.Forms.ComboBox cbValue;
+ private System.Windows.Forms.ComboBox cbBoolean;
+ }
+}
\ No newline at end of file
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.cs
new file mode 100644
index 000000000..771019e34
--- /dev/null
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.cs
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : Administrator
+ * Summary : Form for setting a table filter.
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Scada.Admin.App.Code;
+using Scada.Data.Tables;
+using Scada.UI;
+using System;
+using System.Data;
+using System.Globalization;
+using System.Text;
+using System.Windows.Forms;
+
+namespace Scada.Admin.App.Forms.Tables
+{
+ ///
+ /// Form for setting a table filter.
+ /// Форма для установки фильтра по таблице.
+ ///
+ public partial class FrmFilter : Form
+ {
+ ///
+ /// Represents an extended filter.
+ /// Представляет расширенный фильтр.
+ ///
+ private class FilterExtended : TableFilter
+ {
+ private static readonly string[] MathOperations = { "=", "<>", "<", "<=", ">", ">=" };
+ public int StringOperation { get; set; }
+ public int MathOperation { get; set; }
+
+ public string GetRowFilter()
+ {
+ if (Value == null)
+ {
+ return "";
+ }
+ else if (Value is string)
+ {
+ return string.Format(StringOperation == 0 ?
+ "{0} = '{1}'" :
+ "{0} like '%{1}%'", ColumnName, Value);
+ }
+ else
+ {
+ string valStr = Value is double valDbl ?
+ valDbl.ToString(CultureInfo.InvariantCulture) :
+ Value.ToString();
+
+ return string.Format("{0} {1} {2}", ColumnName, MathOperations[MathOperation], valStr);
+ }
+ }
+ }
+
+ private readonly DataGridView dataGridView;
+ private FilterExtended currentFilter;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ private FrmFilter()
+ {
+ InitializeComponent();
+
+ cbMathOperation.Top = cbStringOperation.Top;
+ cbValue.Top = txtValue.Top;
+ cbBoolean.Top = txtValue.Top;
+ Translator.TranslateForm(this, GetType().FullName);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public FrmFilter(DataGridView dataGridView)
+ : this()
+ {
+ this.dataGridView = dataGridView ?? throw new ArgumentNullException("dataGridView");
+ currentFilter = null;
+ DataTable = null;
+ }
+
+
+ ///
+ /// Gets information of the selected column.
+ ///
+ private ColumnInfo SelectedColumnInfo
+ {
+ get
+ {
+ return cbColumn.SelectedItem as ColumnInfo;
+ }
+ }
+
+ ///
+ /// Gets or sets the data table being filtered.
+ ///
+ public DataTable DataTable { get; set; }
+
+ ///
+ /// Gets a value indicating whether the filter is not set.
+ ///
+ public bool FilterIsEmpty
+ {
+ get
+ {
+ return currentFilter == null;
+ }
+ }
+
+
+ ///
+ /// Fills the column list.
+ ///
+ private void FillColumnList(string selectedColumnName)
+ {
+ try
+ {
+ cbColumn.BeginUpdate();
+ cbColumn.Items.Clear();
+ int selectedIndex = 0;
+
+ foreach (DataGridViewColumn column in dataGridView.Columns)
+ {
+ if (column is DataGridViewTextBoxColumn ||
+ column is DataGridViewComboBoxColumn ||
+ column is DataGridViewCheckBoxColumn)
+ {
+ ColumnInfo columnInfo = new ColumnInfo(column);
+ int index = cbColumn.Items.Add(columnInfo);
+
+ if (column.Name == selectedColumnName)
+ selectedIndex = index;
+ }
+ }
+
+ if (cbColumn.Items.Count > 0)
+ cbColumn.SelectedIndex = selectedIndex;
+ }
+ finally
+ {
+ cbColumn.EndUpdate();
+ }
+ }
+
+ ///
+ /// Adjusts the controls depending on the selected column.
+ ///
+ private void AdjustControls(ColumnInfo columnInfo)
+ {
+ cbStringOperation.Visible = false;
+ cbStringOperation.SelectedIndex = 0;
+
+ cbMathOperation.Visible = false;
+ cbMathOperation.Enabled = true;
+ cbMathOperation.SelectedIndex = 0;
+
+ txtValue.Visible = false;
+ txtValue.Text = "";
+
+ cbValue.Visible = false;
+ cbValue.DataSource = null;
+
+ cbBoolean.Visible = false;
+ cbBoolean.SelectedIndex = 0;
+
+ if (columnInfo != null)
+ {
+ if (columnInfo.IsText)
+ {
+ if (columnInfo.IsNumber)
+ cbMathOperation.Visible = true;
+ else
+ cbStringOperation.Visible = true;
+
+ txtValue.Visible = true;
+ }
+ else if (columnInfo.IsComboBox)
+ {
+ cbMathOperation.Visible = true;
+ cbMathOperation.Enabled = false;
+
+ cbValue.Visible = true;
+ cbValue.DataSource = columnInfo.DataSource1;
+ cbValue.DisplayMember = columnInfo.DisplayMember;
+ cbValue.ValueMember = columnInfo.ValueMember;
+ }
+ else if (columnInfo.IsCheckBox)
+ {
+ cbMathOperation.Visible = true;
+ cbMathOperation.Enabled = false;
+ cbBoolean.Visible = true;
+ }
+ }
+ }
+
+ ///
+ /// Sets the default filter.
+ ///
+ private void SetDefaultFilter(ColumnInfo columnInfo)
+ {
+ DataGridViewCell curCell = dataGridView.CurrentCell;
+
+ if (columnInfo != null && curCell != null)
+ {
+ if (columnInfo.IsText)
+ {
+ txtValue.Text = curCell.Value?.ToString() ?? "";
+ }
+ else if (columnInfo.IsComboBox)
+ {
+ cbValue.SelectedValue = curCell.Value;
+ }
+ else if (columnInfo.IsCheckBox)
+ {
+ cbBoolean.SelectedIndex = (bool)curCell.Value ? 1 : 0;
+ }
+ }
+ }
+
+ ///
+ /// Sets the controls according to the current filter.
+ ///
+ private void DisplayCurrentFilter(ColumnInfo columnInfo)
+ {
+ if (columnInfo != null)
+ {
+ if (columnInfo.IsText)
+ {
+ cbStringOperation.SelectedIndex = currentFilter.StringOperation;
+ cbMathOperation.SelectedIndex = currentFilter.MathOperation;
+ txtValue.Text = currentFilter.Value?.ToString() ?? "";
+ }
+ else if (columnInfo.IsComboBox)
+ {
+ cbStringOperation.SelectedIndex = 0;
+ try { cbValue.SelectedValue = (int)currentFilter.Value; }
+ catch { }
+ }
+ else if (columnInfo.IsCheckBox)
+ {
+ cbStringOperation.SelectedIndex = 0;
+ cbBoolean.SelectedIndex = currentFilter.Value is bool val && val ? 1 : 0;
+ }
+ }
+ }
+
+ ///
+ /// Creates a new filter according to the controls.
+ ///
+ private FilterExtended CreateFilter(ColumnInfo columnInfo)
+ {
+ FilterExtended filter = new FilterExtended
+ {
+ ColumnName = columnInfo.Column.Name,
+ StringOperation = cbStringOperation.SelectedIndex,
+ MathOperation = cbMathOperation.SelectedIndex
+ };
+
+ if (columnInfo.IsText)
+ filter.Value = columnInfo.IsNumber ? (object)ScadaUtils.ParseDouble(txtValue.Text) : txtValue.Text;
+ else if (columnInfo.IsComboBox)
+ filter.Value = (cbValue.SelectedValue is int val) ? val : -1;
+ else if (columnInfo.IsCheckBox)
+ filter.Value = cbBoolean.SelectedIndex > 0;
+
+ return filter;
+ }
+
+
+
+ private void FrmFilter_Load(object sender, EventArgs e)
+ {
+ if (DataTable == null)
+ throw new InvalidOperationException("DataTable must not be null.");
+
+ ActiveControl = cbColumn;
+
+ if (currentFilter == null)
+ {
+ FillColumnList(dataGridView.CurrentCell?.OwningColumn.Name ?? "");
+ SetDefaultFilter(SelectedColumnInfo);
+ }
+ else
+ {
+ FillColumnList(currentFilter.ColumnName);
+ DisplayCurrentFilter(SelectedColumnInfo);
+ }
+ }
+
+ private void cbColumn_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ AdjustControls(SelectedColumnInfo);
+ }
+
+ private void btnClearFilter_Click(object sender, EventArgs e)
+ {
+ currentFilter = null;
+ DataTable.DefaultView.RowFilter = "";
+ DialogResult = DialogResult.OK;
+ }
+
+ private void btnOK_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ currentFilter = CreateFilter(SelectedColumnInfo);
+ DataTable.DefaultView.RowFilter = currentFilter == null ? "" : currentFilter.GetRowFilter();
+ DialogResult = DialogResult.OK;
+ }
+ catch (Exception ex)
+ {
+ ScadaUiUtils.ShowError(AppPhrases.IncorrectTableFilter);
+ }
+ }
+ }
+}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.resx b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.resx
new file mode 100644
index 000000000..1af7de150
--- /dev/null
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFilter.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFind.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFind.cs
index 1cfe51d6a..e5b67f355 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFind.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFind.cs
@@ -38,131 +38,6 @@ namespace Scada.Admin.App.Forms.Tables
///
public partial class FrmFind : Form
{
- ///
- /// Represents information associated with a column.
- /// Представляет информацию, связанную со столбцом.
- ///
- private class ColumnInfo
- {
- private DataTable dataSource1;
- private DataTable dataSource2;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public ColumnInfo(DataGridViewColumn column)
- {
- Column = column ?? throw new ArgumentNullException("column");
- dataSource1 = null;
- dataSource2 = null;
- }
-
- ///
- /// Gets the column.
- ///
- public DataGridViewColumn Column { get; private set; }
- ///
- /// Gets the column header.
- ///
- public string Header
- {
- get
- {
- return Column.HeaderText ?? "";
- }
- }
- ///
- /// Gets a value indicating whether the column contains text.
- ///
- public bool IsText
- {
- get
- {
- return Column is DataGridViewTextBoxColumn;
- }
- }
- ///
- /// Gets the column from which to retrieve strings for display in the combo box.
- ///
- public string DisplayMember
- {
- get
- {
- return Column is DataGridViewComboBoxColumn comboBoxColumn ? comboBoxColumn.DisplayMember : "";
- }
- }
- ///
- /// Gets the column from which to get values that correspond to the selections in the combo box.
- ///
- public string ValueMember
- {
- get
- {
- return Column is DataGridViewComboBoxColumn comboBoxColumn ? comboBoxColumn.ValueMember : "";
- }
- }
-
- ///
- /// Gets the data source #1 contains columns values.
- ///
- public DataTable DataSource1
- {
- get
- {
- if (dataSource1 == null)
- {
- dataSource1 = Column is DataGridViewComboBoxColumn comboBoxColumn ?
- CopyTable(comboBoxColumn.DataSource as DataTable) : null;
- }
-
- return dataSource1;
- }
- }
- ///
- /// Gets the data source #2 contains columns values.
- ///
- public DataTable DataSource2
- {
- get
- {
- if (dataSource2 == null)
- {
- dataSource2 = Column is DataGridViewComboBoxColumn comboBoxColumn ?
- CopyTable(comboBoxColumn.DataSource as DataTable) : null;
- }
-
- return dataSource2;
- }
- }
-
- ///
- /// Makes a table copy having disabled constraints.
- ///
- private DataTable CopyTable(DataTable dataTable)
- {
- if (dataTable == null)
- {
- return null;
- }
- else
- {
- DataTable tableCopy = new DataTable(dataTable.TableName);
- tableCopy.BeginLoadData();
- tableCopy.Merge(dataTable, false, MissingSchemaAction.Add);
- tableCopy.DefaultView.Sort = dataTable.DefaultView.Sort;
- return tableCopy;
- }
- }
- ///
- /// Returns a string that represents the current object.
- ///
- public override string ToString()
- {
- return Header;
- }
- }
-
-
private readonly FrmBaseTable frmBaseTable;
private readonly DataGridView dataGridView;
private int startRowIndex; // the starting point of the search
@@ -175,6 +50,8 @@ public override string ToString()
private FrmFind()
{
InitializeComponent();
+ cbFind.Top = txtFind.Top;
+ cbReplaceWith.Top = txtReplaceWith.Top;
}
///
@@ -191,70 +68,58 @@ public FrmFind(FrmBaseTable frmBaseTable, DataGridView dataGridView)
///
/// Fills the column list.
///
- private void FillColumnList(out ColumnInfo selColumnInfo)
+ private void FillColumnList()
{
try
{
- cbTableColumn.BeginUpdate();
- DataGridViewCell curCell = dataGridView.CurrentCell;
- int curColInd = curCell == null ? -1 : curCell.ColumnIndex;
+ cbColumn.BeginUpdate();
+ int curColInd = dataGridView.CurrentCell == null ? -1 : dataGridView.CurrentCell.ColumnIndex;
int selColInd = 0;
- selColumnInfo = null;
- for (int i = 0, k = 0; i < dataGridView.Columns.Count; i++)
+ foreach (DataGridViewColumn column in dataGridView.Columns)
{
- DataGridViewColumn column = dataGridView.Columns[i];
-
if ((column is DataGridViewTextBoxColumn || column is DataGridViewComboBoxColumn) &&
!column.ReadOnly)
{
ColumnInfo colInfo = new ColumnInfo(column);
- cbTableColumn.Items.Add(colInfo);
+ int index = cbColumn.Items.Add(colInfo);
- if (i == curColInd)
- {
- selColInd = k;
- selColumnInfo = colInfo;
- }
-
- k++;
+ if (column.Index == curColInd)
+ selColInd = index;
}
}
- if (cbTableColumn.Items.Count > 0)
- cbTableColumn.SelectedIndex = selColInd;
+ if (cbColumn.Items.Count > 0)
+ cbColumn.SelectedIndex = selColInd;
}
finally
{
- cbTableColumn.EndUpdate();
+ cbColumn.EndUpdate();
}
}
///
/// Sets the default search condition.
///
- private void SetDefaultSearchCondition(ColumnInfo selColumnInfo)
+ private void SetDefaultSearch(ColumnInfo columnInfo)
{
DataGridViewCell curCell = dataGridView.CurrentCell;
- if (curCell != null && selColumnInfo != null)
+ if (curCell != null && columnInfo != null)
{
- if (selColumnInfo.IsText)
+ if (columnInfo.IsText)
{
if (curCell.EditedFormattedValue != null)
txtFind.Text = curCell.EditedFormattedValue.ToString();
}
+ else if (curCell.IsInEditMode)
+ {
+ if (dataGridView.EditingControl is ComboBox comboBox)
+ cbFind.SelectedValue = comboBox.SelectedValue;
+ }
else
{
- if (curCell.IsInEditMode)
- {
- if (dataGridView.EditingControl is ComboBox comboBox)
- cbFind.SelectedValue = comboBox.SelectedValue;
- }
- else
- {
- cbFind.SelectedValue = curCell.Value;
- }
+ cbFind.SelectedValue = curCell.Value;
}
}
}
@@ -271,8 +136,8 @@ private void AdjustControls(ColumnInfo columnInfo)
else if (columnInfo.IsText)
{
txtFind.Visible = true;
- txtReplaceWith.Visible = true;
cbFind.Visible = false;
+ txtReplaceWith.Visible = true;
cbReplaceWith.Visible = false;
chkCaseSensitive.Enabled = true;
chkWholeCellOnly.Enabled = true;
@@ -282,8 +147,8 @@ private void AdjustControls(ColumnInfo columnInfo)
else
{
txtFind.Visible = false;
- txtReplaceWith.Visible = false;
cbFind.Visible = true;
+ txtReplaceWith.Visible = false;
cbReplaceWith.Visible = true;
chkCaseSensitive.Enabled = false;
chkWholeCellOnly.Enabled = false;
@@ -320,7 +185,7 @@ private bool IsMatched(DataGridViewCell cell, string value, bool ignoreCase, boo
{
StringComparison comparison = ignoreCase ?
StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture;
- string cellVal = cell.EditedFormattedValue == null ? "" : cell.EditedFormattedValue.ToString();
+ string cellVal = cell.EditedFormattedValue?.ToString() ?? "";
return wholeCellOnly && string.Compare(cellVal, value, ignoreCase) == 0 ||
!wholeCellOnly && cellVal.IndexOf(value, comparison) >= 0;
@@ -342,7 +207,7 @@ private bool IsMatched(DataGridViewCell cell, object value)
(dataGridView.EditingControl as ComboBox)?.SelectedValue :
cell.Value;
- return cellVal == value || (cellVal is int) && (value is int) && ((int)cellVal == (int)value);
+ return cellVal == value || (cellVal is int val1) && (value is int val2) && (val1 == val2);
}
}
@@ -514,17 +379,17 @@ private void FrmReplace_Load(object sender, EventArgs e)
{
Translator.TranslateForm(this, GetType().FullName);
- FillColumnList(out ColumnInfo selColumnInfo);
- SetDefaultSearchCondition(selColumnInfo);
+ FillColumnList();
+ SetDefaultSearch(cbColumn.SelectedItem as ColumnInfo);
ResetSearch();
- if (cbTableColumn.Items.Count == 0 || txtFind.Visible && txtFind.Text == "")
+ if (cbColumn.Items.Count == 0 || txtFind.Visible && txtFind.Text == "")
btnFindNext.Enabled = btnReplace.Enabled = btnReplaceAll.Enabled = false;
}
private void cbColumn_SelectedIndexChanged(object sender, EventArgs e)
{
- AdjustControls(cbTableColumn.SelectedItem as ColumnInfo);
+ AdjustControls(cbColumn.SelectedItem as ColumnInfo);
ResetSearch();
}
@@ -547,13 +412,13 @@ private void cbReplaceWith_SelectedIndexChanged(object sender, EventArgs e)
private void btnFindNext_Click(object sender, EventArgs e)
{
- if (cbTableColumn.SelectedItem is ColumnInfo columnInfo)
+ if (cbColumn.SelectedItem is ColumnInfo columnInfo)
FindNext(columnInfo, true);
}
private void btnReplace_Click(object sender, EventArgs e)
{
- if (cbTableColumn.SelectedItem is ColumnInfo columnInfo)
+ if (cbColumn.SelectedItem is ColumnInfo columnInfo)
{
ReplaceCell(columnInfo, true, out bool replaced, out bool noError);
if (noError)
@@ -563,7 +428,7 @@ private void btnReplace_Click(object sender, EventArgs e)
private void btnReplaceAll_Click(object sender, EventArgs e)
{
- if (cbTableColumn.SelectedItem is ColumnInfo columnInfo)
+ if (cbColumn.SelectedItem is ColumnInfo columnInfo)
ReplaceAll(columnInfo);
}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFind.designer.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFind.designer.cs
index 00ae5e243..1a672e808 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFind.designer.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tables/FrmFind.designer.cs
@@ -31,7 +31,7 @@ private void InitializeComponent()
this.btnReplaceAll = new System.Windows.Forms.Button();
this.btnClose = new System.Windows.Forms.Button();
this.lblColumn = new System.Windows.Forms.Label();
- this.cbTableColumn = new System.Windows.Forms.ComboBox();
+ this.cbColumn = new System.Windows.Forms.ComboBox();
this.lblFind = new System.Windows.Forms.Label();
this.txtFind = new System.Windows.Forms.TextBox();
this.lblReplaceWith = new System.Windows.Forms.Label();
@@ -74,15 +74,15 @@ private void InitializeComponent()
this.lblColumn.TabIndex = 0;
this.lblColumn.Text = "Column";
//
- // cbTableColumn
+ // cbColumn
//
- this.cbTableColumn.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
- this.cbTableColumn.FormattingEnabled = true;
- this.cbTableColumn.Location = new System.Drawing.Point(12, 25);
- this.cbTableColumn.Name = "cbTableColumn";
- this.cbTableColumn.Size = new System.Drawing.Size(320, 21);
- this.cbTableColumn.TabIndex = 1;
- this.cbTableColumn.SelectedIndexChanged += new System.EventHandler(this.cbColumn_SelectedIndexChanged);
+ this.cbColumn.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbColumn.FormattingEnabled = true;
+ this.cbColumn.Location = new System.Drawing.Point(12, 25);
+ this.cbColumn.Name = "cbColumn";
+ this.cbColumn.Size = new System.Drawing.Size(320, 21);
+ this.cbColumn.TabIndex = 1;
+ this.cbColumn.SelectedIndexChanged += new System.EventHandler(this.cbColumn_SelectedIndexChanged);
//
// lblFind
//
@@ -143,7 +143,7 @@ private void InitializeComponent()
//
this.cbFind.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbFind.FormattingEnabled = true;
- this.cbFind.Location = new System.Drawing.Point(12, 65);
+ this.cbFind.Location = new System.Drawing.Point(12, 75);
this.cbFind.Name = "cbFind";
this.cbFind.Size = new System.Drawing.Size(320, 21);
this.cbFind.TabIndex = 4;
@@ -154,7 +154,7 @@ private void InitializeComponent()
//
this.cbReplaceWith.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbReplaceWith.FormattingEnabled = true;
- this.cbReplaceWith.Location = new System.Drawing.Point(12, 105);
+ this.cbReplaceWith.Location = new System.Drawing.Point(12, 115);
this.cbReplaceWith.Name = "cbReplaceWith";
this.cbReplaceWith.Size = new System.Drawing.Size(320, 21);
this.cbReplaceWith.TabIndex = 7;
@@ -198,7 +198,7 @@ private void InitializeComponent()
this.Controls.Add(this.lblReplaceWith);
this.Controls.Add(this.txtFind);
this.Controls.Add(this.lblFind);
- this.Controls.Add(this.cbTableColumn);
+ this.Controls.Add(this.cbColumn);
this.Controls.Add(this.lblColumn);
this.Controls.Add(this.btnReplaceAll);
this.Controls.Add(this.btnClose);
@@ -219,7 +219,7 @@ private void InitializeComponent()
private System.Windows.Forms.Button btnReplaceAll;
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Label lblColumn;
- private System.Windows.Forms.ComboBox cbTableColumn;
+ private System.Windows.Forms.ComboBox cbColumn;
private System.Windows.Forms.Label lblFind;
private System.Windows.Forms.TextBox txtFind;
private System.Windows.Forms.Label lblReplaceWith;
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmCnlCreate.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmCnlCreate.cs
index 04e254982..c6f360447 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmCnlCreate.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmCnlCreate.cs
@@ -188,7 +188,8 @@ private bool CreateChannels()
List ctrlCnlPrototypes = ctrlCnlCreate1.CnlPrototypes.CtrlCnls;
int? objNum = ctrlCnlCreate2.ObjNum;
int kpNum = ctrlCnlCreate1.SelectedDevice.KPNum;
- string cnlPrefix = ctrlCnlCreate1.SelectedDevice.Name + " - ";
+ string cnlPrefix = appData.AppSettings.ChannelOptions.PrependDeviceName ?
+ ctrlCnlCreate1.SelectedDevice.Name + " - " : "";
int inCnlNum = ctrlCnlCreate3.StartInCnl;
int ctrlCnlNum = ctrlCnlCreate3.StartOutCnl;
int inCnlsAdded = 0;
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmCommImport.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmCommImport.cs
index cfde887e1..1f5cb3bfc 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmCommImport.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmCommImport.cs
@@ -26,6 +26,7 @@
using Scada.Admin.App.Code;
using Scada.Admin.Project;
using Scada.Comm;
+using Scada.Comm.Devices;
using Scada.Comm.Shell.Code;
using Scada.Data.Entities;
using Scada.Data.Tables;
@@ -42,8 +43,9 @@ namespace Scada.Admin.App.Forms.Tools
///
public partial class FrmCommImport : Form
{
- private readonly ScadaProject project; // the project under development
- private readonly Instance instance; // the import destination instance
+ private readonly ScadaProject project; // the project under development
+ private readonly Instance instance; // the import destination instance
+ private readonly CommEnvironment commEnvironment; // the Communicator environment
///
@@ -57,11 +59,12 @@ private FrmCommImport()
///
/// Initializes a new instance of the class.
///
- public FrmCommImport(ScadaProject project, Instance instance)
+ public FrmCommImport(ScadaProject project, Instance instance, CommEnvironment commEnvironment)
: this()
{
this.project = project ?? throw new ArgumentNullException("project");
this.instance = instance ?? throw new ArgumentNullException("instance");
+ this.commEnvironment = commEnvironment ?? throw new ArgumentNullException("commEnvironment");
CommLineSettings = null;
ImportedCommLines = null;
@@ -177,6 +180,10 @@ private void Import(out bool noData)
Settings.KP kpSettings = SettingsConverter.CreateKP(kpEntity,
project.ConfigBase.KPTypeTable);
kpSettings.Parent = commLineSettings;
+
+ if (commEnvironment.TryGetKPView(kpSettings, true, null, out KPView kpView, out string errMsg))
+ kpSettings.SetReqParams(kpView.DefaultReqParams);
+
commLineSettings.ReqSequence.Add(kpSettings);
ImportedDevices.Add(kpSettings);
}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmSettings.Designer.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmSettings.Designer.cs
index 8dd2f092a..53f68f2d8 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmSettings.Designer.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmSettings.Designer.cs
@@ -44,7 +44,8 @@ private void InitializeComponent()
this.btnBrowseServerDir = new System.Windows.Forms.Button();
this.txtServerDir = new System.Windows.Forms.TextBox();
this.lblServerDir = new System.Windows.Forms.Label();
- this.gbCnlNumOptions = new System.Windows.Forms.GroupBox();
+ this.gbChannelOptions = new System.Windows.Forms.GroupBox();
+ this.chkPrependDeviceName = new System.Windows.Forms.CheckBox();
this.numCnlGap = new System.Windows.Forms.NumericUpDown();
this.lblCnlGap = new System.Windows.Forms.Label();
this.lblCnlShift = new System.Windows.Forms.Label();
@@ -57,7 +58,7 @@ private void InitializeComponent()
this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
this.folderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
this.gbPathOptions.SuspendLayout();
- this.gbCnlNumOptions.SuspendLayout();
+ this.gbChannelOptions.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.numCnlGap)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numCnlShift)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numCnlMult)).BeginInit();
@@ -223,22 +224,34 @@ private void InitializeComponent()
this.lblServerDir.TabIndex = 0;
this.lblServerDir.Text = "Server directory";
//
- // gbCnlNumOptions
- //
- this.gbCnlNumOptions.Controls.Add(this.numCnlGap);
- this.gbCnlNumOptions.Controls.Add(this.lblCnlGap);
- this.gbCnlNumOptions.Controls.Add(this.lblCnlShift);
- this.gbCnlNumOptions.Controls.Add(this.numCnlShift);
- this.gbCnlNumOptions.Controls.Add(this.lblExplanation);
- this.gbCnlNumOptions.Controls.Add(this.numCnlMult);
- this.gbCnlNumOptions.Controls.Add(this.lblCnlMult);
- this.gbCnlNumOptions.Location = new System.Drawing.Point(12, 241);
- this.gbCnlNumOptions.Name = "gbCnlNumOptions";
- this.gbCnlNumOptions.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
- this.gbCnlNumOptions.Size = new System.Drawing.Size(410, 104);
- this.gbCnlNumOptions.TabIndex = 1;
- this.gbCnlNumOptions.TabStop = false;
- this.gbCnlNumOptions.Text = "Channel Numbering";
+ // gbChannelOptions
+ //
+ this.gbChannelOptions.Controls.Add(this.chkPrependDeviceName);
+ this.gbChannelOptions.Controls.Add(this.numCnlGap);
+ this.gbChannelOptions.Controls.Add(this.lblCnlGap);
+ this.gbChannelOptions.Controls.Add(this.lblCnlShift);
+ this.gbChannelOptions.Controls.Add(this.numCnlShift);
+ this.gbChannelOptions.Controls.Add(this.lblExplanation);
+ this.gbChannelOptions.Controls.Add(this.numCnlMult);
+ this.gbChannelOptions.Controls.Add(this.lblCnlMult);
+ this.gbChannelOptions.Location = new System.Drawing.Point(12, 241);
+ this.gbChannelOptions.Name = "gbChannelOptions";
+ this.gbChannelOptions.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
+ this.gbChannelOptions.Size = new System.Drawing.Size(410, 104);
+ this.gbChannelOptions.TabIndex = 1;
+ this.gbChannelOptions.TabStop = false;
+ this.gbChannelOptions.Text = "Channel Generation";
+ //
+ // chkPrependDeviceName
+ //
+ this.chkPrependDeviceName.AutoSize = true;
+ this.chkPrependDeviceName.Location = new System.Drawing.Point(158, 73);
+ this.chkPrependDeviceName.Name = "chkPrependDeviceName";
+ this.chkPrependDeviceName.Size = new System.Drawing.Size(130, 17);
+ this.chkPrependDeviceName.TabIndex = 7;
+ this.chkPrependDeviceName.Text = "Prepend device name";
+ this.chkPrependDeviceName.UseVisualStyleBackColor = true;
+ this.chkPrependDeviceName.CheckedChanged += new System.EventHandler(this.control_Changed);
//
// numCnlGap
//
@@ -368,7 +381,7 @@ private void InitializeComponent()
this.ClientSize = new System.Drawing.Size(434, 386);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOK);
- this.Controls.Add(this.gbCnlNumOptions);
+ this.Controls.Add(this.gbChannelOptions);
this.Controls.Add(this.gbPathOptions);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
@@ -380,8 +393,8 @@ private void InitializeComponent()
this.Load += new System.EventHandler(this.FrmSettings_Load);
this.gbPathOptions.ResumeLayout(false);
this.gbPathOptions.PerformLayout();
- this.gbCnlNumOptions.ResumeLayout(false);
- this.gbCnlNumOptions.PerformLayout();
+ this.gbChannelOptions.ResumeLayout(false);
+ this.gbChannelOptions.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.numCnlGap)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numCnlShift)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numCnlMult)).EndInit();
@@ -404,7 +417,7 @@ private void InitializeComponent()
private System.Windows.Forms.Button btnBrowseTableEditorPath;
private System.Windows.Forms.TextBox txtTableEditorPath;
private System.Windows.Forms.Label lblTableEditorPath;
- private System.Windows.Forms.GroupBox gbCnlNumOptions;
+ private System.Windows.Forms.GroupBox gbChannelOptions;
private System.Windows.Forms.NumericUpDown numCnlMult;
private System.Windows.Forms.Label lblCnlMult;
private System.Windows.Forms.NumericUpDown numCnlShift;
@@ -419,5 +432,6 @@ private void InitializeComponent()
private System.Windows.Forms.Label lblTextEditorPath;
private System.Windows.Forms.OpenFileDialog openFileDialog;
private System.Windows.Forms.FolderBrowserDialog folderBrowserDialog;
+ private System.Windows.Forms.CheckBox chkPrependDeviceName;
}
}
\ No newline at end of file
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmSettings.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmSettings.cs
index 5b0970803..22059bf69 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmSettings.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Forms/Tools/FrmSettings.cs
@@ -24,6 +24,7 @@
*/
using Scada.Admin.App.Code;
+using Scada.Admin.Config;
using Scada.UI;
using System;
using System.IO;
@@ -63,8 +64,8 @@ public FrmSettings(AppData appData)
this.appData = appData ?? throw new ArgumentNullException("appData");
settings = appData.AppSettings;
- serverDir = settings.ServerDir;
- commDir = settings.CommDir;
+ serverDir = settings.PathOptions.ServerDir;
+ commDir = settings.PathOptions.CommDir;
modified = false;
}
@@ -76,7 +77,8 @@ public bool ReopenNeeded
{
get
{
- return serverDir != settings.ServerDir || commDir != settings.CommDir;
+ return serverDir != settings.PathOptions.ServerDir ||
+ commDir != settings.PathOptions.CommDir;
}
}
@@ -86,14 +88,19 @@ public bool ReopenNeeded
///
private void SettingsToControls()
{
- txtServerDir.Text = settings.ServerDir;
- txtCommDir.Text = settings.CommDir;
- txtSchemeEditorPath.Text = settings.SchemeEditorPath;
- txtTableEditorPath.Text = settings.TableEditorPath;
- txtTextEditorPath.Text = settings.TextEditorPath;
- numCnlMult.SetValue(settings.CnlMult);
- numCnlShift.SetValue(settings.CnlShift);
- numCnlGap.SetValue(settings.CnlGap);
+ PathOptions pathOptions = settings.PathOptions;
+ txtServerDir.Text = pathOptions.ServerDir;
+ txtCommDir.Text = pathOptions.CommDir;
+ txtSchemeEditorPath.Text = pathOptions.SchemeEditorPath;
+ txtTableEditorPath.Text = pathOptions.TableEditorPath;
+ txtTextEditorPath.Text = pathOptions.TextEditorPath;
+
+ ChannelOptions channelOptions = settings.ChannelOptions;
+ numCnlMult.SetValue(channelOptions.CnlMult);
+ numCnlShift.SetValue(channelOptions.CnlShift);
+ numCnlGap.SetValue(channelOptions.CnlGap);
+ chkPrependDeviceName.Checked = channelOptions.PrependDeviceName;
+
modified = false;
}
@@ -102,14 +109,18 @@ private void SettingsToControls()
///
private void ControlsToSettings()
{
- settings.ServerDir = txtServerDir.Text;
- settings.CommDir = txtCommDir.Text;
- settings.SchemeEditorPath = txtSchemeEditorPath.Text;
- settings.TableEditorPath = txtTableEditorPath.Text;
- settings.TextEditorPath = txtTextEditorPath.Text;
- settings.CnlMult = Convert.ToInt32(numCnlMult.Value);
- settings.CnlShift = Convert.ToInt32(numCnlShift.Value);
- settings.CnlGap = Convert.ToInt32(numCnlGap.Value);
+ PathOptions pathOptions = settings.PathOptions;
+ pathOptions.ServerDir = txtServerDir.Text;
+ pathOptions.CommDir = txtCommDir.Text;
+ pathOptions.SchemeEditorPath = txtSchemeEditorPath.Text;
+ pathOptions.TableEditorPath = txtTableEditorPath.Text;
+ pathOptions.TextEditorPath = txtTextEditorPath.Text;
+
+ ChannelOptions channelOptions = settings.ChannelOptions;
+ channelOptions.CnlMult = Convert.ToInt32(numCnlMult.Value);
+ channelOptions.CnlShift = Convert.ToInt32(numCnlShift.Value);
+ channelOptions.CnlGap = Convert.ToInt32(numCnlGap.Value);
+ channelOptions.PrependDeviceName = chkPrependDeviceName.Checked;
}
///
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Lang/ScadaAdmin.en-GB.xml b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Lang/ScadaAdmin.en-GB.xml
index 355e8adcb..c13e3312f 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Lang/ScadaAdmin.en-GB.xml
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Lang/ScadaAdmin.en-GB.xml
@@ -278,6 +278,7 @@
Error uploading configuration
+ Properties
Close
Move First
Move Previous
@@ -295,6 +296,7 @@
Cut (Ctrl+X)
Copy (Ctrl+C)
Paste (Ctrl+V)
+ Filter
Find and Replace (Ctrl+F)
Autofit Column Widths
Item Properties
@@ -333,6 +335,20 @@
OK
Cancel
+
+ Filter
+ Column
+ Operation
+ Equals
+ Contains
+ Value
+ False
+ True
+ Clear Filter
+ OK
+ Cancel
+ Incorrect table filter.
+
Find and Replace
Column
@@ -513,10 +529,11 @@
Browse...
Text editor
Browse...
- Channel Numbering
+ Channel Generation
Multiplicity
Shift
Gap
+ Prepend device name
OK
Cancel
Executable files (*.exe)|*.exe|All Files (*.*)|*.*
@@ -681,12 +698,15 @@
Project Properties
Project name
+ Version
Description
OK
Cancel
Start Page
+ Remove From List
+ Copy Path
Recent Projects
No recent projects
New Project
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Lang/ScadaAdmin.ru-RU.xml b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Lang/ScadaAdmin.ru-RU.xml
index b2d6d06f4..7495c71fc 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Lang/ScadaAdmin.ru-RU.xml
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Lang/ScadaAdmin.ru-RU.xml
@@ -278,6 +278,7 @@
Ошибка при передачи конфигурации
+ Свойства
Закрыть
Переместиться в начало
Переместиться на предыдущую строку
@@ -296,6 +297,7 @@
Копировать (Ctrl+C)
Вставить (Ctrl+V)
Найти и заменить (Ctrl+F)
+ Фильтр
Автоподбор ширины столбцов
Свойства элемента
Ошибка при отображении таблицы, столбец "{0}"
@@ -333,6 +335,20 @@
OK
Отмена
+
+ Фильтр
+ Столбец
+ Операция
+ Равен
+ Содержит
+ Значение
+ Нет
+ Да
+ Очистить
+ OK
+ Отмена
+ Некорректный фильтр таблицы.
+
Найти и заменить
Столбец
@@ -412,7 +428,7 @@
Начальный
Конечный
Сброс
- Выходные каналы
+ Каналы управления
Начальный
Конечный
Сброс
@@ -513,10 +529,11 @@
Обзор...
Текстовый редактор
Обзор...
- Нумерация каналов
+ Генерация каналов
Кратность
Смещение
Промежуток
+ Добавлять наименование КП
OK
Отмена
Исполняемые файлы (*.exe)|*.exe|Все файлы (*.*)|*.*
@@ -681,12 +698,15 @@
Свойства проекта
Наименование проекта
+ Версия
Описание
OK
Отмена
Стартовая страница
+ Удалить из списка
+ Копировать путь
Недавние проекты
Недавние проекты отсутствуют
Новый проект
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Properties/Resources.Designer.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Properties/Resources.Designer.cs
index 15b623037..57863d4df 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Properties/Resources.Designer.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Properties/Resources.Designer.cs
@@ -120,6 +120,26 @@ internal static System.Drawing.Bitmap file {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap filter {
+ get {
+ object obj = ResourceManager.GetObject("filter", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap filter_set {
+ get {
+ object obj = ResourceManager.GetObject("filter_set", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Properties/Resources.resx b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Properties/Resources.resx
index 6123fd171..afe2760a6 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Properties/Resources.resx
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Properties/Resources.resx
@@ -136,6 +136,12 @@
..\Resources\file.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\filter.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\filter_set.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
..\Resources\folder_closed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Resources/filter.png b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Resources/filter.png
new file mode 100644
index 000000000..940f8a384
Binary files /dev/null and b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Resources/filter.png differ
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Resources/filter_set.png b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Resources/filter_set.png
new file mode 100644
index 000000000..09d3fc8a2
Binary files /dev/null and b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/Resources/filter_set.png differ
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/ScadaAdmin.csproj b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/ScadaAdmin.csproj
index b7b8977fa..e05be36e0 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdmin/ScadaAdmin.csproj
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdmin/ScadaAdmin.csproj
@@ -12,6 +12,7 @@
512
true
false
+
publish\
true
Disk
@@ -26,7 +27,6 @@
1.0.0.%2a
false
true
-
AnyCPU
@@ -97,6 +97,7 @@
+
@@ -210,6 +211,12 @@
FrmColorSelect.cs
+
+ Form
+
+
+ FrmFilter.cs
+
Form
@@ -380,6 +387,9 @@
FrmColorSelect.cs
+
+ FrmFilter.cs
+
FrmFind.cs
@@ -514,6 +524,8 @@
PreserveNewest
Designer
+
+
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/AdminSettings.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/AdminSettings.cs
deleted file mode 100644
index 88aba1f3c..000000000
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/AdminSettings.cs
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright 2019 Mikhail Shiryaev
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- * Product : Rapid SCADA
- * Module : ScadaAdminCommon
- * Summary : Administrator settings
- *
- * Author : Mikhail Shiryaev
- * Created : 2018
- * Modified : 2019
- */
-
-using System;
-using System.IO;
-using System.Xml;
-
-namespace Scada.Admin
-{
- ///
- /// Administrator settings.
- /// Настройки Администратора.
- ///
- public class AdminSettings
- {
- ///
- /// The default settings file name.
- ///
- public const string DefFileName = "ScadaAdminConfig.xml";
-
-
- ///
- /// Initializes a new instance of the class.
- ///
- public AdminSettings()
- {
- SetToDefault();
- }
-
-
- ///
- /// Gets or sets the directory of the Server application.
- ///
- public string ServerDir { get; set; }
-
- ///
- /// Gets or sets the directory of the Communicator application.
- ///
- public string CommDir { get; set; }
-
- ///
- /// Gets or sets the full file name of a scheme editor.
- ///
- public string SchemeEditorPath { get; set; }
-
- ///
- /// Gets or sets the full file name of a table editor.
- ///
- public string TableEditorPath { get; set; }
-
- ///
- /// Gets or sets the full file name of a text editor.
- ///
- public string TextEditorPath { get; set; }
-
- ///
- /// Gets or sets the multiplicity of the first channel of a device.
- ///
- public int CnlMult { get; set; }
-
- ///
- /// Gets or sets the shift of the first channel of a device.
- ///
- public int CnlShift { get; set; }
-
- ///
- /// Gets or sets the gap between channel numbers of different devices.
- ///
- public int CnlGap { get; set; }
-
-
- ///
- /// Sets the default values.
- ///
- private void SetToDefault()
- {
- if (ScadaUtils.IsRunningOnWin)
- {
- ServerDir = @"C:\SCADA\ScadaServer\";
- CommDir = @"C:\SCADA\ScadaComm\";
- SchemeEditorPath = @"C:\SCADA\ScadaSchemeEditor\ScadaSchemeEditor.exe";
- TableEditorPath = @"C:\SCADA\ScadaTableEditor\ScadaTableEditor.exe";
- TextEditorPath = "";
- }
- else
- {
- ServerDir = "/opt/scada/ScadaServer/";
- CommDir = "/opt/scada/ScadaComm/";
- SchemeEditorPath = "/opt/scada/ScadaSchemeEditor/ScadaSchemeEditor.exe";
- TableEditorPath = "/opt/scada/ScadaTableEditor/ScadaTableEditor.exe";
- TextEditorPath = "";
- }
-
- CnlMult = 100;
- CnlShift = 1;
- CnlGap = 10;
- }
-
- ///
- /// Loads the settings from the specified file.
- ///
- public bool Load(string fileName, out string errMsg)
- {
- try
- {
- SetToDefault();
-
- if (!File.Exists(fileName))
- throw new FileNotFoundException(string.Format(CommonPhrases.NamedFileNotFound, fileName));
-
- XmlDocument xmlDoc = new XmlDocument();
- xmlDoc.Load(fileName);
- XmlElement rootElem = xmlDoc.DocumentElement;
-
- // load path options
- if (rootElem.SelectSingleNode("PathOptions") is XmlNode pathOptionsNode)
- {
- foreach (XmlElement paramElem in pathOptionsNode)
- {
- string name = paramElem.GetAttribute("name");
- string nameL = name.ToLowerInvariant();
- string val = paramElem.GetAttribute("value");
-
- if (nameL == "serverdir")
- ServerDir = ScadaUtils.NormalDir(val);
- else if (nameL == "commdir")
- CommDir = ScadaUtils.NormalDir(val);
- else if (nameL == "schemeeditorpath")
- SchemeEditorPath = val;
- else if (nameL == "tableeditorpath")
- TableEditorPath = val;
- else if (nameL == "texteditorpath")
- TextEditorPath = val;
- }
- }
-
- // load channel numbering options
- if (rootElem.SelectSingleNode("CnlNumOptions") is XmlNode cnlNumOptionsNode)
- {
- foreach (XmlElement paramElem in cnlNumOptionsNode)
- {
- string name = paramElem.GetAttribute("name");
- string nameL = name.ToLowerInvariant();
- string val = paramElem.GetAttribute("value");
-
- try
- {
- if (nameL == "cnlmult")
- CnlMult = int.Parse(val);
- else if (nameL == "cnlshift")
- CnlShift = int.Parse(val);
- else if (nameL == "cnlgap")
- CnlGap = int.Parse(val);
- }
- catch
- {
- throw new Exception(string.Format(CommonPhrases.IncorrectXmlParamVal, name));
- }
- }
- }
-
- errMsg = "";
- return true;
- }
- catch (Exception ex)
- {
- errMsg = CommonPhrases.LoadAppSettingsError + ": " + ex.Message;
- return false;
- }
- }
-
- ///
- /// Saves the settings to the specified file.
- ///
- public bool Save(string fileName, out string errMsg)
- {
- try
- {
- XmlDocument xmlDoc = new XmlDocument();
- XmlDeclaration xmlDecl = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
- xmlDoc.AppendChild(xmlDecl);
-
- XmlElement rootElem = xmlDoc.CreateElement("ScadaAdminConfig");
- xmlDoc.AppendChild(rootElem);
-
- // save path options
- XmlElement pathOptionsElem = rootElem.AppendElem("PathOptions");
- pathOptionsElem.AppendParamElem("ServerDir", ServerDir,
- "Директория Сервера",
- "Server directory");
- pathOptionsElem.AppendParamElem("CommDir", CommDir,
- "Директория Коммуникатора",
- "Communicator directory");
- pathOptionsElem.AppendParamElem("SchemeEditorPath", SchemeEditorPath,
- "Полное имя файла редактора схем",
- "Full file name of a scheme editor");
- pathOptionsElem.AppendParamElem("TableEditorPath", TableEditorPath,
- "Полное имя файла редактора таблиц",
- "Full file name of a table editor");
- pathOptionsElem.AppendParamElem("TextEditorPath", TextEditorPath,
- "Полное имя файла текстового редактора",
- "Full file name of a text editor");
-
- // save channel numbering options
- XmlElement cnlNumOptionsElem = rootElem.AppendElem("CnlNumOptions");
- cnlNumOptionsElem.AppendParamElem("CnlMult", CnlMult,
- "Кратность первого канала устройства",
- "Multiplicity of the first channel of a device");
- cnlNumOptionsElem.AppendParamElem("CnlShift", CnlShift,
- "Смещение первого канала устройства",
- "Shift of the first channel of a device");
- cnlNumOptionsElem.AppendParamElem("CnlGap", CnlGap,
- "Промежуток между номерами каналов разных устройств",
- "Gap between channel numbers of different devices");
-
- xmlDoc.Save(fileName);
- errMsg = "";
- return true;
- }
- catch (Exception ex)
- {
- errMsg = CommonPhrases.SaveAppSettingsError + ": " + ex.Message;
- return false;
- }
- }
- }
-}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/AdminUtils.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/AdminUtils.cs
index 9e71d0087..ce69bcf8f 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/AdminUtils.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/AdminUtils.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
*
* Author : Mikhail Shiryaev
* Created : 2018
- * Modified : 2018
+ * Modified : 2019
*/
using System;
@@ -38,7 +38,7 @@ public static class AdminUtils
///
/// Version of Administrator.
///
- public const string AppVersion = "5.5.0.0";
+ public const string AppVersion = "5.5.1.0";
///
/// Extension of a project file.
///
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/AdminSettings.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/AdminSettings.cs
new file mode 100644
index 000000000..92a33bab9
--- /dev/null
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/AdminSettings.cs
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : ScadaAdminCommon
+ * Summary : Administrator settings
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2018
+ * Modified : 2019
+ */
+
+using System;
+using System.IO;
+using System.Xml;
+
+namespace Scada.Admin.Config
+{
+ ///
+ /// Administrator settings.
+ /// Настройки Администратора.
+ ///
+ public class AdminSettings
+ {
+ ///
+ /// The default settings file name.
+ ///
+ public const string DefFileName = "ScadaAdminConfig.xml";
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AdminSettings()
+ {
+ SetToDefault();
+ }
+
+
+ ///
+ /// Gets the external application path options.
+ ///
+ public PathOptions PathOptions { get; private set; }
+
+ ///
+ /// Gets the channel generation options.
+ ///
+ public ChannelOptions ChannelOptions { get; private set; }
+
+
+ ///
+ /// Sets the default values.
+ ///
+ private void SetToDefault()
+ {
+ PathOptions = new PathOptions();
+ ChannelOptions = new ChannelOptions();
+ }
+
+ ///
+ /// Loads the settings from the specified file.
+ ///
+ public bool Load(string fileName, out string errMsg)
+ {
+ try
+ {
+ SetToDefault();
+
+ if (!File.Exists(fileName))
+ throw new FileNotFoundException(string.Format(CommonPhrases.NamedFileNotFound, fileName));
+
+ XmlDocument xmlDoc = new XmlDocument();
+ xmlDoc.Load(fileName);
+ XmlElement rootElem = xmlDoc.DocumentElement;
+
+ if (rootElem.SelectSingleNode("PathOptions") is XmlNode pathOptionsNode)
+ PathOptions.LoadFromXml(pathOptionsNode);
+
+ if (rootElem.SelectSingleNode("ChannelOptions") is XmlNode channelOptionsNode)
+ ChannelOptions.LoadFromXml(channelOptionsNode);
+
+ errMsg = "";
+ return true;
+ }
+ catch (Exception ex)
+ {
+ errMsg = CommonPhrases.LoadAppSettingsError + ": " + ex.Message;
+ return false;
+ }
+ }
+
+ ///
+ /// Saves the settings to the specified file.
+ ///
+ public bool Save(string fileName, out string errMsg)
+ {
+ try
+ {
+ XmlDocument xmlDoc = new XmlDocument();
+ XmlDeclaration xmlDecl = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
+ xmlDoc.AppendChild(xmlDecl);
+
+ XmlElement rootElem = xmlDoc.CreateElement("ScadaAdminConfig");
+ xmlDoc.AppendChild(rootElem);
+
+ PathOptions.SaveToXml(rootElem.AppendElem("PathOptions"));
+ ChannelOptions.SaveToXml(rootElem.AppendElem("ChannelOptions"));
+
+ xmlDoc.Save(fileName);
+ errMsg = "";
+ return true;
+ }
+ catch (Exception ex)
+ {
+ errMsg = CommonPhrases.SaveAppSettingsError + ": " + ex.Message;
+ return false;
+ }
+ }
+ }
+}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/ChannelOptions.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/ChannelOptions.cs
new file mode 100644
index 000000000..af6564b0b
--- /dev/null
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/ChannelOptions.cs
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : ScadaAdminCommon
+ * Summary : Represents the channel generation options
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+using System.Xml;
+
+namespace Scada.Admin.Config
+{
+ ///
+ /// Represents the channel generation options.
+ /// Представляет параметры генерации каналов.
+ ///
+ public class ChannelOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ChannelOptions()
+ {
+ CnlMult = 100;
+ CnlShift = 1;
+ CnlGap = 10;
+ PrependDeviceName = true;
+ }
+
+
+ ///
+ /// Gets or sets the multiplicity of the first channel of a device.
+ ///
+ public int CnlMult { get; set; }
+
+ ///
+ /// Gets or sets the shift of the first channel of a device.
+ ///
+ public int CnlShift { get; set; }
+
+ ///
+ /// Gets or sets the gap between channel numbers of different devices.
+ ///
+ public int CnlGap { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to prepend a device name in channel names.
+ ///
+ public bool PrependDeviceName { get; set; }
+
+
+ ///
+ /// Loads the options from the XML node.
+ ///
+ public void LoadFromXml(XmlNode xmlNode)
+ {
+ if (xmlNode == null)
+ throw new ArgumentNullException("xmlNode");
+
+ foreach (XmlElement paramElem in xmlNode)
+ {
+ string name = paramElem.GetAttribute("name");
+ string nameL = name.ToLowerInvariant();
+ string val = paramElem.GetAttribute("value");
+
+ try
+ {
+ if (nameL == "cnlmult")
+ CnlMult = int.Parse(val);
+ else if (nameL == "cnlshift")
+ CnlShift = int.Parse(val);
+ else if (nameL == "cnlgap")
+ CnlGap = int.Parse(val);
+ else if (nameL == "prependdevicename")
+ PrependDeviceName = bool.Parse(val);
+ }
+ catch
+ {
+ throw new Exception(string.Format(CommonPhrases.IncorrectXmlParamVal, name));
+ }
+ }
+ }
+
+ ///
+ /// Saves the options into the XML node.
+ ///
+ public void SaveToXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ xmlElem.AppendParamElem("CnlMult", CnlMult,
+ "Кратность первого канала устройства",
+ "Multiplicity of the first channel of a device");
+ xmlElem.AppendParamElem("CnlShift", CnlShift,
+ "Смещение первого канала устройства",
+ "Shift of the first channel of a device");
+ xmlElem.AppendParamElem("CnlGap", CnlGap,
+ "Промежуток между номерами каналов разных устройств",
+ "Gap between channel numbers of different devices");
+ xmlElem.AppendParamElem("PrependDeviceName", PrependDeviceName,
+ "Добавлять наименование КП в имена каналов",
+ "To prepend a device name in channel names");
+ }
+ }
+}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/PathOptions.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/PathOptions.cs
new file mode 100644
index 000000000..fabe5df54
--- /dev/null
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Config/PathOptions.cs
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : ScadaAdminCommon
+ * Summary : Represents the location options of external applications
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+using System.Xml;
+
+namespace Scada.Admin.Config
+{
+ ///
+ /// Represents the location options of external applications.
+ /// Представляет параметры расположения внешних приложений.
+ ///
+ public class PathOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PathOptions()
+ {
+ if (ScadaUtils.IsRunningOnWin)
+ {
+ ServerDir = @"C:\SCADA\ScadaServer\";
+ CommDir = @"C:\SCADA\ScadaComm\";
+ SchemeEditorPath = @"C:\SCADA\ScadaSchemeEditor\ScadaSchemeEditor.exe";
+ TableEditorPath = @"C:\SCADA\ScadaTableEditor\ScadaTableEditor.exe";
+ TextEditorPath = "";
+ }
+ else
+ {
+ ServerDir = "/opt/scada/ScadaServer/";
+ CommDir = "/opt/scada/ScadaComm/";
+ SchemeEditorPath = "/opt/scada/ScadaSchemeEditor/ScadaSchemeEditor.exe";
+ TableEditorPath = "/opt/scada/ScadaTableEditor/ScadaTableEditor.exe";
+ TextEditorPath = "";
+ }
+ }
+
+
+ ///
+ /// Gets or sets the directory of the Server application.
+ ///
+ public string ServerDir { get; set; }
+
+ ///
+ /// Gets or sets the directory of the Communicator application.
+ ///
+ public string CommDir { get; set; }
+
+ ///
+ /// Gets or sets the full file name of a scheme editor.
+ ///
+ public string SchemeEditorPath { get; set; }
+
+ ///
+ /// Gets or sets the full file name of a table editor.
+ ///
+ public string TableEditorPath { get; set; }
+
+ ///
+ /// Gets or sets the full file name of a text editor.
+ ///
+ public string TextEditorPath { get; set; }
+
+
+ ///
+ /// Loads the options from the XML node.
+ ///
+ public void LoadFromXml(XmlNode xmlNode)
+ {
+ if (xmlNode == null)
+ throw new ArgumentNullException("xmlNode");
+
+ foreach (XmlElement paramElem in xmlNode)
+ {
+ string name = paramElem.GetAttribute("name");
+ string nameL = name.ToLowerInvariant();
+ string val = paramElem.GetAttribute("value");
+
+ if (nameL == "serverdir")
+ ServerDir = ScadaUtils.NormalDir(val);
+ else if (nameL == "commdir")
+ CommDir = ScadaUtils.NormalDir(val);
+ else if (nameL == "schemeeditorpath")
+ SchemeEditorPath = val;
+ else if (nameL == "tableeditorpath")
+ TableEditorPath = val;
+ else if (nameL == "texteditorpath")
+ TextEditorPath = val;
+ }
+ }
+
+ ///
+ /// Saves the options into the XML node.
+ ///
+ public void SaveToXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ xmlElem.AppendParamElem("ServerDir", ServerDir,
+ "Директория Сервера",
+ "Server directory");
+ xmlElem.AppendParamElem("CommDir", CommDir,
+ "Директория Коммуникатора",
+ "Communicator directory");
+ xmlElem.AppendParamElem("SchemeEditorPath", SchemeEditorPath,
+ "Полное имя файла редактора схем",
+ "Full file name of a scheme editor");
+ xmlElem.AppendParamElem("TableEditorPath", TableEditorPath,
+ "Полное имя файла редактора таблиц",
+ "Full file name of a table editor");
+ xmlElem.AppendParamElem("TextEditorPath", TextEditorPath,
+ "Полное имя файла текстового редактора",
+ "Full file name of a text editor");
+ }
+ }
+}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Deployment/DeploymentProfile.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Deployment/DeploymentProfile.cs
index aef3e3eed..5674126df 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Deployment/DeploymentProfile.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Deployment/DeploymentProfile.cs
@@ -40,6 +40,7 @@ public class DeploymentProfile
///
public DeploymentProfile()
{
+ InstanceID = 0;
Name = "";
WebUrl = "";
ConnectionSettings = new ConnectionSettings() { ScadaInstance = "" } ;
@@ -48,6 +49,11 @@ public DeploymentProfile()
}
+ ///
+ /// Gets or set the reference to the instance to which the profile belongs.
+ ///
+ public int InstanceID { get; set; }
+
///
/// Gets or sets the profile name.
///
@@ -82,6 +88,7 @@ public void LoadFromXml(XmlNode xmlNode)
if (xmlNode == null)
throw new ArgumentNullException("xmlNode");
+ InstanceID = xmlNode.GetChildAsInt("InstanceID");
Name = xmlNode.GetChildAsString("Name");
WebUrl = xmlNode.GetChildAsString("WebUrl");
@@ -103,6 +110,7 @@ public void SaveToXml(XmlElement xmlElem)
if (xmlElem == null)
throw new ArgumentNullException("xmlElem");
+ xmlElem.AppendElem("InstanceID", InstanceID);
xmlElem.AppendElem("Name", Name);
xmlElem.AppendElem("WebUrl", WebUrl);
ConnectionSettings.SaveToXml(xmlElem.AppendElem("ConnectionSettings"));
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Deployment/DeploymentSettings.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Deployment/DeploymentSettings.cs
index d42a55524..84303a782 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Deployment/DeploymentSettings.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Deployment/DeploymentSettings.cs
@@ -165,5 +165,22 @@ public HashSet GetExistingProfileNames(string exceptName = null)
return existingNames;
}
+
+ ///
+ /// Removes profiles belong to the specified instance.
+ ///
+ public void RemoveProfilesByInstance(int instanceID, out bool profilesAffected)
+ {
+ profilesAffected = false;
+
+ for (int i = Profiles.Count - 1; i >= 0; i--)
+ {
+ if (Profiles.Values[i].InstanceID == instanceID)
+ {
+ Profiles.RemoveAt(i);
+ profilesAffected = true;
+ }
+ }
+ }
}
}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/ImportExport.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/ImportExport.cs
index c4d4de34d..9de54614b 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/ImportExport.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/ImportExport.cs
@@ -34,6 +34,7 @@
using System.Data;
using System.IO;
using System.IO.Compression;
+using System.Text;
namespace Scada.Admin
{
@@ -43,6 +44,12 @@ namespace Scada.Admin
///
public class ImportExport
{
+ ///
+ /// The name of the archive entry that contains project information.
+ ///
+ private const string ProjectInfoEntryName = "Project.txt";
+
+
///
/// Extracts the specified archive.
///
@@ -386,6 +393,16 @@ public void ExportToArchive(string destFileName, ScadaProject project, Instance
DirectoryBuilder.GetDirectory(ConfigParts.Web, AppFolder.Storage, '/'), ignoreRegKeys);
}
}
+
+ // add an information entry to the archive
+ using (Stream entryStream =
+ zipArchive.CreateEntry(ProjectInfoEntryName, CompressionLevel.Fastest).Open())
+ {
+ using (StreamWriter writer = new StreamWriter(entryStream, Encoding.UTF8))
+ {
+ writer.Write(project.GetInfo());
+ }
+ }
}
catch (Exception ex)
{
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/Instance.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/Instance.cs
index 3ad92ec04..7a50b7798 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/Instance.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/Instance.cs
@@ -52,6 +52,7 @@ public class Instance
///
public Instance()
{
+ ID = 0;
Name = "";
ServerApp = new ServerApp();
CommApp = new CommApp();
@@ -62,6 +63,11 @@ public Instance()
}
+ ///
+ /// Gets or sets the instance identifier.
+ ///
+ public int ID { get; set; }
+
///
/// Gets or sets the name of the instance.
///
@@ -129,6 +135,7 @@ public void LoadFromXml(XmlNode xmlNode)
if (xmlNode == null)
throw new ArgumentNullException("xmlNode");
+ ID = xmlNode.GetChildAsInt("ID");
Name = xmlNode.GetChildAsString("Name");
if (xmlNode.SelectSingleNode("ServerApp") is XmlElement serverAppElem)
@@ -151,6 +158,7 @@ public void SaveToXml(XmlElement xmlElem)
if (xmlElem == null)
throw new ArgumentNullException("xmlElem");
+ xmlElem.AppendElem("ID", ID);
xmlElem.AppendElem("Name", Name);
ServerApp.SaveToXml(xmlElem.AppendElem("ServerApp"));
CommApp.SaveToXml(xmlElem.AppendElem("CommApp"));
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/ProjectVersion.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/ProjectVersion.cs
new file mode 100644
index 000000000..440529c46
--- /dev/null
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/ProjectVersion.cs
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : ScadaAdminCommon
+ * Summary : Represents a project version
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+namespace Scada.Admin.Project
+{
+ ///
+ /// Represents a project version.
+ /// Представляет версию проекта.
+ ///
+ public struct ProjectVersion
+ {
+ ///
+ /// Initializes a new instance of the structure.
+ ///
+ public ProjectVersion(int major, int minor)
+ {
+ Major = major;
+ Minor = minor;
+ }
+
+
+ ///
+ /// Gets or sets the major version.
+ ///
+ public int Major { get; set; }
+
+ ///
+ /// Gets or sets the minor version.
+ ///
+ public int Minor { get; set; }
+
+
+ ///
+ /// Returns a string that represents the current object.
+ ///
+ public override string ToString()
+ {
+ return Major + "." + Minor;
+ }
+
+ ///
+ /// Converts the string representation to a version.
+ ///
+ public static ProjectVersion Parse(string s)
+ {
+ string[] parts = (s ?? "").Split('.');
+ return new ProjectVersion(
+ parts.Length > 0 && int.TryParse(parts[0], out int n) ? n : 0,
+ parts.Length > 1 && int.TryParse(parts[1], out n) ? n : 0);
+ }
+ }
+}
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/ScadaProject.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/ScadaProject.cs
index 76dc83664..f8d2b7b99 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/ScadaProject.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Project/ScadaProject.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,13 +20,14 @@
*
* Author : Mikhail Shiryaev
* Created : 2018
- * Modified : 2018
+ * Modified : 2019
*/
using Scada.Admin.Deployment;
using System;
using System.Collections.Generic;
using System.IO;
+using System.Text;
using System.Xml;
namespace Scada.Admin.Project
@@ -114,6 +115,11 @@ public string ProjectDir
}
}
+ ///
+ /// Gets or sets the project version.
+ ///
+ public ProjectVersion Version { get; set; }
+
///
/// Gets or sets the project description.
///
@@ -148,6 +154,7 @@ private void SetToDefault()
fileName = "";
Name = DefaultName;
+ Version = new ProjectVersion(1, 0);
Description = "";
ConfigBase = new ConfigBase();
Interface = new Interface();
@@ -197,6 +204,22 @@ private static void CopyDirectory(DirectoryInfo source, DirectoryInfo dest)
}
}
+ ///
+ /// Gets the maximum ID of existing instances.
+ ///
+ private int GetMaxInstanceID()
+ {
+ int maxID = 0;
+
+ foreach (Instance instance in Instances)
+ {
+ if (instance.ID > maxID)
+ maxID = instance.ID;
+ }
+
+ return maxID;
+ }
+
///
/// Loads the project from the specified file.
@@ -210,6 +233,7 @@ public void Load(string fileName)
xmlDoc.Load(fileName);
XmlElement rootElem = xmlDoc.DocumentElement;
+ Version = ProjectVersion.Parse(rootElem.GetChildAsString("ProjectVersion"));
Description = rootElem.GetChildAsString("Description");
// load instances
@@ -226,6 +250,10 @@ public void Load(string fileName)
instance.LoadFromXml(instanceNode);
instance.InstanceDir = Path.Combine(projectDir, "Instances", instance.Name);
Instances.Add(instance);
+
+ // fix instance ID
+ if (instance.ID <= 0)
+ instance.ID = Instances.Count;
}
}
}
@@ -260,7 +288,8 @@ public void Save(string fileName)
xmlDoc.AppendChild(xmlDecl);
XmlElement rootElem = xmlDoc.CreateElement("ScadaProject");
- rootElem.AppendElem("Version", AdminUtils.AppVersion);
+ rootElem.AppendElem("AdminVersion", AdminUtils.AppVersion);
+ rootElem.AppendElem("ProjectVersion", Version);
rootElem.AppendElem("Description", Description);
xmlDoc.AppendChild(rootElem);
@@ -335,6 +364,7 @@ public Instance CreateInstance(string name)
string projectDir = Path.GetDirectoryName(FileName);
return new Instance()
{
+ ID = GetMaxInstanceID() + 1,
Name = name,
InstanceDir = Path.Combine(projectDir, "Instances", name)
};
@@ -372,6 +402,31 @@ public HashSet GetInstanceNames(bool lowerCase, string exceptName = null
return instanceNames;
}
+ ///
+ /// Gets the project info.
+ ///
+ public string GetInfo()
+ {
+ StringBuilder sbInfo = new StringBuilder();
+
+ if (Localization.UseRussian)
+ {
+ sbInfo
+ .Append("Наименование проекта : ").AppendLine(Name)
+ .Append("Версия проекта : ").AppendLine(Version.ToString())
+ .Append("Метка времени : ").AppendLine(DateTime.Now.ToLocalizedString());
+ }
+ else
+ {
+ sbInfo
+ .Append("Project name : ").AppendLine(Name)
+ .Append("Project version : ").AppendLine(Version.ToString())
+ .Append("Timestamp : ").AppendLine(DateTime.Now.ToLocalizedString());
+ }
+
+ return sbInfo.ToString();
+ }
+
///
/// Creates a new project with the specified parameters.
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Properties/AssemblyInfo.cs b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Properties/AssemblyInfo.cs
index 3f45642d3..3f030811f 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Properties/AssemblyInfo.cs
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/Properties/AssemblyInfo.cs
@@ -10,7 +10,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Rapid SCADA")]
-[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyCopyright("Copyright © 2018-2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
diff --git a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/ScadaAdminCommon.csproj b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/ScadaAdminCommon.csproj
index 7dbce849a..2b62d929d 100644
--- a/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/ScadaAdminCommon.csproj
+++ b/ScadaAdmin/ScadaAdmin5/ScadaAdminCommon/ScadaAdminCommon.csproj
@@ -65,10 +65,12 @@
-
+
+
+
@@ -78,6 +80,7 @@
+
diff --git a/ScadaAgent/ScadaAgentCommon/AgentUtils.cs b/ScadaAgent/ScadaAgentCommon/AgentUtils.cs
index 3cffb88f8..049570681 100644
--- a/ScadaAgent/ScadaAgentCommon/AgentUtils.cs
+++ b/ScadaAgent/ScadaAgentCommon/AgentUtils.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,20 +20,20 @@
*
* Author : Mikhail Shiryaev
* Created : 2018
- * Modified : 2018
+ * Modified : 2019
*/
namespace Scada.Agent
{
///
- /// The class contains utility methods for Agent
- /// Класс, содержащий вспомогательные методы для Агента
+ /// The class contains utility methods for Agent.
+ /// Класс, содержащий вспомогательные методы для Агента.
///
public static class AgentUtils
{
///
- /// Версия Агента
+ /// Версия Агента.
///
- public const string AppVersion = "5.0.1.0";
+ public const string AppVersion = "5.0.2.0";
}
}
diff --git a/ScadaAgent/ScadaAgentCommon/ScadaAgentCommon.xml b/ScadaAgent/ScadaAgentCommon/ScadaAgentCommon.xml
index e26446926..5d87a8e89 100644
--- a/ScadaAgent/ScadaAgentCommon/ScadaAgentCommon.xml
+++ b/ScadaAgent/ScadaAgentCommon/ScadaAgentCommon.xml
@@ -6,13 +6,13 @@
- The class contains utility methods for Agent
- Класс, содержащий вспомогательные методы для Агента
+ The class contains utility methods for Agent.
+ Класс, содержащий вспомогательные методы для Агента.
- Версия Агента
+ Версия Агента.
diff --git a/ScadaAgent/ScadaAgentEngine/AgentLogic.cs b/ScadaAgent/ScadaAgentEngine/AgentLogic.cs
index 7599d47be..5eaf5d87a 100644
--- a/ScadaAgent/ScadaAgentEngine/AgentLogic.cs
+++ b/ScadaAgent/ScadaAgentEngine/AgentLogic.cs
@@ -81,7 +81,7 @@ private enum WorkState
private SessionManager sessionManager; // ссылка на менджер сессий
private AppDirs appDirs; // директории приложения
private ILog log; // журнал приложения
- private Thread thread; // поток работы сервера
+ private Thread thread; // поток работы агента
private volatile bool terminated; // необходимо завершить работу потока
private string infoFileName; // полное имя файла информации
private DateTime utcStartDT; // дата и время запуска (UTC)
@@ -325,12 +325,15 @@ public bool StartProcessing()
"Обработка логики уже запущена" :
"Logic processing is already started");
}
+
+ return true;
}
catch (Exception ex)
{
log.WriteException(ex, Localization.UseRussian ?
"Ошибка при запуске обработки логики" :
"Error starting logic processing");
+ return false;
}
finally
{
@@ -340,8 +343,6 @@ public bool StartProcessing()
WriteInfo();
}
}
-
- return true;
}
///
diff --git a/ScadaAgent/ScadaAgentEngine/ScadaAgentEngine.xml b/ScadaAgent/ScadaAgentEngine/ScadaAgentEngine.xml
index 6416ebff7..020c17870 100644
--- a/ScadaAgent/ScadaAgentEngine/ScadaAgentEngine.xml
+++ b/ScadaAgent/ScadaAgentEngine/ScadaAgentEngine.xml
@@ -267,8 +267,8 @@
- Object for manipulating a system instance
- Объект для манипуляций с экземпляром системы
+ Object for manipulating a system instance.
+ Объект для манипуляций с экземпляром системы.
@@ -306,6 +306,11 @@
Макс. количество попыток проверки пользователя
+
+
+ The name of the archive entry that contains project information.
+
+
Все части конфигурации в виде массива
diff --git a/ScadaAgent/ScadaAgentEngine/ScadaInstance.cs b/ScadaAgent/ScadaAgentEngine/ScadaInstance.cs
index 8984f2a40..d3e203a43 100644
--- a/ScadaAgent/ScadaAgentEngine/ScadaInstance.cs
+++ b/ScadaAgent/ScadaAgentEngine/ScadaInstance.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
*
* Author : Mikhail Shiryaev
* Created : 2018
- * Modified : 2018
+ * Modified : 2019
*/
using Scada.Data.Configuration;
@@ -37,8 +37,8 @@
namespace Scada.Agent.Engine
{
///
- /// Object for manipulating a system instance
- /// Объект для манипуляций с экземпляром системы
+ /// Object for manipulating a system instance.
+ /// Объект для манипуляций с экземпляром системы.
///
public class ScadaInstance
{
@@ -101,6 +101,10 @@ public PathList GetOrAdd(ConfigParts configPart, AppFolder appFolder)
///
private const int MaxValidateUserAttempts = 3;
///
+ /// The name of the archive entry that contains project information.
+ ///
+ private const string ProjectInfoEntryName = "Project.txt";
+ ///
/// Все части конфигурации в виде массива
///
private static readonly ConfigParts[] AllConfigParts = { ConfigParts.Base,
@@ -538,7 +542,8 @@ public string GetAbsPath(RelPath relPath)
///
public string GetAbsPath(ConfigParts configPart, AppFolder appFolder, string path)
{
- return Path.Combine(Settings.Directory, DirectoryBuilder.GetDirectory(configPart, appFolder), path);
+ return Path.Combine(Settings.Directory,
+ DirectoryBuilder.GetDirectory(configPart, appFolder, Path.DirectorySeparatorChar), path);
}
///
@@ -608,7 +613,7 @@ public bool UnpackConfig(string srcFileName, ConfigOptions configOptions)
{
try
{
- // удаление существующей конфигурации
+ // delete the existing configuration
List configPaths = GetConfigPaths(configOptions.ConfigParts);
PathDict pathDict = PrepareIgnoredPaths(configOptions.IgnoredPaths);
@@ -617,9 +622,14 @@ public bool UnpackConfig(string srcFileName, ConfigOptions configOptions)
ClearDir(relPath, pathDict);
}
- // определение допустимых директорий для распаковки
+ // delete a project information file
+ string instanceDir = Settings.Directory;
+ string projectInfoFileName = Path.Combine(instanceDir, ProjectInfoEntryName);
+ File.Delete(projectInfoFileName);
+
+ // define allowed directories to unpack
ConfigParts configParts = configOptions.ConfigParts;
- List allowedEntries = new List(AllConfigParts.Length);
+ List allowedEntries = new List { ProjectInfoEntryName };
foreach (ConfigParts configPart in AllConfigParts)
{
@@ -627,14 +637,12 @@ public bool UnpackConfig(string srcFileName, ConfigOptions configOptions)
allowedEntries.Add(DirectoryBuilder.GetDirectory(configPart, '/'));
}
- // распаковка новой конфигурации
+ // unpack the new configuration
using (FileStream fileStream =
new FileStream(srcFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read))
{
- string instanceDir = Settings.Directory;
-
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
if (StartsWith(entry.FullName, allowedEntries, StringComparison.Ordinal))
diff --git a/ScadaAgent/ScadaAgentSvc/ScadaAgentSvc.csproj b/ScadaAgent/ScadaAgentSvc/ScadaAgentSvc.csproj
index f78c7376d..c3a80c208 100644
--- a/ScadaAgent/ScadaAgentSvc/ScadaAgentSvc.csproj
+++ b/ScadaAgent/ScadaAgentSvc/ScadaAgentSvc.csproj
@@ -98,6 +98,9 @@
ProjectInstaller.cs
+
+ SvcMain.cs
+
diff --git a/ScadaAgent/ScadaAgentSvc/SvcMain.Designer.cs b/ScadaAgent/ScadaAgentSvc/SvcMain.Designer.cs
index a0bf3e3ab..623d1937d 100644
--- a/ScadaAgent/ScadaAgentSvc/SvcMain.Designer.cs
+++ b/ScadaAgent/ScadaAgentSvc/SvcMain.Designer.cs
@@ -28,8 +28,11 @@ protected override void Dispose(bool disposing)
///
private void InitializeComponent()
{
- components = new System.ComponentModel.Container();
- this.ServiceName = "Service1";
+ //
+ // SvcMain
+ //
+ this.ServiceName = "ScadaAgentService";
+
}
#endregion
diff --git a/ScadaAgent/ScadaAgentSvc/SvcMain.resx b/ScadaAgent/ScadaAgentSvc/SvcMain.resx
new file mode 100644
index 000000000..e5858cc29
--- /dev/null
+++ b/ScadaAgent/ScadaAgentSvc/SvcMain.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpDbImport/DbImport/UI/FrmConfig.cs b/ScadaComm/OpenKPs/KpDbImport/DbImport/UI/FrmConfig.cs
index 3517ac05f..46e867f7c 100644
--- a/ScadaComm/OpenKPs/KpDbImport/DbImport/UI/FrmConfig.cs
+++ b/ScadaComm/OpenKPs/KpDbImport/DbImport/UI/FrmConfig.cs
@@ -16,7 +16,7 @@
*
* Product : Rapid SCADA
* Module : KpDBImport
- * Summary : Device properties form
+ * Summary : Device configuration form
*
* Author : Mikhail Shiryaev
* Created : 2018
@@ -34,18 +34,18 @@
namespace Scada.Comm.Devices.DbImport.UI
{
///
- /// Device properties form.
- /// Форма настройки свойств КП.
+ /// Device configuration form.
+ /// Форма настройки конфигурации КП.
///
public partial class FrmConfig : Form
{
- private AppDirs appDirs; // the application directories
- private int kpNum; // the device number
- private Config config; // the device configuration
- private string configFileName; // the configuration file name
- private bool modified; // the configuration was modified
- private bool connChanging; // connection settings are changing
- private bool cmdSelecting; // a command is selecting
+ private readonly AppDirs appDirs; // the application directories
+ private readonly int kpNum; // the device number
+ private readonly Config config; // the device configuration
+ private string configFileName; // the configuration file name
+ private bool modified; // the configuration was modified
+ private bool connChanging; // connection settings are changing
+ private bool cmdSelecting; // a command is selecting
@@ -294,7 +294,7 @@ private void FrmConfig_Load(object sender, EventArgs e)
Text = string.Format(Text, kpNum);
- // load configuration
+ // load a configuration
configFileName = Config.GetFileName(appDirs.ConfigDir, kpNum);
if (File.Exists(configFileName) && !config.Load(configFileName, out errMsg))
diff --git a/ScadaComm/OpenKPs/KpEmail/KpEmailLogic.cs b/ScadaComm/OpenKPs/KpEmail/KpEmailLogic.cs
index 0a03057cf..dadd72eb6 100644
--- a/ScadaComm/OpenKPs/KpEmail/KpEmailLogic.cs
+++ b/ScadaComm/OpenKPs/KpEmail/KpEmailLogic.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
*
* Author : Mikhail Shiryaev
* Created : 2016
- * Modified : 2017
+ * Modified : 2019
*
* Description
* Sending email notifications.
@@ -33,6 +33,7 @@
using Scada.Data.Tables;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Net;
using System.Net.Mail;
using System.Threading;
@@ -45,12 +46,12 @@ namespace Scada.Comm.Devices
///
public class KpEmailLogic : KPLogic
{
- private AB.AddressBook addressBook; // адресная книга, общая для линии связи
- private Config config; // конфигурация соединения с почтовым сервером
- private SmtpClient smtpClient; // клиент SMTP
- private bool fatalError; // фатальная ошибка при инициализации КП
- private string state; // состояние КП
- private bool writeState; // вывести состояние КП
+ private AddressBook addressBook; // адресная книга, общая для линии связи
+ private Config config; // конфигурация соединения с почтовым сервером
+ private SmtpClient smtpClient; // клиент SMTP
+ private bool fatalError; // фатальная ошибка при инициализации КП
+ private string state; // состояние КП
+ private bool writeState; // вывести состояние КП
///
@@ -82,8 +83,7 @@ public KpEmailLogic(int number)
///
private void LoadConfig()
{
- string errMsg;
- fatalError = !config.Load(Config.GetFileName(AppDirs.ConfigDir, Number), out errMsg);
+ fatalError = !config.Load(Config.GetFileName(AppDirs.ConfigDir, Number), out string errMsg);
if (fatalError)
{
@@ -115,19 +115,18 @@ private void InitSnmpClient()
///
/// Попытаться получить почтовое сообщение из команды ТУ
///
- private bool TryGetMessage(Command cmd, out MailMessage message)
+ private bool TryGetMessage(Command cmd, bool withAttachments, out MailMessage message)
{
string cmdDataStr = cmd.GetCmdDataStr();
- int ind1 = cmdDataStr.IndexOf(';');
- int ind2 = ind1 >= 0 ? cmdDataStr.IndexOf(';', ind1 + 1) : -1;
+ int scInd1 = cmdDataStr.IndexOf(';');
+ int scInd2 = scInd1 >= 0 ? cmdDataStr.IndexOf(';', scInd1 + 1) : -1;
- if (ind1 >= 0 && ind2 >= 0)
+ if (scInd1 >= 0 && scInd2 >= 0)
{
- string recipient = cmdDataStr.Substring(0, ind1);
- string subject = cmdDataStr.Substring(ind1 + 1, ind2 - ind1 - 1);
- string text = cmdDataStr.Substring(ind2 + 1);
-
+ // get addresses
+ string recipient = cmdDataStr.Substring(0, scInd1);
List addresses = new List();
+
if (addressBook == null)
{
// добавление адреса получателя из данных команды
@@ -136,10 +135,11 @@ private bool TryGetMessage(Command cmd, out MailMessage message)
else
{
// поиск адресов получателей в адресной книге
- AB.AddressBook.ContactGroup contactGroup = addressBook.FindContactGroup(recipient);
+ AddressBook.ContactGroup contactGroup = addressBook.FindContactGroup(recipient);
if (contactGroup == null)
{
- AB.AddressBook.Contact contact = addressBook.FindContact(recipient);
+ AddressBook.Contact contact = addressBook.FindContact(recipient);
+
if (contact == null)
{
// добавление адреса получателя из данных команды
@@ -154,13 +154,46 @@ private bool TryGetMessage(Command cmd, out MailMessage message)
else
{
// добавление адресов получателей из группы контактов
- foreach (AB.AddressBook.Contact contact in contactGroup.Contacts)
+ foreach (AddressBook.Contact contact in contactGroup.Contacts)
+ {
addresses.AddRange(contact.Emails);
+ }
}
}
- // создание сообщения
- message = CreateMessage(addresses, subject, text);
+ // get subject, text and attachments
+ string subject = cmdDataStr.Substring(scInd1 + 1, scInd2 - scInd1 - 1);
+ string text = null;
+ string[] fileNames = null;
+
+ if (withAttachments)
+ {
+ int scInd3 = cmdDataStr.LastIndexOf(';');
+
+ if (scInd2 < scInd3)
+ {
+ text = cmdDataStr.Substring(scInd2 + 1, scInd3 - scInd2 - 1);
+ List fileNameList = new List();
+
+ foreach (string s in cmdDataStr.Substring(scInd3 + 1).Split(','))
+ {
+ string fileName = s.Trim();
+ if (File.Exists(fileName))
+ {
+ fileNameList.Add(fileName);
+ }
+ }
+
+ if (fileNameList.Count > 0)
+ fileNames = fileNameList.ToArray();
+ }
+ }
+
+ if (text == null)
+ text = cmdDataStr.Substring(scInd2 + 1);
+
+ // create message
+ message = CreateMessage(addresses, subject, text, fileNames);
return message != null;
}
else
@@ -173,7 +206,7 @@ private bool TryGetMessage(Command cmd, out MailMessage message)
///
/// Создать почтовое сообщение
///
- private MailMessage CreateMessage(List addresses, string subject, string text)
+ private MailMessage CreateMessage(List addresses, string subject, string text, string[] fileNames)
{
MailMessage message = new MailMessage();
@@ -207,6 +240,15 @@ private MailMessage CreateMessage(List addresses, string subject, string
{
message.Subject = subject;
message.Body = text;
+
+ if (fileNames != null)
+ {
+ foreach (string fileName in fileNames)
+ {
+ message.Attachments.Add(new Attachment(fileName));
+ }
+ }
+
return message;
}
else
@@ -283,10 +325,9 @@ public override void SendCmd(Command cmd)
}
else
{
- if (cmd.CmdNum == 1 && cmd.CmdTypeID == BaseValues.CmdTypes.Binary)
+ if (cmd.CmdNum == 1 || cmd.CmdNum == 2 && cmd.CmdTypeID == BaseValues.CmdTypes.Binary)
{
- MailMessage message;
- if (TryGetMessage(cmd, out message))
+ if (TryGetMessage(cmd, cmd.CmdNum == 2, out MailMessage message))
{
if (SendMessage(message))
lastCommSucc = true;
@@ -322,4 +363,4 @@ public override void OnCommLineStart()
SetCurData(0, 0, 1);
}
}
-}
\ No newline at end of file
+}
diff --git a/ScadaComm/OpenKPs/KpEmail/KpEmailView.cs b/ScadaComm/OpenKPs/KpEmail/KpEmailView.cs
index 568c1934b..5259a6797 100644
--- a/ScadaComm/OpenKPs/KpEmail/KpEmailView.cs
+++ b/ScadaComm/OpenKPs/KpEmail/KpEmailView.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
*
* Author : Mikhail Shiryaev
* Created : 2016
- * Modified : 2017
+ * Modified : 2019
*/
using Scada.Comm.Devices.AB;
@@ -38,7 +38,7 @@ public class KpEmailView : KPView
///
/// Версия библиотеки КП
///
- internal const string KpVersion = "5.0.0.2";
+ internal const string KpVersion = "5.0.1.0";
///
@@ -69,19 +69,23 @@ public override string KPDescr
return Localization.UseRussian ?
"Отправка уведомлений по электронной почте.\n\n" +
"Команды ТУ:\n" +
- "1 (бинарная) - отправка уведомления.\n\n" +
+ "1 (бинарная) - отправка электронного письма;\n" +
+ "2 (бинарная) - отправка электронного письма с вложениями.\n\n" +
"Примеры текста команды:\n" +
- "имя_группы;тема;сообщение\n" +
- "имя_контакта;тема;сообщение\n" +
- "эл_почта;тема;сообщение" :
+ "имя_группы;тема;сообщение;вложения\n" +
+ "имя_контакта;тема;сообщение;вложения\n" +
+ "эл_почта;тема;сообщение;вложения\n" +
+ "вложения - это список путей к файлам, разделенных запятыми.":
"Sending email notifications.\n\n" +
"Commands:\n" +
- "1 (binary) - send the notification.\n\n" +
+ "1 (binary) - send the email;\n" +
+ "2 (binary) - send the email with attachments.\n\n" +
"Command text examples:\n" +
- "group_name;subject;message\n" +
- "contact_name;subject;message\n" +
- "email;subject;message";
+ "group_name;subject;message;attachments\n" +
+ "contact_name;subject;message;attachments\n" +
+ "email;subject;message;attachments\n" +
+ "attachments is a comma-separated list of file paths.";
}
}
diff --git a/ScadaComm/OpenKPs/KpModbus/Modbus/UI/FrmDevTemplate.Designer.cs b/ScadaComm/OpenKPs/KpModbus/Modbus/UI/FrmDevTemplate.Designer.cs
index 80106ead7..701cea159 100644
--- a/ScadaComm/OpenKPs/KpModbus/Modbus/UI/FrmDevTemplate.Designer.cs
+++ b/ScadaComm/OpenKPs/KpModbus/Modbus/UI/FrmDevTemplate.Designer.cs
@@ -73,7 +73,7 @@ private void InitializeComponent()
treeNode1.Name = "grsNode";
treeNode1.SelectedImageKey = "group.png";
treeNode1.Text = "Element groups";
- treeNode2.ImageIndex = 2;
+ treeNode2.ImageIndex = 3;
treeNode2.Name = "cmdsNode";
treeNode2.SelectedImageKey = "cmds.png";
treeNode2.Text = "Commands";
diff --git a/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa.Linux.xml b/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa.Linux.xml
new file mode 100644
index 000000000..4bff9baca
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa.Linux.xml
@@ -0,0 +1,256 @@
+
+
+
+ KpOpcUa Driver
+
+
+ urn:localhost:RapidScada:KpOpcUa
+
+
+ https://rapidscada.org/
+
+
+ Client_1
+
+
+
+
+
+
+ Directory
+ %LocalApplicationData%/OPC Foundation/pki/app
+
+
+ CN=KpOpcUa, O=OPC Foundation, DC=localhost
+
+
+
+
+
+
+
+
+ Directory
+ %LocalApplicationData%/OPC Foundation/pki/issuer
+
+
+
+
+ Directory
+ %LocalApplicationData%/OPC Foundation/pki/trusted
+
+
+
+ 32
+
+
+
+ Directory
+ %LocalApplicationData%/OPC Foundation/pki/rejected
+
+
+
+ true
+ 2048
+
+
+ false
+
+
+
+ Directory
+ %LocalApplicationData%/OPC Foundation/pki/issuerUser
+
+
+
+
+ Directory
+ %LocalApplicationData%/OPC Foundation/pki/trustedUser
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+ 120000
+
+
+ 1048576
+
+
+ 4194304
+
+
+ 65535
+
+
+ 4194304
+
+
+ 65535
+
+
+ 300000
+
+
+ 3600000
+
+
+
+
+
+
+ 600000
+
+
+
+ opc.tcp://{0}:4840/UADiscovery
+ http://{0}:52601/UADiscovery
+ http://{0}/UADiscovery/Default.svc
+
+
+
+
+
+
+ Opc.Ua.SampleClient.Endpoints.xml
+
+
+ 10000
+
+
+
+
+
+
+
+
+ UInt32
+ 100
+ UInt32
+
+
+ Double
+ 100
+ Double
+
+
+
+
+
+
+
+
+
+ %LocalApplicationData%/OPC Foundation/Logs/Scada.Comm.Devices.KpOpcUa.log.txt
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
diff --git a/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa.Win.xml b/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa.Win.xml
new file mode 100644
index 000000000..67118d1d9
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa.Win.xml
@@ -0,0 +1,256 @@
+
+
+
+ KpOpcUa Driver
+
+
+ urn:localhost:RapidScada:KpOpcUa
+
+
+ https://rapidscada.org/
+
+
+ Client_1
+
+
+
+
+
+
+ X509Store
+ CurrentUser\UA_MachineDefault
+
+
+ CN=KpOpcUa, O=OPC Foundation, DC=localhost
+
+
+
+
+
+
+
+
+ Directory
+ %CommonApplicationData%\OPC Foundation\pki\issuer
+
+
+
+
+ Directory
+ %CommonApplicationData%\OPC Foundation\pki\trusted
+
+
+
+ 32
+
+
+
+ Directory
+ %CommonApplicationData%\OPC Foundation\pki\rejected
+
+
+
+ true
+ 2048
+
+
+ false
+
+
+
+ Directory
+ %CommonApplicationData%\OPC Foundation\pki\issuerUser
+
+
+
+
+ Directory
+ %CommonApplicationData%\OPC Foundation\pki\trustedUser
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+ 120000
+
+
+ 1048576
+
+
+ 4194304
+
+
+ 65535
+
+
+ 4194304
+
+
+ 65535
+
+
+ 300000
+
+
+ 3600000
+
+
+
+
+
+
+ 600000
+
+
+
+ opc.tcp://{0}:4840/UADiscovery
+ http://{0}:52601/UADiscovery
+ http://{0}/UADiscovery/Default.svc
+
+
+
+
+
+
+ Opc.Ua.SampleClient.Endpoints.xml
+
+
+ 10000
+
+
+
+
+
+
+
+
+ UInt32
+ 100
+ UInt32
+
+
+ Double
+ 100
+ Double
+
+
+
+
+
+
+
+
+
+ %CommonApplicationData%\OPC Foundation\Logs\Scada.Comm.Devices.KpOpcUa.log.txt
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
diff --git a/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa_001.xml b/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa_001.xml
new file mode 100644
index 000000000..f2e040c54
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/Config/KpOpcUa_001.xml
@@ -0,0 +1,25 @@
+
+
+
+ opc.tcp://HP:53530/OPCUA/SimulationServer
+ SignAndEncrypt
+ Basic128Rsa15
+ Anonymous
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ScadaComm/OpenKPs/KpOpcUa/KpOpcUa.csproj b/ScadaComm/OpenKPs/KpOpcUa/KpOpcUa.csproj
new file mode 100644
index 000000000..c06ef347d
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/KpOpcUa.csproj
@@ -0,0 +1,162 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {205C7DAB-C3ED-4D4F-8F2F-CFE65D6C4044}
+ Library
+ Properties
+ Scada.Comm.Devices
+ KpOpcUa
+ v4.6.1
+ 512
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\BouncyCastle.1.8.4\lib\BouncyCastle.Crypto.dll
+
+
+ ..\..\..\..\scada\Log\Log\bin\Release\Log.dll
+
+
+ ..\packages\OPCFoundation.NetStandard.Opc.Ua.1.4.356.27\lib\net46\Opc.Ua.Client.dll
+
+
+ ..\packages\OPCFoundation.NetStandard.Opc.Ua.1.4.356.27\lib\net46\Opc.Ua.Configuration.dll
+
+
+ ..\packages\OPCFoundation.NetStandard.Opc.Ua.1.4.356.27\lib\net46\Opc.Ua.Core.dll
+
+
+ ..\..\..\..\scada\ScadaComm\ScadaComm\ScadaCommCommon\bin\Release\ScadaCommCommon.dll
+
+
+ False
+ ..\..\..\..\scada\ScadaData\ScadaData\bin\Release\ScadaData.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ CtrlCommand.cs
+
+
+ UserControl
+
+
+ CtrlItem.cs
+
+
+ UserControl
+
+
+ CtrlSubscription.cs
+
+
+ Form
+
+
+ FrmConfig.cs
+
+
+ Form
+
+
+ FrmNodeAttr.cs
+
+
+ Form
+
+
+ FrmSecurityOptions.cs
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+ Designer
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+ CtrlCommand.cs
+
+
+ CtrlItem.cs
+
+
+ CtrlSubscription.cs
+
+
+ FrmConfig.cs
+
+
+ FrmNodeAttr.cs
+
+
+ FrmSecurityOptions.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/KpOpcUaLogic.cs b/ScadaComm/OpenKPs/KpOpcUa/KpOpcUaLogic.cs
new file mode 100644
index 000000000..c11380e87
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/KpOpcUaLogic.cs
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Device driver communication logic
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Opc.Ua;
+using Opc.Ua.Client;
+using Opc.Ua.Configuration;
+using Scada.Comm.Devices.OpcUa;
+using Scada.Comm.Devices.OpcUa.Config;
+using Scada.Data.Configuration;
+using Scada.Data.Models;
+using Scada.Data.Tables;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Scada.Comm.Devices
+{
+ ///
+ /// Device driver communication logic.
+ /// Логика работы драйвера КП.
+ ///
+ public class KpOpcUaLogic : KPLogic
+ {
+ ///
+ /// Represents metadata about a subscription.
+ ///
+ private class SubscriptionTag
+ {
+ public SubscriptionTag(Subscription subscription)
+ {
+ Subscription = subscription;
+ ItemsByNodeID = new Dictionary();
+ }
+
+ public Subscription Subscription { get; private set; }
+ public Dictionary ItemsByNodeID { get; private set; }
+ }
+
+ ///
+ /// Represents metadata about a monitored item.
+ ///
+ public class ItemTag
+ {
+ public ItemConfig ItemConfig { get; set; }
+ public KPTag KPTag { get; set; }
+ }
+
+ ///
+ /// Supported tag types.
+ ///
+ private enum TagType { Number, String, DateTime };
+
+
+ ///
+ /// The period of reconnecting to OPC server if a connection lost, ms
+ ///
+ private const int ReconnectPeriod = 10000;
+ ///
+ /// The delay before reconnect.
+ ///
+ private static readonly TimeSpan ReconnectDelay = TimeSpan.FromSeconds(5);
+
+ private readonly object opcLock; // synchronizes communication with OPC server
+ private DeviceConfig deviceConfig; // the device driver configuration
+ private bool autoAccept; // auto accept OPC server certificate
+ private bool connected; // connection with OPC server is established
+ private DateTime connAttemptDT; // the time stamp of a connection attempt
+ private Session opcSession; // the OPC session
+ private SessionReconnectHandler reconnectHandler; // the object needed to reconnect
+ private Dictionary tagsByCnlNum; // the device tags accessed by channel number
+ private Dictionary subscrByID; // the subscription tags accessed by IDs
+ private Dictionary cmdByNum; // the commands accessed by their numbers
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public KpOpcUaLogic(int number)
+ : base(number)
+ {
+ opcLock = new object();
+ deviceConfig = null;
+ autoAccept = true;
+ connected = false;
+ connAttemptDT = DateTime.MinValue;
+ opcSession = null;
+ reconnectHandler = null;
+ tagsByCnlNum = null;
+ subscrByID = null;
+ cmdByNum = null;
+
+ CanSendCmd = true;
+ ConnRequired = false;
+ }
+
+
+ ///
+ /// Connects to the OPC server.
+ ///
+ private void ConnectToOpcServer()
+ {
+ try
+ {
+ OpcUaHelper helper = new OpcUaHelper(AppDirs, Number, OpcUaHelper.RuntimeKind.Logic)
+ {
+ CertificateValidation = CertificateValidator_CertificateValidation,
+ WriteToLog = WriteToLog
+ };
+
+ connected = helper.ConnectAsync(deviceConfig.ConnectionOptions, ReqParams.Timeout).Result;
+ autoAccept = autoAccept || helper.AutoAccept;
+ opcSession = helper.OpcSession;
+ opcSession.KeepAlive += OpcSession_KeepAlive;
+ opcSession.Notification += OpcSession_Notification;
+ }
+ catch (Exception ex)
+ {
+ connected = false;
+ WriteToLog((Localization.UseRussian ?
+ "Ошибка при соединении с OPC-сервером: " :
+ "Error connecting OPC server: ") + ex);
+ }
+ }
+
+ ///
+ /// Creates subscriptions according to the configuration.
+ ///
+ private bool CreateSubscriptions()
+ {
+ try
+ {
+ if (opcSession == null)
+ throw new InvalidOperationException("OPC session must not be null.");
+
+ subscrByID = new Dictionary();
+
+ foreach (SubscriptionConfig subscriptionConfig in deviceConfig.Subscriptions)
+ {
+ if (!subscriptionConfig.Active)
+ continue;
+
+ Subscription subscription = new Subscription(opcSession.DefaultSubscription)
+ {
+ DisplayName = subscriptionConfig.DisplayName,
+ PublishingInterval = subscriptionConfig.PublishingInterval
+ };
+
+ SubscriptionTag subscriptionTag = new SubscriptionTag(subscription);
+
+ foreach (ItemConfig itemConfig in subscriptionConfig.Items)
+ {
+ if (!itemConfig.Active)
+ continue;
+
+ subscription.AddItem(new MonitoredItem(subscription.DefaultItem)
+ {
+ StartNodeId = itemConfig.NodeID,
+ DisplayName = itemConfig.DisplayName
+ });
+
+ if (itemConfig.Tag is KPTag kpTag)
+ {
+ subscriptionTag.ItemsByNodeID[itemConfig.NodeID] = new ItemTag
+ {
+ ItemConfig = itemConfig,
+ KPTag = kpTag
+ };
+ }
+ }
+
+ opcSession.AddSubscription(subscription);
+ subscription.Create();
+ subscrByID[subscription.Id] = subscriptionTag;
+ }
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ WriteToLog((Localization.UseRussian ?
+ "Ошибка при создании подписок: " :
+ "Error creating subscriptions: ") + ex);
+ return false;
+ }
+ }
+
+ ///
+ /// Clears all subscriptions of the OPC session.
+ ///
+ private void ClearSubscriptions()
+ {
+ try
+ {
+ subscrByID = null;
+ opcSession.RemoveSubscriptions(new List(opcSession.Subscriptions));
+ }
+ catch (Exception ex)
+ {
+ WriteToLog((Localization.UseRussian ?
+ "Ошибка при очистке подписок: " :
+ "Error clearing subscriptions: ") + ex);
+ }
+ }
+
+ ///
+ /// Validates the certificate.
+ ///
+ private void CertificateValidator_CertificateValidation(CertificateValidator validator,
+ CertificateValidationEventArgs e)
+ {
+ if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
+ {
+ e.Accept = autoAccept;
+
+ if (autoAccept)
+ {
+ WriteToLog(string.Format(Localization.UseRussian ?
+ "Принятый сертификат: {0}" :
+ "Accepted certificate: {0}", e.Certificate.Subject));
+ }
+ else
+ {
+ WriteToLog(string.Format(Localization.UseRussian ?
+ "Отклоненный сертификат: {0}" :
+ "Rejected certificate: {0}", e.Certificate.Subject));
+ }
+ }
+ }
+
+ ///
+ /// Reconnects if needed.
+ ///
+ private void OpcSession_KeepAlive(Session sender, KeepAliveEventArgs e)
+ {
+ if (e.Status != null && ServiceResult.IsNotGood(e.Status))
+ {
+ WriteToLog(string.Format("{0} {1}/{2}",
+ e.Status, sender.OutstandingRequestCount, sender.DefunctRequestCount));
+
+ if (reconnectHandler == null)
+ {
+ InvalidateCurData();
+ WorkState = WorkStates.Error;
+ WriteToLog(Localization.UseRussian ?
+ "Переподключение к OPC-серверу" :
+ "Reconnecting to OPC server");
+ reconnectHandler = new SessionReconnectHandler();
+ reconnectHandler.BeginReconnect(sender, ReconnectPeriod, OpcSession_ReconnectComplete);
+ }
+ }
+ }
+
+ ///
+ /// Processes the reconnect procedure.
+ ///
+ private void OpcSession_ReconnectComplete(object sender, EventArgs e)
+ {
+ // ignore callbacks from discarded objects
+ if (!ReferenceEquals(sender, reconnectHandler))
+ {
+ return;
+ }
+
+ opcSession = reconnectHandler.Session;
+ reconnectHandler.Dispose();
+ reconnectHandler = null;
+
+ // after reconnecting, the subscriptions are automatically recreated, but with the wrong IDs and names,
+ // so it's needed to clear them and create again
+ ClearSubscriptions();
+ WorkState = CreateSubscriptions() ? WorkStates.Normal : WorkStates.Error;
+ WriteToLog(Localization.UseRussian ?
+ "Переподключено" :
+ "Reconnected");
+ }
+
+ ///
+ /// Processes new data received from OPC server.
+ ///
+ private void OpcSession_Notification(Session session, NotificationEventArgs e)
+ {
+ try
+ {
+ Monitor.Enter(opcLock);
+ WriteToLog("");
+ LastSessDT = DateTime.Now;
+
+ if (subscrByID != null &&
+ subscrByID.TryGetValue(e.Subscription.Id, out SubscriptionTag subscriptionTag))
+ {
+ WriteToLog(string.Format(Localization.UseRussian ?
+ "{0} КП {1}. Обработка новых данных. Подписка: {2}" :
+ "{0} Device {1}. Process new data. Subscription: {2}",
+ LastSessDT.ToLocalizedString(), Number, e.Subscription.DisplayName));
+ ProcessDataChanges(subscriptionTag, e.NotificationMessage);
+ ProcessEvents(e.NotificationMessage);
+ lastCommSucc = true;
+ }
+ else
+ {
+ WriteToLog(string.Format(Localization.UseRussian ?
+ "Ошибка: подписка [{0}] \"{1}\" не найдена" :
+ "Error: subscription [{0}] \"{1}\" not found",
+ e.Subscription.Id, e.Subscription.DisplayName));
+ lastCommSucc = false;
+ }
+ }
+ catch (Exception ex)
+ {
+ WriteToLog((Localization.UseRussian ?
+ "Ошибка при обработке новых данных: " :
+ "Error processing new data: ") + ex);
+ lastCommSucc = false;
+ }
+ finally
+ {
+ CalcSessStats();
+ Monitor.Exit(opcLock);
+ }
+ }
+
+ ///
+ /// Processes new data.
+ ///
+ private void ProcessDataChanges(SubscriptionTag subscriptionTag, NotificationMessage notificationMessage)
+ {
+ foreach (MonitoredItemNotification change in notificationMessage.GetDataChanges(false))
+ {
+ MonitoredItem monitoredItem = subscriptionTag.Subscription.FindItemByClientHandle(change.ClientHandle);
+
+ if (monitoredItem != null)
+ {
+ if (subscriptionTag.ItemsByNodeID.TryGetValue(monitoredItem.StartNodeId.ToString(),
+ out ItemTag itemTag))
+ {
+ WriteToLog((Localization.UseRussian ? "Приём " : "Receive ") +
+ monitoredItem.DisplayName + " = " + change.Value + " (" + change.Value.StatusCode + ")");
+
+ int tagIndex = itemTag.KPTag.Index;
+ int tagStatus = StatusCode.IsGood(change.Value.StatusCode) ?
+ BaseValues.CnlStatuses.Defined :
+ BaseValues.CnlStatuses.Undefined;
+
+ if (itemTag.ItemConfig.IsArray)
+ {
+ int arrayLen = itemTag.ItemConfig.ArrayLen;
+ double[] vals = DecodeArray(change.Value.Value, arrayLen, out TagType tagType);
+
+ for (int i = 0; i < arrayLen; i++)
+ {
+ SetCurData(tagIndex, vals[i], tagStatus);
+ KPTags[tagIndex].Aux = tagType;
+ tagIndex++;
+ }
+ }
+ else
+ {
+ SetCurData(tagIndex, DecodeItemVal(change.Value.Value, out TagType tagType), tagStatus);
+ itemTag.KPTag.Aux = tagType;
+ }
+ }
+ else
+ {
+ WriteToLog(string.Format(Localization.UseRussian ?
+ "Ошибка: тег \"{0}\" не найден" :
+ "Error: tag \"{0}\" not found", monitoredItem.StartNodeId));
+ }
+ }
+ }
+ }
+
+ ///
+ /// Processes new events.
+ ///
+ private void ProcessEvents(NotificationMessage notificationMessage)
+ {
+ foreach (EventFieldList eventFields in notificationMessage.GetEvents(true))
+ {
+ // events are not really implemented
+ WriteToLog((Localization.UseRussian ?
+ "Новое событие " :
+ "New event ") + eventFields);
+ }
+ }
+
+ ///
+ /// Decodes the received tag value and returns the tag type.
+ ///
+ private double DecodeItemVal(object val, out TagType tagType)
+ {
+ try
+ {
+ if (val is string)
+ {
+ tagType = TagType.String;
+ return ScadaUtils.EncodeAscii((string)val);
+ }
+ else if (val is DateTime)
+ {
+ tagType = TagType.DateTime;
+ return ScadaUtils.EncodeDateTime((DateTime)val);
+ }
+ else
+ {
+ tagType = TagType.Number;
+ return Convert.ToDouble(val);
+ }
+ }
+ catch (Exception ex)
+ {
+ WriteToLog((Localization.UseRussian ?
+ "Ошибка при декодировании элемента: " :
+ "Error decoding item: ") + ex);
+
+ tagType = TagType.Number;
+ return 0.0;
+ }
+ }
+
+ ///
+ /// Decodes the received array and returns the tag type.
+ ///
+ private double[] DecodeArray(object val, int len, out TagType tagType)
+ {
+ double[] outArr = new double[len];
+ tagType = TagType.Number;
+
+ try
+ {
+ // get the type of the 1st element
+ Array inArr = (Array)val;
+
+ if (inArr.Length > 0)
+ {
+ object firstItem = inArr.GetValue(0);
+ if (firstItem is string)
+ tagType = TagType.String;
+ else if (firstItem is DateTime)
+ tagType = TagType.DateTime;
+ }
+
+ // decode array elements
+ for (int i = 0, n = Math.Min(inArr.Length, len); i < n; i++)
+ {
+ object inVal = inArr.GetValue(i);
+ double outVal;
+
+ switch (tagType)
+ {
+ case TagType.String:
+ outVal = ScadaUtils.EncodeAscii((string)inVal);
+ break;
+ case TagType.DateTime:
+ outVal = ScadaUtils.EncodeDateTime((DateTime)inVal);
+ break;
+ default:
+ outVal = Convert.ToDouble(inVal);
+ break;
+ }
+
+ outArr[i] = outVal;
+ }
+ }
+ catch (Exception ex)
+ {
+ WriteToLog((Localization.UseRussian ?
+ "Ошибка при декодировании массива: " :
+ "Error decoding array: ") + ex);
+ }
+
+ return outArr;
+ }
+
+ ///
+ /// Initializes the device tags.
+ ///
+ private void InitDeviceTags()
+ {
+ tagsByCnlNum = new Dictionary();
+ List tagGroups = new List(deviceConfig.Subscriptions.Count);
+ int signal = 1;
+
+ foreach (SubscriptionConfig subscriptionConfig in deviceConfig.Subscriptions)
+ {
+ TagGroup tagGroup = new TagGroup(subscriptionConfig.DisplayName);
+ tagGroups.Add(tagGroup);
+
+ foreach (ItemConfig itemConfig in subscriptionConfig.Items)
+ {
+ bool cnlNumSpecified = itemConfig.CnlNum > 0;
+
+ if (itemConfig.IsArray)
+ {
+ for (int i = 0, n = itemConfig.ArrayLen; i < n; i++)
+ {
+ KPTag kpTag = tagGroup.AddNewTag(signal++, itemConfig.DisplayName + "[" + i + "]");
+ itemConfig.Tag = itemConfig.Tag ?? kpTag; // store a reference to the 1st tag
+
+ if (cnlNumSpecified)
+ tagsByCnlNum[itemConfig.CnlNum + i] = kpTag;
+ }
+ }
+ else
+ {
+ KPTag kpTag = tagGroup.AddNewTag(signal++, itemConfig.DisplayName);
+ itemConfig.Tag = kpTag;
+
+ if (cnlNumSpecified)
+ tagsByCnlNum[itemConfig.CnlNum] = kpTag;
+ }
+ }
+ }
+
+ InitKPTags(tagGroups);
+ }
+
+ ///
+ /// Converts the tag data to string.
+ ///
+ protected override string ConvertTagDataToStr(KPTag kpTag, SrezTableLight.CnlData tagData)
+ {
+ if (tagData.Stat > 0 && kpTag.Aux is TagType tagType)
+ {
+ switch (tagType)
+ {
+ case TagType.String:
+ return ScadaUtils.DecodeAscii(tagData.Val);
+ case TagType.DateTime:
+ return ScadaUtils.DecodeDateTime(tagData.Val).ToLocalizedString();
+ }
+ }
+
+ return base.ConvertTagDataToStr(kpTag, tagData);
+ }
+
+
+
+ ///
+ /// Performs a communication session.
+ ///
+ public override void Session()
+ {
+ if (deviceConfig == null)
+ {
+ lastCommSucc = false;
+ WorkState = WorkStates.Error;
+ }
+ else if (!connected)
+ {
+ base.Session();
+
+ // delay before connection
+ DateTime utcNow = DateTime.UtcNow;
+ TimeSpan connectionDelay = ReconnectDelay - (utcNow - connAttemptDT);
+
+ if (connectionDelay > TimeSpan.Zero)
+ {
+ WriteToLog(string.Format(Localization.UseRussian ?
+ "Задержка перед соединением {0} с" :
+ "Delay before connection {0} sec", connectionDelay.TotalSeconds.ToString("N1")));
+ Thread.Sleep(connectionDelay);
+ }
+
+ // connect to OPC server and create subscriptions
+ connAttemptDT = DateTime.UtcNow;
+ ConnectToOpcServer();
+ WorkState = connected && CreateSubscriptions() ?
+ WorkStates.Normal : WorkStates.Error;
+ }
+
+ Thread.Sleep(ReqParams.Delay);
+ }
+
+ ///
+ /// Sends the telecontrol command.
+ ///
+ public override void SendCmd(Command cmd)
+ {
+ try
+ {
+ Monitor.Enter(opcLock);
+ base.SendCmd(cmd);
+ lastCommSucc = false;
+
+ if (connected)
+ {
+ if (cmdByNum.TryGetValue(cmd.CmdNum, out CommandConfig commandConfig) &&
+ cmd.CmdTypeID == BaseValues.CmdTypes.Standard)
+ {
+ // prepare value to write
+ string dataTypeName = commandConfig.DataTypeName;
+ Type itemType = Type.GetType(dataTypeName, false, true);
+ object itemVal;
+
+ if (itemType == null)
+ {
+ throw new ScadaException(string.Format(Localization.UseRussian ?
+ "Не удалось получить тип данных {0}" :
+ "Unable to get data type {0}", dataTypeName));
+ }
+
+ if (itemType.IsArray)
+ {
+ throw new ScadaException(string.Format(Localization.UseRussian ?
+ "Тип данных {0} не поддерживается" :
+ "Data type {0} not supported", dataTypeName));
+ }
+
+ try
+ {
+ itemVal = Convert.ChangeType(cmd.CmdVal, itemType);
+ }
+ catch
+ {
+ throw new ScadaException(string.Format(Localization.UseRussian ?
+ "Не удалось привести значение команды к типу {0}" :
+ "Unable to convert command value to the type {0}", itemType.FullName));
+ }
+
+ // write value
+ WriteToLog(string.Format(Localization.UseRussian ?
+ "Отправка значения OPC-серверу: {0} = {1}" :
+ "Send value to the OPC server: {0} = {1}", commandConfig.DisplayName, itemVal));
+
+ WriteValue valueToWrite = new WriteValue
+ {
+ NodeId = commandConfig.NodeID,
+ AttributeId = Attributes.Value,
+ Value = new DataValue(new Variant(itemVal))
+ };
+
+ opcSession.Write(null, new WriteValueCollection { valueToWrite },
+ out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos);
+
+ if (StatusCode.IsGood(results[0]))
+ {
+ WriteToLog(CommPhrases.ResponseOK);
+ lastCommSucc = true;
+ }
+ else
+ {
+ WriteToLog(CommPhrases.ResponseError);
+ }
+ }
+ else
+ {
+ WriteToLog(CommPhrases.IllegalCommand);
+ }
+ }
+ else
+ {
+ WriteToLog(Localization.UseRussian ?
+ "Невозможно отправить команду ТУ, т.к. соединение с OPC-сервером не установлено" :
+ "Unable to send command because connection with the OPC server is not established");
+ }
+ }
+ finally
+ {
+ CalcCmdStats();
+ Monitor.Exit(opcLock);
+ }
+ }
+
+ ///
+ /// Performs actions after adding the device to a communication line.
+ ///
+ public override void OnAddedToCommLine()
+ {
+ deviceConfig = new DeviceConfig();
+
+ if (deviceConfig.Load(DeviceConfig.GetFileName(AppDirs.ConfigDir, Number), out string errMsg))
+ {
+ InitDeviceTags();
+
+ // fill the command dictionary
+ cmdByNum = new Dictionary();
+ deviceConfig.Commands.ForEach((CommandConfig c) => cmdByNum[c.CmdNum] = c);
+ }
+ else
+ {
+ deviceConfig = null;
+ WriteToLog(errMsg);
+ WriteToLog(Localization.UseRussian ?
+ "Взаимодействие с OPC-сервером невозможно, т.к. конфигурация КП не загружена" :
+ "Interaction with OPC server is impossible because device configuration is not loaded");
+ }
+ }
+
+ ///
+ /// Performs actions when terminating a communication line.
+ ///
+ public override void OnCommLineTerminate()
+ {
+ if (opcSession != null)
+ opcSession.Close();
+ }
+
+ ///
+ /// Binds the device tag to the input channel.
+ ///
+ public override void BindTag(int signal, int cnlNum, int objNum, int paramID)
+ {
+ // the signal here is the signal specified for a channel in the configuration database
+ if (signal > 0)
+ {
+ base.BindTag(signal, cnlNum, objNum, paramID);
+ }
+ else if (tagsByCnlNum.TryGetValue(cnlNum, out KPTag kpTag))
+ {
+ kpTag.CnlNum = cnlNum;
+ kpTag.ObjNum = objNum;
+ kpTag.ParamID = paramID;
+ }
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/KpOpcUaView.cs b/ScadaComm/OpenKPs/KpOpcUa/KpOpcUaView.cs
new file mode 100644
index 000000000..4bc6c46f5
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/KpOpcUaView.cs
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Device driver user interface
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Scada.Comm.Devices.OpcUa.Config;
+using Scada.Comm.Devices.OpcUa.UI;
+using Scada.Data.Configuration;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Scada.Comm.Devices
+{
+ ///
+ /// Device driver user interface.
+ /// Пользовательский интерфейс драйвера КП.
+ ///
+ public class KpOpcUaView : KPView
+ {
+ ///
+ /// The driver version.
+ ///
+ internal const string KpVersion = "5.0.0.0";
+
+
+ ///
+ /// Initializes a new instance of the class. Designed for general configuring.
+ ///
+ public KpOpcUaView()
+ : this(0)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class. Designed for configuring a particular device.
+ ///
+ public KpOpcUaView(int number)
+ : base(number)
+ {
+ CanShowProps = number > 0;
+ }
+
+
+ ///
+ /// Gets the driver description.
+ ///
+ public override string KPDescr
+ {
+ get
+ {
+ return Localization.UseRussian ?
+ "Взаимодействие с контроллерами по спецификации OPC UA.\n\n" +
+ "Команды ТУ:\n" +
+ "определяются конфигурацией КП (только стандартные)." :
+
+ "Interacting with controllers according to the OPC UA specification.\n\n" +
+ "Commands:\n" +
+ "defined by device configuration (only standard).";
+ }
+ }
+
+ ///
+ /// Gets the driver version.
+ ///
+ public override string Version
+ {
+ get
+ {
+ return KpVersion;
+ }
+ }
+
+ ///
+ /// Gets the prototypes of default device channels.
+ ///
+ public override KPCnlPrototypes DefaultCnls
+ {
+ get
+ {
+ // load configuration
+ DeviceConfig deviceConfig = new DeviceConfig();
+ string fileName = DeviceConfig.GetFileName(AppDirs.ConfigDir, Number);
+
+ if (!File.Exists(fileName))
+ return null;
+ else if (!deviceConfig.Load(fileName, out string errMsg))
+ throw new ScadaException(errMsg);
+
+ // create channel prototypes
+ KPCnlPrototypes prototypes = new KPCnlPrototypes();
+ List inCnls = prototypes.InCnls;
+ List ctrlCnls = prototypes.CtrlCnls;
+
+ // input channels
+ int signal = 1;
+ foreach (SubscriptionConfig subscriptionConfig in deviceConfig.Subscriptions)
+ {
+ foreach (ItemConfig itemConfig in subscriptionConfig.Items)
+ {
+ string tagNamePrefix = string.IsNullOrEmpty(itemConfig.DisplayName) ?
+ (Localization.UseRussian ? "Безымянный тег" : "Unnamed tag") :
+ itemConfig.DisplayName;
+ bool isArray = itemConfig.IsArray;
+ int tagCntByItem = isArray && itemConfig.ArrayLen > 0 ? itemConfig.ArrayLen : 1;
+
+ for (int k = 0; k < tagCntByItem; k++)
+ {
+ if (itemConfig.CnlNum <= 0)
+ {
+ string tagName = isArray ? tagNamePrefix + "[" + k + "]" : tagNamePrefix;
+ inCnls.Add(new InCnlPrototype(tagName, BaseValues.CnlTypes.TI) { Signal = signal });
+ }
+ signal++;
+ }
+
+ }
+ }
+
+ // output channels
+ foreach (CommandConfig commandConfig in deviceConfig.Commands)
+ {
+ ctrlCnls.Add(new CtrlCnlPrototype(commandConfig.DisplayName, BaseValues.CmdTypes.Standard)
+ {
+ CmdNum = commandConfig.CmdNum
+ });
+ }
+
+ return prototypes;
+ }
+ }
+
+
+ ///
+ /// Shows the driver properties.
+ ///
+ public override void ShowProps()
+ {
+ new FrmConfig(AppDirs, Number).ShowDialog();
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/Lang/KpOpcUa.en-GB.xml b/ScadaComm/OpenKPs/KpOpcUa/Lang/KpOpcUa.en-GB.xml
new file mode 100644
index 000000000..a66df1c5f
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/Lang/KpOpcUa.en-GB.xml
@@ -0,0 +1,71 @@
+
+
+
+ OPC UA - Device {0} Properties
+ Connection
+ Server URL
+ Connect to Server
+ Disconnect from Server
+ Security Options
+ Server Browse
+ View Attributes
+ Device
+ Add Selected Item
+ Add Subscription
+ Move Up
+ Move Down
+ Delete
+ Item Parameters
+ Item not selected
+ Save
+ Close
+ Subscription Parameters
+ Active
+ Display name
+ Publishing interval
+ Item Parameters
+ Active
+ Node ID
+ Is array
+ Array length
+ Input channel
+ Signal
+ Command Parameters
+ Data type
+ Command number
+ Error connecting to OPC server
+ Error disconnecting from OPC server
+ Error browsing OPC server item
+ Error getting OPC item data type
+ Server URL must not be empty.
+ Empty
+ Subscriptions
+ Commands
+ <Subscription>
+ <Item>
+ <Command>
+ Data type "{0}" is unknown.
+
+
+ Node Attributes
+ Name
+ Value
+ Close
+ Error reading attributes from OPC server
+
+
+ Security Options
+ Security mode
+ None
+ Sign
+ Sign and encrypt
+ Security policy
+ Authentication mode
+ Anonymous
+ Username and password
+ Username
+ Password
+ OK
+ Cancel
+
+
diff --git a/ScadaComm/OpenKPs/KpOpcUa/Lang/KpOpcUa.ru-RU.xml b/ScadaComm/OpenKPs/KpOpcUa/Lang/KpOpcUa.ru-RU.xml
new file mode 100644
index 000000000..86797af4c
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/Lang/KpOpcUa.ru-RU.xml
@@ -0,0 +1,71 @@
+
+
+
+ OPC UA - Свойства КП {0}
+ Соединение
+ Адрес сервера
+ Соединиться с сервером
+ Разъединиться с сервером
+ Настройки безопасности
+ Обзор сервера
+ Просмотреть атрибуты
+ КП
+ Добавить выбранный элемент
+ Добавить подписку
+ Переместить вверх
+ Переместить вниз
+ Удалить
+ Параметры элемента
+ Элемент не выбран
+ Сохранить
+ Закрыть
+ Параметры подписки
+ Активна
+ Наименование
+ Интервал публикации
+ Параметры элемента
+ Активен
+ Идентификатор узла
+ Массив
+ Длина массива
+ Входной канал
+ Сигнал
+ Параметры команды
+ Тип данных
+ Номер команды
+ Ошибка при соединении с OPC-сервером
+ Ошибка при разъединении с OPC-сервером
+ Ошибка при обзоре элемента OPC-сервера
+ Ошибка при получении типа данных OPC-элемента
+ Адрес сервера не должен быть пустым.
+ Пусто
+ Подписки
+ Команды
+ <Подписка>
+ <Элемент>
+ <Комманда>
+ Неизвестный тип данных "{0}".
+
+
+ Атрибуты узла
+ Наименование
+ Значение
+ Закрыть
+ Ошибка при чтении атрибутов с OPC-сервера
+
+
+ Настройки безопасности
+ Режим безопасности
+ Нет
+ Подпись
+ Подпись и шифрование
+ Политика безопасности
+ Режим аутентификации
+ Анонимно
+ Имя пользователя и пароль
+ Имя пользователя
+ Пароль
+ OK
+ Отмена
+
+
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/AuthenticationMode.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/AuthenticationMode.cs
new file mode 100644
index 000000000..42c1d6fae
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/AuthenticationMode.cs
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Specifies the authentication modes
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+namespace Scada.Comm.Devices.OpcUa.Config
+{
+ ///
+ /// Specifies the authentication modes.
+ /// Задает режимы аутентификации.
+ ///
+ public enum AuthenticationMode
+ {
+ Anonymous,
+ Username
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/CommandConfig.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/CommandConfig.cs
new file mode 100644
index 000000000..a59d35490
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/CommandConfig.cs
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Represents a command configuration
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+using System.Xml;
+
+namespace Scada.Comm.Devices.OpcUa.Config
+{
+ ///
+ /// Represents a command configuration.
+ /// Представляет конфигурацию команды.
+ ///
+ public class CommandConfig
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CommandConfig()
+ {
+ NodeID = "";
+ DisplayName = "";
+ DataTypeName = "";
+ CmdNum = 0;
+ }
+
+
+ ///
+ /// Gets or sets the OPC UA node ID.
+ ///
+ public string NodeID { get; set; }
+
+ ///
+ /// Gets or sets the display name.
+ ///
+ public string DisplayName { get; set; }
+
+ ///
+ /// Gets or sets the data type name.
+ ///
+ public string DataTypeName { get; set; }
+
+ ///
+ /// Gets or sets the command number.
+ ///
+ public int CmdNum { get; set; }
+
+
+ ///
+ /// Loads the configuration from the XML node.
+ ///
+ public void LoadFromXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ NodeID = xmlElem.GetAttrAsString("nodeID");
+ DisplayName = xmlElem.GetAttrAsString("displayName");
+ DataTypeName = xmlElem.GetAttrAsString("dataType");
+ CmdNum = xmlElem.GetAttrAsInt("cmdNum");
+ }
+
+ ///
+ /// Saves the configuration into the XML node.
+ ///
+ public void SaveToXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ xmlElem.SetAttribute("nodeID", NodeID);
+ xmlElem.SetAttribute("displayName", DisplayName);
+ xmlElem.SetAttribute("dataType", DataTypeName);
+ xmlElem.SetAttribute("cmdNum", CmdNum);
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/ConnectionOptions.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/ConnectionOptions.cs
new file mode 100644
index 000000000..1cd26c513
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/ConnectionOptions.cs
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Represents the OPC server connection options
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Opc.Ua;
+using System;
+using System.Xml;
+
+namespace Scada.Comm.Devices.OpcUa.Config
+{
+ ///
+ /// Represents the OPC server connection options.
+ /// Представляет параметры соединения с OPC-сервером.
+ ///
+ public class ConnectionOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ConnectionOptions()
+ {
+ ServerUrl = "";
+ SecurityMode = MessageSecurityMode.None;
+ SecurityPolicy = SecurityPolicy.None;
+ AuthenticationMode = AuthenticationMode.Anonymous;
+ Username = "";
+ Password = "";
+ }
+
+
+ ///
+ /// Gets or sets the server URL.
+ ///
+ public string ServerUrl { get; set; }
+
+ ///
+ /// Gets or sets the security mode.
+ ///
+ public MessageSecurityMode SecurityMode { get; set; }
+
+ ///
+ /// Gets or sets the security policy.
+ ///
+ public SecurityPolicy SecurityPolicy { get; set; }
+
+ ///
+ /// Gets or sets the authentication mode.
+ ///
+ public AuthenticationMode AuthenticationMode { get; set; }
+
+ ///
+ /// Gets or sets the username.
+ ///
+ public string Username { get; set; }
+
+ ///
+ /// Gets or sets the password.
+ ///
+ public string Password { get; set; }
+
+
+ ///
+ /// Loads the options from the XML node.
+ ///
+ public void LoadFromXml(XmlNode xmlNode)
+ {
+ if (xmlNode == null)
+ throw new ArgumentNullException("xmlNode");
+
+ ServerUrl = xmlNode.GetChildAsString("ServerUrl");
+ SecurityMode = xmlNode.GetChildAsEnum("SecurityMode", MessageSecurityMode.None);
+ SecurityPolicy = xmlNode.GetChildAsEnum("SecurityPolicy");
+ AuthenticationMode = xmlNode.GetChildAsEnum("AuthenticationMode");
+ Username = xmlNode.GetChildAsString("User");
+ Password = ScadaUtils.Decrypt(xmlNode.GetChildAsString("Password"));
+ }
+
+ ///
+ /// Saves the options into the XML node.
+ ///
+ public void SaveToXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ xmlElem.AppendElem("ServerUrl", ServerUrl);
+ xmlElem.AppendElem("SecurityMode", SecurityMode);
+ xmlElem.AppendElem("SecurityPolicy", SecurityPolicy);
+ xmlElem.AppendElem("AuthenticationMode", AuthenticationMode);
+ xmlElem.AppendElem("Username", Username);
+ xmlElem.AppendElem("Password", ScadaUtils.Encrypt(Password));
+ }
+
+ ///
+ /// Gets the security policy as a string.
+ ///
+ public string GetSecurityPolicy()
+ {
+ switch (SecurityPolicy)
+ {
+ case SecurityPolicy.Basic128Rsa15:
+ return SecurityPolicies.Basic128Rsa15;
+
+ case SecurityPolicy.Basic256:
+ return SecurityPolicies.Basic256;
+
+ case SecurityPolicy.Basic256Sha256:
+ return SecurityPolicies.Basic256Sha256;
+
+ case SecurityPolicy.Aes128_Sha256_RsaOaep:
+ return SecurityPolicies.Aes128_Sha256_RsaOaep;
+
+ case SecurityPolicy.Aes256_Sha256_RsaPss:
+ return SecurityPolicies.Aes256_Sha256_RsaPss;
+
+ case SecurityPolicy.Https:
+ return SecurityPolicies.Https;
+
+ default:
+ return SecurityPolicies.None;
+ }
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/DeviceConfig.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/DeviceConfig.cs
new file mode 100644
index 000000000..4b1beb37a
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/DeviceConfig.cs
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Represents a driver configuration for a device
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml;
+
+namespace Scada.Comm.Devices.OpcUa.Config
+{
+ ///
+ /// Represents a driver configuration for a device.
+ /// Представляет конфигурацию драйвера для устройства.
+ ///
+ public class DeviceConfig
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DeviceConfig()
+ {
+ SetToDefault();
+ }
+
+
+ ///
+ /// Gets the connection options.
+ ///
+ public ConnectionOptions ConnectionOptions { get; private set; }
+
+ ///
+ /// Gets the subscriptions.
+ ///
+ public List Subscriptions { get; private set; }
+
+ ///
+ /// Gets the commands.
+ ///
+ public List Commands { get; private set; }
+
+
+ ///
+ /// Sets the default values.
+ ///
+ private void SetToDefault()
+ {
+ ConnectionOptions = new ConnectionOptions();
+ Subscriptions = new List();
+ Commands = new List();
+ }
+
+ ///
+ /// Loads the configuration from the specified file.
+ ///
+ public bool Load(string fileName, out string errMsg)
+ {
+ try
+ {
+ SetToDefault();
+
+ if (!File.Exists(fileName))
+ throw new FileNotFoundException(string.Format(CommonPhrases.NamedFileNotFound, fileName));
+
+ XmlDocument xmlDoc = new XmlDocument();
+ xmlDoc.Load(fileName);
+ XmlElement rootElem = xmlDoc.DocumentElement;
+
+ if (rootElem.SelectSingleNode("ConnectionOptions") is XmlNode connectionOptionsNode)
+ ConnectionOptions.LoadFromXml(connectionOptionsNode);
+
+ if (rootElem.SelectSingleNode("Subscriptions") is XmlNode subscriptionsNode)
+ {
+ foreach (XmlElement subscriptionElem in subscriptionsNode.SelectNodes("Subscription"))
+ {
+ SubscriptionConfig subscriptionConfig = new SubscriptionConfig();
+ subscriptionConfig.LoadFromXml(subscriptionElem);
+ Subscriptions.Add(subscriptionConfig);
+ }
+ }
+
+ if (rootElem.SelectSingleNode("Commands") is XmlNode commandsNode)
+ {
+ foreach (XmlElement commandElem in commandsNode.SelectNodes("Command"))
+ {
+ CommandConfig commandConfig = new CommandConfig();
+ commandConfig.LoadFromXml(commandElem);
+ Commands.Add(commandConfig);
+ }
+ }
+
+ errMsg = "";
+ return true;
+ }
+ catch (Exception ex)
+ {
+ errMsg = CommPhrases.LoadKpSettingsError + ": " + ex.Message;
+ return false;
+ }
+ }
+
+ ///
+ /// Saves the configuration to the specified file.
+ ///
+ public bool Save(string fileName, out string errMsg)
+ {
+ try
+ {
+ XmlDocument xmlDoc = new XmlDocument();
+ XmlDeclaration xmlDecl = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
+ xmlDoc.AppendChild(xmlDecl);
+
+ XmlElement rootElem = xmlDoc.CreateElement("KpOpcUa");
+ xmlDoc.AppendChild(rootElem);
+
+ ConnectionOptions.SaveToXml(rootElem.AppendElem("ConnectionOptions"));
+ XmlElement subscriptionsElem = rootElem.AppendElem("Subscriptions");
+ XmlElement commandsElem = rootElem.AppendElem("Commands");
+
+ foreach (SubscriptionConfig subscriptionConfig in Subscriptions)
+ {
+ subscriptionConfig.SaveToXml(subscriptionsElem.AppendElem("Subscription"));
+ }
+
+ foreach (CommandConfig commandConfig in Commands)
+ {
+ commandConfig.SaveToXml(commandsElem.AppendElem("Command"));
+ }
+
+ xmlDoc.Save(fileName);
+ errMsg = "";
+ return true;
+ }
+ catch (Exception ex)
+ {
+ errMsg = CommPhrases.SaveKpSettingsError + ": " + ex.Message;
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the configuration file name.
+ ///
+ public static string GetFileName(string configDir, int kpNum)
+ {
+ return Path.Combine(configDir, "KpOpcUa_" + CommUtils.AddZeros(kpNum, 3) + ".xml");
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/ItemConfig.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/ItemConfig.cs
new file mode 100644
index 000000000..06946c92f
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/ItemConfig.cs
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Represents a monitored item configuration
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+using System.Xml;
+
+namespace Scada.Comm.Devices.OpcUa.Config
+{
+ ///
+ /// Represents a monitored item configuration.
+ /// Представляет конфигурацию отслеживаемого элемента.
+ ///
+ public class ItemConfig
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ItemConfig()
+ {
+ Active = true;
+ NodeID = "";
+ DisplayName = "";
+ IsArray = false;
+ ArrayLen = 1;
+ CnlNum = 0;
+ Tag = null;
+ }
+
+
+ ///
+ /// Gets or sets a value indicating that the item is active.
+ ///
+ public bool Active { get; set; }
+
+ ///
+ /// Gets or sets the OPC UA node ID.
+ ///
+ public string NodeID { get; set; }
+
+ ///
+ /// Gets or sets the display name.
+ ///
+ public string DisplayName { get; set; }
+
+ ///
+ /// Gets or sets a value indicating that the item data type is an array.
+ ///
+ public bool IsArray { get; set; }
+
+ ///
+ /// Get or sets the array length if the item represents an array.
+ ///
+ public int ArrayLen { get; set; }
+
+ ///
+ /// Gets or sets the input channel number to which the item is bound.
+ ///
+ public int CnlNum { get; set; }
+
+ ///
+ /// Gets or sets the object that contains data related to the item.
+ ///
+ public object Tag { get; set; }
+
+
+ ///
+ /// Loads the configuration from the XML node.
+ ///
+ public void LoadFromXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ Active = xmlElem.GetAttrAsBool("active");
+ NodeID = xmlElem.GetAttrAsString("nodeID");
+ DisplayName = xmlElem.GetAttrAsString("displayName");
+ IsArray = xmlElem.GetAttrAsBool("isArray");
+ ArrayLen = xmlElem.GetAttrAsInt("arrayLen");
+ CnlNum = xmlElem.GetAttrAsInt("cnlNum");
+ }
+
+ ///
+ /// Saves the configuration into the XML node.
+ ///
+ public void SaveToXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ xmlElem.SetAttribute("active", Active);
+ xmlElem.SetAttribute("nodeID", NodeID);
+ xmlElem.SetAttribute("displayName", DisplayName);
+ xmlElem.SetAttribute("isArray", IsArray);
+ xmlElem.SetAttribute("arrayLen", ArrayLen);
+ xmlElem.SetAttribute("cnlNum", CnlNum);
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/SecurityPolicy.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/SecurityPolicy.cs
new file mode 100644
index 000000000..dff044556
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/SecurityPolicy.cs
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Specifies the connection security modes
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+namespace Scada.Comm.Devices.OpcUa.Config
+{
+ ///
+ /// Specifies the connection security policies.
+ /// Задает политики безопасности соединения.
+ ///
+ public enum SecurityPolicy
+ {
+ None,
+ Basic128Rsa15,
+ Basic256,
+ Basic256Sha256,
+ Aes128_Sha256_RsaOaep,
+ Aes256_Sha256_RsaPss,
+ Https
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/SubscriptionConfig.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/SubscriptionConfig.cs
new file mode 100644
index 000000000..59b0202ab
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/Config/SubscriptionConfig.cs
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Represents a subscription configuration
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Xml;
+
+namespace Scada.Comm.Devices.OpcUa.Config
+{
+ ///
+ /// Represents a subscription configuration.
+ /// Представляет конфигурацию подписки.
+ ///
+ public class SubscriptionConfig
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SubscriptionConfig()
+ {
+ Active = true;
+ DisplayName = "";
+ PublishingInterval = 1000;
+ Items = new List();
+ }
+
+
+ ///
+ /// Gets or sets a value indicating that the subscription is active.
+ ///
+ public bool Active { get; set; }
+
+ ///
+ /// Gets or sets the display name.
+ ///
+ public string DisplayName { get; set; }
+
+ ///
+ /// Gets or sets the publishing interval.
+ ///
+ public int PublishingInterval { get; set; }
+
+ ///
+ /// Gets the monitored items of the subscription.
+ ///
+ public List Items { get; private set; }
+
+
+ ///
+ /// Loads the configuration from the XML node.
+ ///
+ public void LoadFromXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ Active = xmlElem.GetAttrAsBool("active");
+ DisplayName = xmlElem.GetAttrAsString("displayName");
+ PublishingInterval = xmlElem.GetAttrAsInt("publishingInterval", PublishingInterval);
+
+ foreach (XmlElement itemElem in xmlElem.SelectNodes("Item"))
+ {
+ ItemConfig itemConfig = new ItemConfig();
+ itemConfig.LoadFromXml(itemElem);
+ Items.Add(itemConfig);
+ }
+ }
+
+ ///
+ /// Saves the configuration into the XML node.
+ ///
+ public void SaveToXml(XmlElement xmlElem)
+ {
+ if (xmlElem == null)
+ throw new ArgumentNullException("xmlElem");
+
+ xmlElem.SetAttribute("active", Active);
+ xmlElem.SetAttribute("displayName", DisplayName);
+ xmlElem.SetAttribute("publishingInterval", PublishingInterval);
+
+ foreach (ItemConfig itemConfig in Items)
+ {
+ itemConfig.SaveToXml(xmlElem.AppendElem("Item"));
+ }
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/OpcUaHelper.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/OpcUaHelper.cs
new file mode 100644
index 000000000..f9e34bba6
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/OpcUaHelper.cs
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Helper methods to work using OPC UA
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Opc.Ua;
+using Opc.Ua.Client;
+using Opc.Ua.Configuration;
+using Scada.Comm.Devices.OpcUa.Config;
+using System;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Utils;
+
+namespace Scada.Comm.Devices.OpcUa
+{
+ ///
+ /// Helper methods to work using OPC UA.
+ /// Вспомогательные методы для работы с OPC UA.
+ ///
+ public class OpcUaHelper
+ {
+ ///
+ /// The runtime kinds.
+ ///
+ public enum RuntimeKind { Logic, View };
+
+ ///
+ /// The OPC UA configuration file name for the logic runtime.
+ ///
+ private const string LogicOpcConfig = "KpOpcUa.Logic.xml";
+ ///
+ /// The OPC UA configuration file name for the view runtime.
+ ///
+ private const string ViewOpcConfig = "KpOpcUa.View.xml";
+
+ private readonly AppDirs appDirs; // the application directories
+ private readonly string kpNumStr; // the device number as a string
+ private readonly RuntimeKind runtime; // the runtime kind
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public OpcUaHelper(AppDirs appDirs, int kpNum, RuntimeKind runtime)
+ {
+ this.appDirs = appDirs ?? throw new ArgumentNullException("appDirs");
+ kpNumStr = CommUtils.AddZeros(kpNum, 3);
+ this.runtime = runtime;
+
+ AutoAccept = false;
+ OpcSession = null;
+ CertificateValidation = null;
+ WriteToLog = (string text) => { }; // stub
+ }
+
+
+ ///
+ /// Gets or sets the certificate validation method.
+ ///
+ public CertificateValidationEventHandler CertificateValidation { get; set; }
+
+ ///
+ /// Gets or sets the logging method.
+ ///
+ public Log.WriteLineDelegate WriteToLog { get; set; }
+
+ ///
+ /// Gets a value indicating whether to automatically accept server certificates.
+ ///
+ public bool AutoAccept { get; private set; }
+
+ ///
+ /// Gets the OPC session
+ ///
+ public Session OpcSession { get; private set; }
+
+
+ ///
+ /// Writes an OPC UA configuration file depending on operating system and runtime kind.
+ ///
+ private void WriteConfigFile(out string fileName)
+ {
+ fileName = Path.Combine(appDirs.ConfigDir, runtime == RuntimeKind.View ? ViewOpcConfig : LogicOpcConfig);
+
+ if (!File.Exists(fileName))
+ {
+ string resourceName = ScadaUtils.IsRunningOnWin ?
+ "Scada.Comm.Devices.Config.KpOpcUa.Win.xml" :
+ "Scada.Comm.Devices.Config.KpOpcUa.Linux.xml";
+ string fileContents;
+
+ using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
+ {
+ using (StreamReader reader = new StreamReader(stream))
+ {
+ fileContents = reader.ReadToEnd();
+ }
+ }
+
+ File.WriteAllText(fileName, fileContents, Encoding.UTF8);
+ }
+ }
+
+ ///
+ /// Connects to the OPC server asynchronously.
+ ///
+ public async Task ConnectAsync(ConnectionOptions connectionOptions, int operationTimeout = -1)
+ {
+ AutoAccept = false;
+ OpcSession = null;
+
+ ApplicationInstance application = new ApplicationInstance
+ {
+ ApplicationName = string.Format("KpOpcUa_{0} Driver", kpNumStr),
+ ApplicationType = ApplicationType.Client,
+ ConfigSectionName = "Scada.Comm.Devices.KpOpcUa"
+ };
+
+ // load the application configuration
+ WriteConfigFile(out string configFileName);
+ ApplicationConfiguration config = await application.LoadApplicationConfiguration(configFileName, false);
+
+ // check the application certificate
+ bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);
+
+ if (!haveAppCertificate)
+ {
+ throw new ScadaException(Localization.UseRussian ?
+ "Сертификат экземпляра приложения недействителен!" :
+ "Application instance certificate invalid!");
+ }
+
+ if (haveAppCertificate)
+ {
+ config.ApplicationUri = Opc.Ua.Utils.GetApplicationUriFromCertificate(
+ config.SecurityConfiguration.ApplicationCertificate.Certificate);
+
+ if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
+ {
+ AutoAccept = true;
+ }
+
+ if (CertificateValidation != null)
+ config.CertificateValidator.CertificateValidation += CertificateValidation;
+ }
+ else
+ {
+ WriteToLog(Localization.UseRussian ?
+ "Предупреждение: отсутствует сертификат приложения, используется незащищенное соединение." :
+ "Warning: missing application certificate, using unsecure connection.");
+ }
+
+ // create session
+ EndpointDescription selectedEndpoint = CoreClientUtils.SelectEndpoint(
+ connectionOptions.ServerUrl, haveAppCertificate, operationTimeout);
+ selectedEndpoint.SecurityMode = connectionOptions.SecurityMode;
+ selectedEndpoint.SecurityPolicyUri = connectionOptions.GetSecurityPolicy();
+ EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(config);
+ ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);
+ UserIdentity userIdentity = connectionOptions.AuthenticationMode == AuthenticationMode.Username ?
+ new UserIdentity(connectionOptions.Username, connectionOptions.Password) :
+ new UserIdentity(new AnonymousIdentityToken());
+
+ OpcSession = await Session.Create(config, endpoint, false,
+ "Rapid SCADA KpOpcUa_" + kpNumStr,
+ (uint)config.ClientConfiguration.DefaultSessionTimeout, userIdentity, null);
+
+ WriteToLog(string.Format(Localization.UseRussian ?
+ "OPC сессия успешно создана: {0}" :
+ "OPC session created successfully: {0}", connectionOptions.ServerUrl));
+ return true;
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.Designer.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.Designer.cs
new file mode 100644
index 000000000..4756491ee
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.Designer.cs
@@ -0,0 +1,171 @@
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ partial class CtrlCommand
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.gbCommand = new System.Windows.Forms.GroupBox();
+ this.numCmdNum = new System.Windows.Forms.NumericUpDown();
+ this.lblCmdNum = new System.Windows.Forms.Label();
+ this.txtDataType = new System.Windows.Forms.TextBox();
+ this.lblDataType = new System.Windows.Forms.Label();
+ this.txtNodeID = new System.Windows.Forms.TextBox();
+ this.lblNodeID = new System.Windows.Forms.Label();
+ this.txtDisplayName = new System.Windows.Forms.TextBox();
+ this.lblDisplayName = new System.Windows.Forms.Label();
+ this.gbCommand.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.numCmdNum)).BeginInit();
+ this.SuspendLayout();
+ //
+ // gbCommand
+ //
+ this.gbCommand.Controls.Add(this.numCmdNum);
+ this.gbCommand.Controls.Add(this.lblCmdNum);
+ this.gbCommand.Controls.Add(this.txtDataType);
+ this.gbCommand.Controls.Add(this.lblDataType);
+ this.gbCommand.Controls.Add(this.txtNodeID);
+ this.gbCommand.Controls.Add(this.lblNodeID);
+ this.gbCommand.Controls.Add(this.txtDisplayName);
+ this.gbCommand.Controls.Add(this.lblDisplayName);
+ this.gbCommand.Location = new System.Drawing.Point(0, 0);
+ this.gbCommand.Name = "gbCommand";
+ this.gbCommand.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
+ this.gbCommand.Size = new System.Drawing.Size(230, 407);
+ this.gbCommand.TabIndex = 0;
+ this.gbCommand.TabStop = false;
+ this.gbCommand.Text = "Command Parameters";
+ //
+ // numCmdNum
+ //
+ this.numCmdNum.Location = new System.Drawing.Point(13, 149);
+ this.numCmdNum.Maximum = new decimal(new int[] {
+ 65535,
+ 0,
+ 0,
+ 0});
+ this.numCmdNum.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.numCmdNum.Name = "numCmdNum";
+ this.numCmdNum.Size = new System.Drawing.Size(100, 20);
+ this.numCmdNum.TabIndex = 7;
+ this.numCmdNum.Value = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.numCmdNum.ValueChanged += new System.EventHandler(this.numCmdNum_ValueChanged);
+ //
+ // lblCmdNum
+ //
+ this.lblCmdNum.AutoSize = true;
+ this.lblCmdNum.Location = new System.Drawing.Point(10, 133);
+ this.lblCmdNum.Name = "lblCmdNum";
+ this.lblCmdNum.Size = new System.Drawing.Size(92, 13);
+ this.lblCmdNum.TabIndex = 6;
+ this.lblCmdNum.Text = "Command number";
+ //
+ // txtDataType
+ //
+ this.txtDataType.Location = new System.Drawing.Point(13, 110);
+ this.txtDataType.Name = "txtDataType";
+ this.txtDataType.ReadOnly = true;
+ this.txtDataType.Size = new System.Drawing.Size(204, 20);
+ this.txtDataType.TabIndex = 5;
+ //
+ // lblDataType
+ //
+ this.lblDataType.AutoSize = true;
+ this.lblDataType.Location = new System.Drawing.Point(10, 94);
+ this.lblDataType.Name = "lblDataType";
+ this.lblDataType.Size = new System.Drawing.Size(53, 13);
+ this.lblDataType.TabIndex = 4;
+ this.lblDataType.Text = "Data type";
+ //
+ // txtNodeID
+ //
+ this.txtNodeID.Location = new System.Drawing.Point(13, 71);
+ this.txtNodeID.Name = "txtNodeID";
+ this.txtNodeID.ReadOnly = true;
+ this.txtNodeID.Size = new System.Drawing.Size(204, 20);
+ this.txtNodeID.TabIndex = 3;
+ //
+ // lblNodeID
+ //
+ this.lblNodeID.AutoSize = true;
+ this.lblNodeID.Location = new System.Drawing.Point(10, 55);
+ this.lblNodeID.Name = "lblNodeID";
+ this.lblNodeID.Size = new System.Drawing.Size(47, 13);
+ this.lblNodeID.TabIndex = 2;
+ this.lblNodeID.Text = "Node ID";
+ //
+ // txtDisplayName
+ //
+ this.txtDisplayName.Location = new System.Drawing.Point(13, 32);
+ this.txtDisplayName.Name = "txtDisplayName";
+ this.txtDisplayName.Size = new System.Drawing.Size(204, 20);
+ this.txtDisplayName.TabIndex = 1;
+ this.txtDisplayName.TextChanged += new System.EventHandler(this.txtDisplayName_TextChanged);
+ //
+ // lblDisplayName
+ //
+ this.lblDisplayName.AutoSize = true;
+ this.lblDisplayName.Location = new System.Drawing.Point(10, 16);
+ this.lblDisplayName.Name = "lblDisplayName";
+ this.lblDisplayName.Size = new System.Drawing.Size(70, 13);
+ this.lblDisplayName.TabIndex = 0;
+ this.lblDisplayName.Text = "Display name";
+ //
+ // CtrlCommand
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.gbCommand);
+ this.Name = "CtrlCommand";
+ this.Size = new System.Drawing.Size(230, 407);
+ this.gbCommand.ResumeLayout(false);
+ this.gbCommand.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.numCmdNum)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.GroupBox gbCommand;
+ private System.Windows.Forms.TextBox txtNodeID;
+ private System.Windows.Forms.Label lblNodeID;
+ private System.Windows.Forms.TextBox txtDisplayName;
+ private System.Windows.Forms.Label lblDisplayName;
+ private System.Windows.Forms.TextBox txtDataType;
+ private System.Windows.Forms.Label lblDataType;
+ private System.Windows.Forms.NumericUpDown numCmdNum;
+ private System.Windows.Forms.Label lblCmdNum;
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.cs
new file mode 100644
index 000000000..a64460027
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.cs
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Control for editing a command
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Scada.Comm.Devices.OpcUa.Config;
+using Scada.UI;
+using System;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ ///
+ /// Control for editing a command.
+ /// Элемент управления для редактирования команды.
+ ///
+ public partial class CtrlCommand : UserControl
+ {
+ private CommandConfig commandConfig;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CtrlCommand()
+ {
+ InitializeComponent();
+ }
+
+
+ ///
+ /// Gets or sets the edited command.
+ ///
+ public CommandConfig CommandConfig
+ {
+ get
+ {
+ return commandConfig;
+ }
+ set
+ {
+ commandConfig = null;
+ ShowCommandProps(value);
+ commandConfig = value;
+ }
+ }
+
+
+ ///
+ /// Shows the command properties.
+ ///
+ private void ShowCommandProps(CommandConfig commandConfig)
+ {
+ if (commandConfig != null)
+ {
+ txtDisplayName.Text = commandConfig.DisplayName;
+ txtNodeID.Text = commandConfig.NodeID;
+ txtDataType.Text = commandConfig.DataTypeName;
+ numCmdNum.SetValue(commandConfig.CmdNum);
+ }
+ }
+
+ ///
+ /// Raises the ObjectChanged event.
+ ///
+ private void OnObjectChanged(object changeArgument)
+ {
+ ObjectChanged?.Invoke(this, new ObjectChangedEventArgs(commandConfig, changeArgument));
+ }
+
+ ///
+ /// Sets the input focus.
+ ///
+ public void SetFocus()
+ {
+ txtDisplayName.Select();
+ }
+
+
+ ///
+ /// Occurs when a property of the edited object changes.
+ ///
+ [Category("Property Changed")]
+ public event ObjectChangedEventHandler ObjectChanged;
+
+
+ private void txtDisplayName_TextChanged(object sender, EventArgs e)
+ {
+ if (commandConfig != null)
+ {
+ commandConfig.DisplayName = txtDisplayName.Text;
+ OnObjectChanged(TreeUpdateTypes.CurrentNode);
+ }
+ }
+
+ private void numCmdNum_ValueChanged(object sender, EventArgs e)
+ {
+ if (commandConfig != null)
+ {
+ commandConfig.CmdNum = Convert.ToInt32(numCmdNum.Value);
+ OnObjectChanged(TreeUpdateTypes.None);
+ }
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.resx b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.resx
new file mode 100644
index 000000000..1af7de150
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlCommand.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.Designer.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.Designer.cs
new file mode 100644
index 000000000..35b9d465f
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.Designer.cs
@@ -0,0 +1,229 @@
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ partial class CtrlItem
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.gbItem = new System.Windows.Forms.GroupBox();
+ this.txtSignal = new System.Windows.Forms.TextBox();
+ this.lblSignal = new System.Windows.Forms.Label();
+ this.numCnlNum = new System.Windows.Forms.NumericUpDown();
+ this.lblCnlNum = new System.Windows.Forms.Label();
+ this.numArrayLen = new System.Windows.Forms.NumericUpDown();
+ this.lblArrayLen = new System.Windows.Forms.Label();
+ this.chkIsArray = new System.Windows.Forms.CheckBox();
+ this.txtNodeID = new System.Windows.Forms.TextBox();
+ this.lblNodeID = new System.Windows.Forms.Label();
+ this.txtDisplayName = new System.Windows.Forms.TextBox();
+ this.lblDisplayName = new System.Windows.Forms.Label();
+ this.chkItemActive = new System.Windows.Forms.CheckBox();
+ this.gbItem.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.numCnlNum)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numArrayLen)).BeginInit();
+ this.SuspendLayout();
+ //
+ // gbItem
+ //
+ this.gbItem.Controls.Add(this.txtSignal);
+ this.gbItem.Controls.Add(this.lblSignal);
+ this.gbItem.Controls.Add(this.numCnlNum);
+ this.gbItem.Controls.Add(this.lblCnlNum);
+ this.gbItem.Controls.Add(this.numArrayLen);
+ this.gbItem.Controls.Add(this.lblArrayLen);
+ this.gbItem.Controls.Add(this.chkIsArray);
+ this.gbItem.Controls.Add(this.txtNodeID);
+ this.gbItem.Controls.Add(this.lblNodeID);
+ this.gbItem.Controls.Add(this.txtDisplayName);
+ this.gbItem.Controls.Add(this.lblDisplayName);
+ this.gbItem.Controls.Add(this.chkItemActive);
+ this.gbItem.Location = new System.Drawing.Point(0, 0);
+ this.gbItem.Name = "gbItem";
+ this.gbItem.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
+ this.gbItem.Size = new System.Drawing.Size(230, 407);
+ this.gbItem.TabIndex = 0;
+ this.gbItem.TabStop = false;
+ this.gbItem.Text = "Item Parameters";
+ //
+ // txtSignal
+ //
+ this.txtSignal.Location = new System.Drawing.Point(13, 234);
+ this.txtSignal.Name = "txtSignal";
+ this.txtSignal.ReadOnly = true;
+ this.txtSignal.Size = new System.Drawing.Size(100, 20);
+ this.txtSignal.TabIndex = 11;
+ //
+ // lblSignal
+ //
+ this.lblSignal.AutoSize = true;
+ this.lblSignal.Location = new System.Drawing.Point(10, 218);
+ this.lblSignal.Name = "lblSignal";
+ this.lblSignal.Size = new System.Drawing.Size(36, 13);
+ this.lblSignal.TabIndex = 10;
+ this.lblSignal.Text = "Signal";
+ //
+ // numCnlNum
+ //
+ this.numCnlNum.Location = new System.Drawing.Point(13, 195);
+ this.numCnlNum.Maximum = new decimal(new int[] {
+ 65535,
+ 0,
+ 0,
+ 0});
+ this.numCnlNum.Name = "numCnlNum";
+ this.numCnlNum.Size = new System.Drawing.Size(100, 20);
+ this.numCnlNum.TabIndex = 9;
+ this.numCnlNum.ValueChanged += new System.EventHandler(this.numCnlNum_ValueChanged);
+ //
+ // lblCnlNum
+ //
+ this.lblCnlNum.AutoSize = true;
+ this.lblCnlNum.Location = new System.Drawing.Point(10, 179);
+ this.lblCnlNum.Name = "lblCnlNum";
+ this.lblCnlNum.Size = new System.Drawing.Size(72, 13);
+ this.lblCnlNum.TabIndex = 8;
+ this.lblCnlNum.Text = "Input channel";
+ //
+ // numArrayLen
+ //
+ this.numArrayLen.Location = new System.Drawing.Point(13, 156);
+ this.numArrayLen.Maximum = new decimal(new int[] {
+ 1000,
+ 0,
+ 0,
+ 0});
+ this.numArrayLen.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.numArrayLen.Name = "numArrayLen";
+ this.numArrayLen.Size = new System.Drawing.Size(100, 20);
+ this.numArrayLen.TabIndex = 7;
+ this.numArrayLen.Value = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.numArrayLen.ValueChanged += new System.EventHandler(this.numArrayLen_ValueChanged);
+ //
+ // lblArrayLen
+ //
+ this.lblArrayLen.AutoSize = true;
+ this.lblArrayLen.Location = new System.Drawing.Point(10, 140);
+ this.lblArrayLen.Name = "lblArrayLen";
+ this.lblArrayLen.Size = new System.Drawing.Size(63, 13);
+ this.lblArrayLen.TabIndex = 6;
+ this.lblArrayLen.Text = "Array length";
+ //
+ // chkIsArray
+ //
+ this.chkIsArray.AutoSize = true;
+ this.chkIsArray.Location = new System.Drawing.Point(13, 120);
+ this.chkIsArray.Name = "chkIsArray";
+ this.chkIsArray.Size = new System.Drawing.Size(60, 17);
+ this.chkIsArray.TabIndex = 5;
+ this.chkIsArray.Text = "Is array";
+ this.chkIsArray.UseVisualStyleBackColor = true;
+ this.chkIsArray.CheckedChanged += new System.EventHandler(this.chkIsArray_CheckedChanged);
+ //
+ // txtNodeID
+ //
+ this.txtNodeID.Location = new System.Drawing.Point(13, 94);
+ this.txtNodeID.Name = "txtNodeID";
+ this.txtNodeID.ReadOnly = true;
+ this.txtNodeID.Size = new System.Drawing.Size(204, 20);
+ this.txtNodeID.TabIndex = 4;
+ //
+ // lblNodeID
+ //
+ this.lblNodeID.AutoSize = true;
+ this.lblNodeID.Location = new System.Drawing.Point(10, 78);
+ this.lblNodeID.Name = "lblNodeID";
+ this.lblNodeID.Size = new System.Drawing.Size(47, 13);
+ this.lblNodeID.TabIndex = 3;
+ this.lblNodeID.Text = "Node ID";
+ //
+ // txtDisplayName
+ //
+ this.txtDisplayName.Location = new System.Drawing.Point(13, 55);
+ this.txtDisplayName.Name = "txtDisplayName";
+ this.txtDisplayName.Size = new System.Drawing.Size(204, 20);
+ this.txtDisplayName.TabIndex = 2;
+ this.txtDisplayName.TextChanged += new System.EventHandler(this.txtDisplayName_TextChanged);
+ //
+ // lblDisplayName
+ //
+ this.lblDisplayName.AutoSize = true;
+ this.lblDisplayName.Location = new System.Drawing.Point(10, 39);
+ this.lblDisplayName.Name = "lblDisplayName";
+ this.lblDisplayName.Size = new System.Drawing.Size(70, 13);
+ this.lblDisplayName.TabIndex = 1;
+ this.lblDisplayName.Text = "Display name";
+ //
+ // chkItemActive
+ //
+ this.chkItemActive.AutoSize = true;
+ this.chkItemActive.Location = new System.Drawing.Point(13, 19);
+ this.chkItemActive.Name = "chkItemActive";
+ this.chkItemActive.Size = new System.Drawing.Size(56, 17);
+ this.chkItemActive.TabIndex = 0;
+ this.chkItemActive.Text = "Active";
+ this.chkItemActive.UseVisualStyleBackColor = true;
+ this.chkItemActive.CheckedChanged += new System.EventHandler(this.chkItemActive_CheckedChanged);
+ //
+ // CtrlItem
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.gbItem);
+ this.Name = "CtrlItem";
+ this.Size = new System.Drawing.Size(230, 407);
+ this.gbItem.ResumeLayout(false);
+ this.gbItem.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.numCnlNum)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numArrayLen)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.GroupBox gbItem;
+ private System.Windows.Forms.TextBox txtDisplayName;
+ private System.Windows.Forms.Label lblDisplayName;
+ private System.Windows.Forms.CheckBox chkItemActive;
+ private System.Windows.Forms.TextBox txtNodeID;
+ private System.Windows.Forms.Label lblNodeID;
+ private System.Windows.Forms.CheckBox chkIsArray;
+ private System.Windows.Forms.Label lblArrayLen;
+ private System.Windows.Forms.NumericUpDown numArrayLen;
+ private System.Windows.Forms.Label lblSignal;
+ private System.Windows.Forms.NumericUpDown numCnlNum;
+ private System.Windows.Forms.Label lblCnlNum;
+ private System.Windows.Forms.TextBox txtSignal;
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.cs
new file mode 100644
index 000000000..34520d95f
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.cs
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Control for editing a monitored item
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Scada.Comm.Devices.OpcUa.Config;
+using Scada.UI;
+using System;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ ///
+ /// Control for editing a monitored item.
+ /// Элемент управления для редактирования отслеживаемого элемента.
+ ///
+ public partial class CtrlItem : UserControl
+ {
+ private ItemConfig itemConfig;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CtrlItem()
+ {
+ InitializeComponent();
+ }
+
+
+ ///
+ /// Gets or sets the edited monitored item.
+ ///
+ public ItemConfig ItemConfig
+ {
+ get
+ {
+ return itemConfig;
+ }
+ set
+ {
+ itemConfig = null;
+ ShowItemProps(value);
+ itemConfig = value;
+ }
+ }
+
+
+ ///
+ /// Shows the monitored item properties.
+ ///
+ private void ShowItemProps(ItemConfig itemConfig)
+ {
+ if (itemConfig != null)
+ {
+ chkItemActive.Checked = itemConfig.Active;
+ txtDisplayName.Text = itemConfig.DisplayName;
+ txtNodeID.Text = itemConfig.NodeID;
+ chkIsArray.Checked = itemConfig.IsArray;
+ numArrayLen.Enabled = itemConfig.IsArray;
+ numArrayLen.SetValue(itemConfig.ArrayLen);
+ numCnlNum.SetValue(itemConfig.CnlNum);
+
+ if (itemConfig.Tag is FrmConfig.ItemConfigTag tag)
+ txtSignal.Text = tag.GetSignalStr();
+ }
+ }
+
+ ///
+ /// Updates the information in the item tag.
+ ///
+ private void UpdateTag()
+ {
+ if (itemConfig.Tag is FrmConfig.ItemConfigTag tag)
+ {
+ tag.SetLength(itemConfig.IsArray, itemConfig.ArrayLen);
+ txtSignal.Text = tag.GetSignalStr();
+ }
+ }
+
+ ///
+ /// Raises the ObjectChanged event.
+ ///
+ private void OnObjectChanged(object changeArgument)
+ {
+ ObjectChanged?.Invoke(this, new ObjectChangedEventArgs(itemConfig, changeArgument));
+ }
+
+ ///
+ /// Sets the input focus.
+ ///
+ public void SetFocus()
+ {
+ txtDisplayName.Select();
+ }
+
+ ///
+ /// Shows the signal value.
+ ///
+ public void ShowSignal()
+ {
+ if (itemConfig?.Tag is FrmConfig.ItemConfigTag tag)
+ txtSignal.Text = tag.GetSignalStr();
+ }
+
+
+ ///
+ /// Occurs when a property of the edited object changes.
+ ///
+ [Category("Property Changed")]
+ public event ObjectChangedEventHandler ObjectChanged;
+
+
+ private void chkItemActive_CheckedChanged(object sender, EventArgs e)
+ {
+ if (itemConfig != null)
+ {
+ itemConfig.Active = chkItemActive.Checked;
+ OnObjectChanged(TreeUpdateTypes.None);
+ }
+ }
+
+ private void txtDisplayName_TextChanged(object sender, EventArgs e)
+ {
+ if (itemConfig != null)
+ {
+ itemConfig.DisplayName = txtDisplayName.Text;
+ OnObjectChanged(TreeUpdateTypes.CurrentNode);
+ }
+ }
+
+ private void chkIsArray_CheckedChanged(object sender, EventArgs e)
+ {
+ if (itemConfig != null)
+ {
+ if (chkIsArray.Checked)
+ {
+ itemConfig.IsArray = true;
+ numArrayLen.Enabled = true;
+ }
+ else
+ {
+ itemConfig.IsArray = false;
+ numArrayLen.SetValue(1);
+ numArrayLen.Enabled = false;
+ }
+
+ UpdateTag();
+ OnObjectChanged(TreeUpdateTypes.None);
+ }
+ }
+
+ private void numArrayLen_ValueChanged(object sender, EventArgs e)
+ {
+ if (itemConfig != null)
+ {
+ itemConfig.ArrayLen = Convert.ToInt32(numArrayLen.Value);
+ UpdateTag();
+ OnObjectChanged(TreeUpdateTypes.UpdateSignals);
+ }
+ }
+
+ private void numCnlNum_ValueChanged(object sender, EventArgs e)
+ {
+ if (itemConfig != null)
+ {
+ itemConfig.CnlNum = Convert.ToInt32(numCnlNum.Value);
+ OnObjectChanged(TreeUpdateTypes.None);
+ }
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.resx b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.resx
new file mode 100644
index 000000000..1af7de150
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlItem.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.Designer.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.Designer.cs
new file mode 100644
index 000000000..6e94620c9
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.Designer.cs
@@ -0,0 +1,129 @@
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ partial class CtrlSubscription
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.dbSubscription = new System.Windows.Forms.GroupBox();
+ this.numPublishingInterval = new System.Windows.Forms.NumericUpDown();
+ this.lblPublishingInterval = new System.Windows.Forms.Label();
+ this.txtDisplayName = new System.Windows.Forms.TextBox();
+ this.lblDisplayName = new System.Windows.Forms.Label();
+ this.chkSubscrActive = new System.Windows.Forms.CheckBox();
+ this.dbSubscription.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.numPublishingInterval)).BeginInit();
+ this.SuspendLayout();
+ //
+ // dbSubscription
+ //
+ this.dbSubscription.Controls.Add(this.numPublishingInterval);
+ this.dbSubscription.Controls.Add(this.lblPublishingInterval);
+ this.dbSubscription.Controls.Add(this.txtDisplayName);
+ this.dbSubscription.Controls.Add(this.lblDisplayName);
+ this.dbSubscription.Controls.Add(this.chkSubscrActive);
+ this.dbSubscription.Location = new System.Drawing.Point(0, 0);
+ this.dbSubscription.Name = "dbSubscription";
+ this.dbSubscription.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
+ this.dbSubscription.Size = new System.Drawing.Size(230, 407);
+ this.dbSubscription.TabIndex = 0;
+ this.dbSubscription.TabStop = false;
+ this.dbSubscription.Text = "Subscription Parameters";
+ //
+ // numPublishingInterval
+ //
+ this.numPublishingInterval.Location = new System.Drawing.Point(13, 94);
+ this.numPublishingInterval.Maximum = new decimal(new int[] {
+ 1000000,
+ 0,
+ 0,
+ 0});
+ this.numPublishingInterval.Name = "numPublishingInterval";
+ this.numPublishingInterval.Size = new System.Drawing.Size(100, 20);
+ this.numPublishingInterval.TabIndex = 4;
+ this.numPublishingInterval.ValueChanged += new System.EventHandler(this.numPublishingInterval_ValueChanged);
+ //
+ // lblPublishingInterval
+ //
+ this.lblPublishingInterval.AutoSize = true;
+ this.lblPublishingInterval.Location = new System.Drawing.Point(10, 78);
+ this.lblPublishingInterval.Name = "lblPublishingInterval";
+ this.lblPublishingInterval.Size = new System.Drawing.Size(92, 13);
+ this.lblPublishingInterval.TabIndex = 3;
+ this.lblPublishingInterval.Text = "Publishing interval";
+ //
+ // txtDisplayName
+ //
+ this.txtDisplayName.Location = new System.Drawing.Point(13, 55);
+ this.txtDisplayName.Name = "txtDisplayName";
+ this.txtDisplayName.Size = new System.Drawing.Size(204, 20);
+ this.txtDisplayName.TabIndex = 2;
+ this.txtDisplayName.TextChanged += new System.EventHandler(this.txtDisplayName_TextChanged);
+ //
+ // lblDisplayName
+ //
+ this.lblDisplayName.AutoSize = true;
+ this.lblDisplayName.Location = new System.Drawing.Point(10, 39);
+ this.lblDisplayName.Name = "lblDisplayName";
+ this.lblDisplayName.Size = new System.Drawing.Size(70, 13);
+ this.lblDisplayName.TabIndex = 1;
+ this.lblDisplayName.Text = "Display name";
+ //
+ // chkSubscrActive
+ //
+ this.chkSubscrActive.AutoSize = true;
+ this.chkSubscrActive.Location = new System.Drawing.Point(13, 19);
+ this.chkSubscrActive.Name = "chkSubscrActive";
+ this.chkSubscrActive.Size = new System.Drawing.Size(56, 17);
+ this.chkSubscrActive.TabIndex = 0;
+ this.chkSubscrActive.Text = "Active";
+ this.chkSubscrActive.UseVisualStyleBackColor = true;
+ this.chkSubscrActive.CheckedChanged += new System.EventHandler(this.chkSubscrActive_CheckedChanged);
+ //
+ // CtrlSubscription
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.dbSubscription);
+ this.Name = "CtrlSubscription";
+ this.Size = new System.Drawing.Size(230, 407);
+ this.dbSubscription.ResumeLayout(false);
+ this.dbSubscription.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.numPublishingInterval)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.GroupBox dbSubscription;
+ private System.Windows.Forms.CheckBox chkSubscrActive;
+ private System.Windows.Forms.NumericUpDown numPublishingInterval;
+ private System.Windows.Forms.Label lblPublishingInterval;
+ private System.Windows.Forms.TextBox txtDisplayName;
+ private System.Windows.Forms.Label lblDisplayName;
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.cs
new file mode 100644
index 000000000..54f965b93
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.cs
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Control for editing a subscription
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Scada.Comm.Devices.OpcUa.Config;
+using Scada.UI;
+using System;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ ///
+ /// Control for editing a subscription.
+ /// Элемент управления для редактирования подписки.
+ ///
+ public partial class CtrlSubscription : UserControl
+ {
+ private SubscriptionConfig subscriptionConfig;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CtrlSubscription()
+ {
+ InitializeComponent();
+ }
+
+
+ ///
+ /// Gets or sets the edited subscription.
+ ///
+ public SubscriptionConfig SubscriptionConfig
+ {
+ get
+ {
+ return subscriptionConfig;
+ }
+ set
+ {
+ subscriptionConfig = null;
+ ShowSubscriptionProps(value);
+ subscriptionConfig = value;
+ }
+ }
+
+
+ ///
+ /// Shows the subscription properties.
+ ///
+ private void ShowSubscriptionProps(SubscriptionConfig subscriptionConfig)
+ {
+ if (subscriptionConfig != null)
+ {
+ chkSubscrActive.Checked = subscriptionConfig.Active;
+ txtDisplayName.Text = subscriptionConfig.DisplayName;
+ numPublishingInterval.SetValue(subscriptionConfig.PublishingInterval);
+ }
+ }
+
+ ///
+ /// Raises the ObjectChanged event.
+ ///
+ private void OnObjectChanged(object changeArgument)
+ {
+ ObjectChanged?.Invoke(this, new ObjectChangedEventArgs(subscriptionConfig, changeArgument));
+ }
+
+ ///
+ /// Sets the input focus.
+ ///
+ public void SetFocus()
+ {
+ txtDisplayName.Select();
+ }
+
+
+ ///
+ /// Occurs when a property of the edited object changes.
+ ///
+ [Category("Property Changed")]
+ public event ObjectChangedEventHandler ObjectChanged;
+
+
+ private void chkSubscrActive_CheckedChanged(object sender, EventArgs e)
+ {
+ if (subscriptionConfig != null)
+ {
+ subscriptionConfig.Active = chkSubscrActive.Checked;
+ OnObjectChanged(TreeUpdateTypes.None);
+ }
+ }
+
+ private void txtDisplayName_TextChanged(object sender, EventArgs e)
+ {
+ if (subscriptionConfig != null)
+ {
+ subscriptionConfig.DisplayName = txtDisplayName.Text;
+ OnObjectChanged(TreeUpdateTypes.CurrentNode);
+ }
+ }
+
+ private void numPublishingInterval_ValueChanged(object sender, EventArgs e)
+ {
+ if (subscriptionConfig != null)
+ {
+ subscriptionConfig.PublishingInterval = Convert.ToInt32(numPublishingInterval.Value);
+ OnObjectChanged(TreeUpdateTypes.None);
+ }
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.resx b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.resx
new file mode 100644
index 000000000..1af7de150
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/CtrlSubscription.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.Designer.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.Designer.cs
new file mode 100644
index 000000000..0fcfbc17e
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.Designer.cs
@@ -0,0 +1,405 @@
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ partial class FrmConfig
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmConfig));
+ this.btnClose = new System.Windows.Forms.Button();
+ this.btnSave = new System.Windows.Forms.Button();
+ this.gbDevice = new System.Windows.Forms.GroupBox();
+ this.tvDevice = new System.Windows.Forms.TreeView();
+ this.ilTree = new System.Windows.Forms.ImageList(this.components);
+ this.btnDeleteItem = new System.Windows.Forms.Button();
+ this.btnMoveDownItem = new System.Windows.Forms.Button();
+ this.btnMoveUpItem = new System.Windows.Forms.Button();
+ this.btnAddSubscription = new System.Windows.Forms.Button();
+ this.btnAddItem = new System.Windows.Forms.Button();
+ this.gbServerBrowse = new System.Windows.Forms.GroupBox();
+ this.btnViewAttrs = new System.Windows.Forms.Button();
+ this.tvServer = new System.Windows.Forms.TreeView();
+ this.gbConnection = new System.Windows.Forms.GroupBox();
+ this.btnSecurityOptions = new System.Windows.Forms.Button();
+ this.btnDisconnect = new System.Windows.Forms.Button();
+ this.btnConnect = new System.Windows.Forms.Button();
+ this.txtServerUrl = new System.Windows.Forms.TextBox();
+ this.lblServerUrl = new System.Windows.Forms.Label();
+ this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+ this.gbEmptyItem = new System.Windows.Forms.GroupBox();
+ this.lblEmptyItem = new System.Windows.Forms.Label();
+ this.ctrlCommand = new Scada.Comm.Devices.OpcUa.UI.CtrlCommand();
+ this.ctrlItem = new Scada.Comm.Devices.OpcUa.UI.CtrlItem();
+ this.ctrlSubscription = new Scada.Comm.Devices.OpcUa.UI.CtrlSubscription();
+ this.gbDevice.SuspendLayout();
+ this.gbServerBrowse.SuspendLayout();
+ this.gbConnection.SuspendLayout();
+ this.gbEmptyItem.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // btnClose
+ //
+ this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnClose.Location = new System.Drawing.Point(647, 496);
+ this.btnClose.Name = "btnClose";
+ this.btnClose.Size = new System.Drawing.Size(75, 23);
+ this.btnClose.TabIndex = 8;
+ this.btnClose.Text = "Close";
+ this.btnClose.UseVisualStyleBackColor = true;
+ //
+ // btnSave
+ //
+ this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnSave.Enabled = false;
+ this.btnSave.Location = new System.Drawing.Point(566, 496);
+ this.btnSave.Name = "btnSave";
+ this.btnSave.Size = new System.Drawing.Size(75, 23);
+ this.btnSave.TabIndex = 7;
+ this.btnSave.Text = "Save";
+ this.btnSave.UseVisualStyleBackColor = true;
+ this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
+ //
+ // gbDevice
+ //
+ this.gbDevice.Controls.Add(this.tvDevice);
+ this.gbDevice.Controls.Add(this.btnDeleteItem);
+ this.gbDevice.Controls.Add(this.btnMoveDownItem);
+ this.gbDevice.Controls.Add(this.btnMoveUpItem);
+ this.gbDevice.Controls.Add(this.btnAddSubscription);
+ this.gbDevice.Controls.Add(this.btnAddItem);
+ this.gbDevice.Location = new System.Drawing.Point(252, 83);
+ this.gbDevice.Name = "gbDevice";
+ this.gbDevice.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
+ this.gbDevice.Size = new System.Drawing.Size(234, 407);
+ this.gbDevice.TabIndex = 2;
+ this.gbDevice.TabStop = false;
+ this.gbDevice.Text = "Device";
+ //
+ // tvDevice
+ //
+ this.tvDevice.HideSelection = false;
+ this.tvDevice.ImageIndex = 0;
+ this.tvDevice.ImageList = this.ilTree;
+ this.tvDevice.Location = new System.Drawing.Point(13, 48);
+ this.tvDevice.Name = "tvDevice";
+ this.tvDevice.SelectedImageIndex = 0;
+ this.tvDevice.Size = new System.Drawing.Size(208, 346);
+ this.tvDevice.TabIndex = 5;
+ this.tvDevice.AfterCollapse += new System.Windows.Forms.TreeViewEventHandler(this.tvDevice_AfterCollapse);
+ this.tvDevice.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.tvDevice_AfterExpand);
+ this.tvDevice.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvDevice_AfterSelect);
+ //
+ // ilTree
+ //
+ this.ilTree.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("ilTree.ImageStream")));
+ this.ilTree.TransparentColor = System.Drawing.Color.Transparent;
+ this.ilTree.Images.SetKeyName(0, "command.png");
+ this.ilTree.Images.SetKeyName(1, "empty.png");
+ this.ilTree.Images.SetKeyName(2, "folder_closed.png");
+ this.ilTree.Images.SetKeyName(3, "folder_open.png");
+ this.ilTree.Images.SetKeyName(4, "method.png");
+ this.ilTree.Images.SetKeyName(5, "object.png");
+ this.ilTree.Images.SetKeyName(6, "variable.png");
+ //
+ // btnDeleteItem
+ //
+ this.btnDeleteItem.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnDeleteItem.Image = ((System.Drawing.Image)(resources.GetObject("btnDeleteItem.Image")));
+ this.btnDeleteItem.Location = new System.Drawing.Point(129, 19);
+ this.btnDeleteItem.Name = "btnDeleteItem";
+ this.btnDeleteItem.Size = new System.Drawing.Size(23, 23);
+ this.btnDeleteItem.TabIndex = 4;
+ this.toolTip.SetToolTip(this.btnDeleteItem, "Delete");
+ this.btnDeleteItem.UseVisualStyleBackColor = true;
+ this.btnDeleteItem.Click += new System.EventHandler(this.btnDeleteItem_Click);
+ //
+ // btnMoveDownItem
+ //
+ this.btnMoveDownItem.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnMoveDownItem.Image = ((System.Drawing.Image)(resources.GetObject("btnMoveDownItem.Image")));
+ this.btnMoveDownItem.Location = new System.Drawing.Point(100, 19);
+ this.btnMoveDownItem.Name = "btnMoveDownItem";
+ this.btnMoveDownItem.Size = new System.Drawing.Size(23, 23);
+ this.btnMoveDownItem.TabIndex = 3;
+ this.toolTip.SetToolTip(this.btnMoveDownItem, "Move Down");
+ this.btnMoveDownItem.UseVisualStyleBackColor = true;
+ this.btnMoveDownItem.Click += new System.EventHandler(this.btnMoveDownItem_Click);
+ //
+ // btnMoveUpItem
+ //
+ this.btnMoveUpItem.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnMoveUpItem.Image = ((System.Drawing.Image)(resources.GetObject("btnMoveUpItem.Image")));
+ this.btnMoveUpItem.Location = new System.Drawing.Point(71, 19);
+ this.btnMoveUpItem.Name = "btnMoveUpItem";
+ this.btnMoveUpItem.Size = new System.Drawing.Size(23, 23);
+ this.btnMoveUpItem.TabIndex = 2;
+ this.toolTip.SetToolTip(this.btnMoveUpItem, "Move Up");
+ this.btnMoveUpItem.UseVisualStyleBackColor = true;
+ this.btnMoveUpItem.Click += new System.EventHandler(this.btnMoveUpItem_Click);
+ //
+ // btnAddSubscription
+ //
+ this.btnAddSubscription.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnAddSubscription.Image = ((System.Drawing.Image)(resources.GetObject("btnAddSubscription.Image")));
+ this.btnAddSubscription.Location = new System.Drawing.Point(42, 19);
+ this.btnAddSubscription.Name = "btnAddSubscription";
+ this.btnAddSubscription.Size = new System.Drawing.Size(23, 23);
+ this.btnAddSubscription.TabIndex = 1;
+ this.toolTip.SetToolTip(this.btnAddSubscription, "Add Subscription");
+ this.btnAddSubscription.UseVisualStyleBackColor = true;
+ this.btnAddSubscription.Click += new System.EventHandler(this.btnAddSubscription_Click);
+ //
+ // btnAddItem
+ //
+ this.btnAddItem.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnAddItem.Image = ((System.Drawing.Image)(resources.GetObject("btnAddItem.Image")));
+ this.btnAddItem.Location = new System.Drawing.Point(13, 19);
+ this.btnAddItem.Name = "btnAddItem";
+ this.btnAddItem.Size = new System.Drawing.Size(23, 23);
+ this.btnAddItem.TabIndex = 0;
+ this.toolTip.SetToolTip(this.btnAddItem, "Add Selected Item");
+ this.btnAddItem.UseVisualStyleBackColor = true;
+ this.btnAddItem.Click += new System.EventHandler(this.btnAddItem_Click);
+ //
+ // gbServerBrowse
+ //
+ this.gbServerBrowse.Controls.Add(this.btnViewAttrs);
+ this.gbServerBrowse.Controls.Add(this.tvServer);
+ this.gbServerBrowse.Location = new System.Drawing.Point(12, 83);
+ this.gbServerBrowse.Name = "gbServerBrowse";
+ this.gbServerBrowse.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
+ this.gbServerBrowse.Size = new System.Drawing.Size(234, 407);
+ this.gbServerBrowse.TabIndex = 1;
+ this.gbServerBrowse.TabStop = false;
+ this.gbServerBrowse.Text = "Server Browse";
+ //
+ // btnViewAttrs
+ //
+ this.btnViewAttrs.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnViewAttrs.Image = ((System.Drawing.Image)(resources.GetObject("btnViewAttrs.Image")));
+ this.btnViewAttrs.Location = new System.Drawing.Point(13, 19);
+ this.btnViewAttrs.Name = "btnViewAttrs";
+ this.btnViewAttrs.Size = new System.Drawing.Size(23, 23);
+ this.btnViewAttrs.TabIndex = 1;
+ this.toolTip.SetToolTip(this.btnViewAttrs, "View Attributes");
+ this.btnViewAttrs.UseVisualStyleBackColor = true;
+ this.btnViewAttrs.Click += new System.EventHandler(this.btnViewAttrs_Click);
+ //
+ // tvServer
+ //
+ this.tvServer.HideSelection = false;
+ this.tvServer.ImageIndex = 0;
+ this.tvServer.ImageList = this.ilTree;
+ this.tvServer.Location = new System.Drawing.Point(13, 48);
+ this.tvServer.Name = "tvServer";
+ this.tvServer.SelectedImageIndex = 0;
+ this.tvServer.Size = new System.Drawing.Size(208, 346);
+ this.tvServer.TabIndex = 0;
+ this.tvServer.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvServer_BeforeExpand);
+ this.tvServer.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvServer_AfterSelect);
+ this.tvServer.NodeMouseDoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.tvServer_NodeMouseDoubleClick);
+ this.tvServer.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tvServer_KeyDown);
+ //
+ // gbConnection
+ //
+ this.gbConnection.Controls.Add(this.btnSecurityOptions);
+ this.gbConnection.Controls.Add(this.btnDisconnect);
+ this.gbConnection.Controls.Add(this.btnConnect);
+ this.gbConnection.Controls.Add(this.txtServerUrl);
+ this.gbConnection.Controls.Add(this.lblServerUrl);
+ this.gbConnection.Location = new System.Drawing.Point(12, 12);
+ this.gbConnection.Name = "gbConnection";
+ this.gbConnection.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
+ this.gbConnection.Size = new System.Drawing.Size(710, 65);
+ this.gbConnection.TabIndex = 0;
+ this.gbConnection.TabStop = false;
+ this.gbConnection.Text = "Connection";
+ //
+ // btnSecurityOptions
+ //
+ this.btnSecurityOptions.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnSecurityOptions.Image = ((System.Drawing.Image)(resources.GetObject("btnSecurityOptions.Image")));
+ this.btnSecurityOptions.Location = new System.Drawing.Point(674, 31);
+ this.btnSecurityOptions.Name = "btnSecurityOptions";
+ this.btnSecurityOptions.Size = new System.Drawing.Size(23, 23);
+ this.btnSecurityOptions.TabIndex = 4;
+ this.toolTip.SetToolTip(this.btnSecurityOptions, "Security Options");
+ this.btnSecurityOptions.UseVisualStyleBackColor = true;
+ this.btnSecurityOptions.Click += new System.EventHandler(this.btnSecurityOptions_Click);
+ //
+ // btnDisconnect
+ //
+ this.btnDisconnect.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnDisconnect.Image = ((System.Drawing.Image)(resources.GetObject("btnDisconnect.Image")));
+ this.btnDisconnect.Location = new System.Drawing.Point(645, 31);
+ this.btnDisconnect.Name = "btnDisconnect";
+ this.btnDisconnect.Size = new System.Drawing.Size(23, 23);
+ this.btnDisconnect.TabIndex = 3;
+ this.toolTip.SetToolTip(this.btnDisconnect, "Disconnect from Server");
+ this.btnDisconnect.UseVisualStyleBackColor = true;
+ this.btnDisconnect.Click += new System.EventHandler(this.btnDisconnect_Click);
+ //
+ // btnConnect
+ //
+ this.btnConnect.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
+ this.btnConnect.Image = ((System.Drawing.Image)(resources.GetObject("btnConnect.Image")));
+ this.btnConnect.Location = new System.Drawing.Point(616, 31);
+ this.btnConnect.Name = "btnConnect";
+ this.btnConnect.Size = new System.Drawing.Size(23, 23);
+ this.btnConnect.TabIndex = 2;
+ this.toolTip.SetToolTip(this.btnConnect, "Connect to Server");
+ this.btnConnect.UseVisualStyleBackColor = true;
+ this.btnConnect.Click += new System.EventHandler(this.btnConnect_ClickAsync);
+ //
+ // txtServerUrl
+ //
+ this.txtServerUrl.Location = new System.Drawing.Point(13, 32);
+ this.txtServerUrl.Name = "txtServerUrl";
+ this.txtServerUrl.Size = new System.Drawing.Size(597, 20);
+ this.txtServerUrl.TabIndex = 1;
+ this.txtServerUrl.TextChanged += new System.EventHandler(this.txtServerUrl_TextChanged);
+ //
+ // lblServerUrl
+ //
+ this.lblServerUrl.AutoSize = true;
+ this.lblServerUrl.Location = new System.Drawing.Point(10, 16);
+ this.lblServerUrl.Name = "lblServerUrl";
+ this.lblServerUrl.Size = new System.Drawing.Size(63, 13);
+ this.lblServerUrl.TabIndex = 0;
+ this.lblServerUrl.Text = "Server URL";
+ //
+ // gbEmptyItem
+ //
+ this.gbEmptyItem.Controls.Add(this.lblEmptyItem);
+ this.gbEmptyItem.Location = new System.Drawing.Point(492, 83);
+ this.gbEmptyItem.Name = "gbEmptyItem";
+ this.gbEmptyItem.Padding = new System.Windows.Forms.Padding(10, 3, 10, 10);
+ this.gbEmptyItem.Size = new System.Drawing.Size(230, 407);
+ this.gbEmptyItem.TabIndex = 3;
+ this.gbEmptyItem.TabStop = false;
+ this.gbEmptyItem.Text = "Item Parameters";
+ //
+ // lblEmptyItem
+ //
+ this.lblEmptyItem.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
+ this.lblEmptyItem.ForeColor = System.Drawing.SystemColors.GrayText;
+ this.lblEmptyItem.Location = new System.Drawing.Point(13, 16);
+ this.lblEmptyItem.Name = "lblEmptyItem";
+ this.lblEmptyItem.Size = new System.Drawing.Size(204, 50);
+ this.lblEmptyItem.TabIndex = 0;
+ this.lblEmptyItem.Text = "Item not selected";
+ this.lblEmptyItem.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // ctrlCommand
+ //
+ this.ctrlCommand.CommandConfig = null;
+ this.ctrlCommand.Location = new System.Drawing.Point(492, 83);
+ this.ctrlCommand.Name = "ctrlCommand";
+ this.ctrlCommand.Size = new System.Drawing.Size(230, 407);
+ this.ctrlCommand.TabIndex = 6;
+ this.ctrlCommand.ObjectChanged += new Scada.UI.ObjectChangedEventHandler(this.ctrlItem_ObjectChanged);
+ //
+ // ctrlItem
+ //
+ this.ctrlItem.ItemConfig = null;
+ this.ctrlItem.Location = new System.Drawing.Point(492, 83);
+ this.ctrlItem.Name = "ctrlItem";
+ this.ctrlItem.Size = new System.Drawing.Size(230, 407);
+ this.ctrlItem.TabIndex = 5;
+ this.ctrlItem.ObjectChanged += new Scada.UI.ObjectChangedEventHandler(this.ctrlItem_ObjectChanged);
+ //
+ // ctrlSubscription
+ //
+ this.ctrlSubscription.Location = new System.Drawing.Point(492, 83);
+ this.ctrlSubscription.Name = "ctrlSubscription";
+ this.ctrlSubscription.Size = new System.Drawing.Size(230, 407);
+ this.ctrlSubscription.SubscriptionConfig = null;
+ this.ctrlSubscription.TabIndex = 4;
+ this.ctrlSubscription.ObjectChanged += new Scada.UI.ObjectChangedEventHandler(this.ctrlItem_ObjectChanged);
+ //
+ // FrmConfig
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.btnClose;
+ this.ClientSize = new System.Drawing.Size(734, 531);
+ this.Controls.Add(this.ctrlCommand);
+ this.Controls.Add(this.ctrlItem);
+ this.Controls.Add(this.ctrlSubscription);
+ this.Controls.Add(this.gbEmptyItem);
+ this.Controls.Add(this.btnClose);
+ this.Controls.Add(this.btnSave);
+ this.Controls.Add(this.gbDevice);
+ this.Controls.Add(this.gbServerBrowse);
+ this.Controls.Add(this.gbConnection);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "FrmConfig";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "OPC UA - Device {0} Properties";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmConfig_FormClosing);
+ this.Load += new System.EventHandler(this.FrmConfig_Load);
+ this.gbDevice.ResumeLayout(false);
+ this.gbServerBrowse.ResumeLayout(false);
+ this.gbConnection.ResumeLayout(false);
+ this.gbConnection.PerformLayout();
+ this.gbEmptyItem.ResumeLayout(false);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+ private System.Windows.Forms.Button btnClose;
+ private System.Windows.Forms.Button btnSave;
+ private System.Windows.Forms.GroupBox gbDevice;
+ private System.Windows.Forms.GroupBox gbServerBrowse;
+ private System.Windows.Forms.GroupBox gbConnection;
+ private System.Windows.Forms.TextBox txtServerUrl;
+ private System.Windows.Forms.Label lblServerUrl;
+ private System.Windows.Forms.Button btnConnect;
+ private System.Windows.Forms.Button btnSecurityOptions;
+ private System.Windows.Forms.Button btnDisconnect;
+ private System.Windows.Forms.ToolTip toolTip;
+ private System.Windows.Forms.TreeView tvServer;
+ private System.Windows.Forms.ImageList ilTree;
+ private System.Windows.Forms.Button btnAddItem;
+ private System.Windows.Forms.Button btnDeleteItem;
+ private System.Windows.Forms.Button btnMoveDownItem;
+ private System.Windows.Forms.Button btnMoveUpItem;
+ private System.Windows.Forms.Button btnAddSubscription;
+ private System.Windows.Forms.TreeView tvDevice;
+ private CtrlSubscription ctrlSubscription;
+ private System.Windows.Forms.GroupBox gbEmptyItem;
+ private System.Windows.Forms.Label lblEmptyItem;
+ private CtrlItem ctrlItem;
+ private CtrlCommand ctrlCommand;
+ private System.Windows.Forms.Button btnViewAttrs;
+ }
+}
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.cs
new file mode 100644
index 000000000..49d577070
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.cs
@@ -0,0 +1,985 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Device configuration form
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Opc.Ua;
+using Opc.Ua.Client;
+using Scada.Comm.Devices.OpcUa.Config;
+using Scada.UI;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ ///
+ /// Device configuration form.
+ /// Форма настройки конфигурации КП.
+ ///
+ public partial class FrmConfig : Form
+ {
+ ///
+ /// Represents an object associated with a node of the server tree.
+ ///
+ private class ServerNodeTag
+ {
+ public ServerNodeTag(ReferenceDescription rd, NamespaceTable namespaceTable)
+ {
+ DisplayName = rd.DisplayName.Text;
+ OpcNodeId = ExpandedNodeId.ToNodeId(rd.NodeId, namespaceTable);
+ NodeClass = rd.NodeClass;
+ DataType = null;
+ IsFilled = false;
+ }
+
+ public string DisplayName { get; private set; }
+ public NodeId OpcNodeId { get; private set; }
+ public NodeClass NodeClass { get; private set; }
+ public Type DataType { get; private set; }
+ public bool IsFilled { get; set; }
+ }
+
+ ///
+ /// Represents an object associated with a monitored item configuration.
+ ///
+ internal class ItemConfigTag
+ {
+ public ItemConfigTag(int signal, bool isArray, int arrayLen)
+ {
+ Signal = signal;
+ SetLength(isArray, arrayLen);
+ }
+
+ public int Signal { get; set; }
+ public int Length { get; set; }
+
+ public void SetLength(bool isArray, int arrayLen)
+ {
+ Length = arrayLen > 1 ? arrayLen : 1;
+ }
+ public string GetSignalStr()
+ {
+ return Length > 1 ?
+ Signal + " - " + (Signal + Length - 1) :
+ Signal.ToString();
+ }
+ }
+
+ private const string FolderOpenImageKey = "folder_open.png";
+ private const string FolderClosedImageKey = "folder_closed.png";
+
+ private static readonly Dictionary KnownTypes = new Dictionary
+ {
+ { "byte", typeof(byte) },
+ { "double", typeof(double) },
+ { "Int16", typeof(Int16) },
+ { "int32", typeof(Int32) },
+ { "int64", typeof(Int64) },
+ { "sbyte", typeof(SByte) },
+ { "float", typeof(Single) },
+ { "uint16", typeof(UInt16) },
+ { "uint32", typeof(UInt32) },
+ { "uint64", typeof(UInt64) },
+ };
+
+ private readonly AppDirs appDirs; // the application directories
+ private readonly int kpNum; // the device number
+ private readonly DeviceConfig deviceConfig; // the device configuration
+ private string configFileName; // the configuration file name
+ private bool modified; // the configuration was modified
+ private bool changing; // controls are being changed programmatically
+ private int? maxCmdNum; // the maximum command number
+ private Session opcSession; // the OPC session
+ private TreeNode subscriptionsNode; // the tree node of the subscriptions
+ private TreeNode commandsNode; // the tree node of the commands
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ private FrmConfig()
+ {
+ InitializeComponent();
+ ctrlSubscription.Visible = false;
+ ctrlItem.Visible = false;
+ ctrlCommand.Visible = false;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public FrmConfig(AppDirs appDirs, int kpNum)
+ : this()
+ {
+ this.appDirs = appDirs ?? throw new ArgumentNullException("appDirs");
+ this.kpNum = kpNum;
+ deviceConfig = new DeviceConfig();
+ configFileName = "";
+ modified = false;
+ changing = false;
+ maxCmdNum = null;
+ opcSession = null;
+ subscriptionsNode = null;
+ commandsNode = null;
+ }
+
+
+ ///
+ /// Gets or sets a value indicating whether the configuration was modified.
+ ///
+ private bool Modified
+ {
+ get
+ {
+ return modified;
+ }
+ set
+ {
+ modified = value;
+ btnSave.Enabled = modified;
+ }
+ }
+
+
+ ///
+ /// Sets the controls according to the configuration.
+ ///
+ private void ConfigToControls()
+ {
+ changing = true;
+ txtServerUrl.Text = deviceConfig.ConnectionOptions.ServerUrl;
+ FillDeviceTree();
+ changing = false;
+ }
+
+ ///
+ /// Fills the device tree.
+ ///
+ private void FillDeviceTree()
+ {
+ try
+ {
+ tvDevice.BeginUpdate();
+ tvDevice.Nodes.Clear();
+
+ subscriptionsNode = TreeViewUtils.CreateNode(KpPhrases.SubscriptionsNode, FolderClosedImageKey);
+ commandsNode = TreeViewUtils.CreateNode(KpPhrases.CommandsNode, FolderClosedImageKey);
+ int signal = 1;
+
+ foreach (SubscriptionConfig subscriptionConfig in deviceConfig.Subscriptions)
+ {
+ TreeNode subscriptionNode = CreateSubscriptionNode(subscriptionConfig);
+ subscriptionsNode.Nodes.Add(subscriptionNode);
+
+ foreach (ItemConfig itemConfig in subscriptionConfig.Items)
+ {
+ subscriptionNode.Nodes.Add(CreateItemNode(itemConfig));
+
+ ItemConfigTag tag = new ItemConfigTag(signal, itemConfig.IsArray, itemConfig.ArrayLen);
+ signal += tag.Length;
+ itemConfig.Tag = tag;
+ }
+ }
+
+ foreach (CommandConfig commandConfig in deviceConfig.Commands)
+ {
+ commandsNode.Nodes.Add(CreateCommandNode(commandConfig));
+ }
+
+ tvDevice.Nodes.Add(subscriptionsNode);
+ tvDevice.Nodes.Add(commandsNode);
+ subscriptionsNode.Expand();
+ commandsNode.Expand();
+ }
+ finally
+ {
+ tvDevice.EndUpdate();
+ }
+ }
+
+ ///
+ /// Creates a new subscription node according to the subscription configuration.
+ ///
+ private TreeNode CreateSubscriptionNode(SubscriptionConfig subscriptionConfig)
+ {
+ TreeNode subscriptionNode = TreeViewUtils.CreateNode(
+ GetDisplayName(subscriptionConfig.DisplayName, KpPhrases.EmptySubscription),
+ FolderClosedImageKey);
+ subscriptionNode.Tag = subscriptionConfig;
+ return subscriptionNode;
+ }
+
+ ///
+ /// Creates a new monitored item node according to the item configuration.
+ ///
+ private TreeNode CreateItemNode(ItemConfig itemConfig)
+ {
+ TreeNode itemNode = TreeViewUtils.CreateNode(
+ GetDisplayName(itemConfig.DisplayName, KpPhrases.EmptyItem),
+ "variable.png");
+ itemNode.Tag = itemConfig;
+ return itemNode;
+ }
+
+ ///
+ /// Creates a new command node according to the command configuration.
+ ///
+ private TreeNode CreateCommandNode(CommandConfig commandConfig)
+ {
+ TreeNode commandNode = TreeViewUtils.CreateNode(
+ GetDisplayName(commandConfig.DisplayName, KpPhrases.EmptyCommand),
+ "command.png");
+ commandNode.Tag = commandConfig;
+ return commandNode;
+ }
+
+ ///
+ /// Returns the specified display name or the default name.
+ ///
+ private string GetDisplayName(string displayName, string defaultName)
+ {
+ return string.IsNullOrEmpty(displayName) ? defaultName : displayName;
+ }
+
+ ///
+ /// Connects to the OPC server.
+ ///
+ private async Task ConnectToOpcServer()
+ {
+ try
+ {
+ OpcUaHelper helper = new OpcUaHelper(appDirs, kpNum, OpcUaHelper.RuntimeKind.View)
+ {
+ CertificateValidation = CertificateValidator_CertificateValidation
+ };
+
+ if (await helper.ConnectAsync(deviceConfig.ConnectionOptions))
+ {
+ opcSession = helper.OpcSession;
+ return true;
+ }
+ else
+ {
+ opcSession = null;
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ ScadaUiUtils.ShowError(KpPhrases.ConnectServerError + ":" + Environment.NewLine + ex.Message);
+ return false;
+ }
+ finally
+ {
+ SetConnButtonsEnabled();
+ }
+ }
+
+ ///
+ /// Disconnects from the OPC server.
+ ///
+ private void DisconnectFromOpcServer()
+ {
+ try
+ {
+ tvServer.Nodes.Clear();
+ btnViewAttrs.Enabled = false;
+ btnAddItem.Enabled = false;
+ subscriptionsNode = null;
+ commandsNode = null;
+
+ if (opcSession != null)
+ {
+ opcSession.Close();
+ opcSession = null;
+ }
+ }
+ catch (Exception ex)
+ {
+ ScadaUiUtils.ShowError(KpPhrases.DisconnectServerError + ":" + Environment.NewLine + ex.Message);
+ }
+ finally
+ {
+ SetConnButtonsEnabled();
+ }
+ }
+
+ ///
+ /// Browses the server node.
+ ///
+ private void BrowseServerNode(TreeNode treeNode)
+ {
+ try
+ {
+ tvServer.BeginUpdate();
+ bool fillNode = false;
+ TreeNodeCollection nodeCollection = null;
+ NodeId nodeId = null;
+
+ if (treeNode == null)
+ {
+ fillNode = true;
+ nodeCollection = tvServer.Nodes;
+ nodeId = ObjectIds.ObjectsFolder;
+ }
+ else if (treeNode.Tag is ServerNodeTag serverNodeTag)
+ {
+ fillNode = !serverNodeTag.IsFilled;
+ nodeCollection = treeNode.Nodes;
+ nodeId = serverNodeTag.OpcNodeId;
+ }
+
+ if (fillNode && nodeId != null && opcSession != null)
+ {
+ opcSession.Browse(null, null, nodeId,
+ 0, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true,
+ (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method,
+ out byte[] continuationPoint, out ReferenceDescriptionCollection references);
+ nodeCollection.Clear();
+
+ foreach (ReferenceDescription rd in references)
+ {
+ TreeNode childNode = TreeViewUtils.CreateNode(rd.DisplayName, SelectImageKey(rd.NodeClass));
+ childNode.Tag = new ServerNodeTag(rd, opcSession.NamespaceUris);
+
+ if (rd.NodeClass.HasFlag(NodeClass.Object))
+ {
+ TreeNode emptyNode = TreeViewUtils.CreateNode(KpPhrases.EmptyNode, "empty.png");
+ childNode.Nodes.Add(emptyNode);
+ }
+
+ nodeCollection.Add(childNode);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ ScadaUiUtils.ShowError(KpPhrases.BrowseServerError + ":" + Environment.NewLine + ex.Message);
+ }
+ finally
+ {
+ tvServer.EndUpdate();
+ }
+ }
+
+ ///
+ /// Selects an image key depending on the node class.
+ ///
+ private string SelectImageKey(NodeClass nodeClass)
+ {
+ if (nodeClass.HasFlag(NodeClass.Object))
+ return "object.png";
+ else if (nodeClass.HasFlag(NodeClass.Method))
+ return "method.png";
+ else
+ return "variable.png";
+ }
+
+ ///
+ /// Adds a new item to the configuration.
+ ///
+ private bool AddItem(TreeNode serverNode)
+ {
+ if (serverNode?.Tag is ServerNodeTag serverNodeTag &&
+ serverNodeTag.NodeClass == NodeClass.Variable)
+ {
+ TreeNode deviceNode = tvDevice.SelectedNode;
+ object deviceNodeTag = deviceNode?.Tag;
+
+ if (GetTopParentNode(tvDevice.SelectedNode) == commandsNode)
+ {
+ // add a new command
+ if (GetDataTypeName(serverNodeTag.OpcNodeId, out string dataTypeName))
+ {
+ CommandConfig commandConfig = new CommandConfig
+ {
+ NodeID = serverNodeTag.OpcNodeId.ToString(),
+ DisplayName = serverNodeTag.DisplayName,
+ DataTypeName = dataTypeName,
+ CmdNum = GetNextCmdNum()
+ };
+
+ tvDevice.Insert(commandsNode, CreateCommandNode(commandConfig),
+ deviceConfig.Commands, commandConfig);
+
+ Modified = true;
+ return true;
+ }
+ }
+ else
+ {
+ // create a new monitored item
+ ItemConfig itemConfig = new ItemConfig
+ {
+ NodeID = serverNodeTag.OpcNodeId.ToString(),
+ DisplayName = serverNodeTag.DisplayName,
+ };
+
+ itemConfig.Tag = new ItemConfigTag(0, itemConfig.IsArray, itemConfig.ArrayLen);
+
+ // find a subscription
+ TreeNode subscriptionNode = deviceNode?.FindClosest(typeof(SubscriptionConfig)) ??
+ subscriptionsNode.LastNode;
+ SubscriptionConfig subscriptionConfig;
+
+ // add a new subscription
+ if (subscriptionNode == null)
+ {
+ subscriptionConfig = new SubscriptionConfig();
+ subscriptionNode = CreateSubscriptionNode(subscriptionConfig);
+ tvDevice.Insert(subscriptionsNode, subscriptionNode,
+ deviceConfig.Subscriptions, subscriptionConfig);
+ }
+ else
+ {
+ subscriptionConfig = (SubscriptionConfig)subscriptionNode.Tag;
+ }
+
+ // add the monitored item
+ TreeNode itemNode = CreateItemNode(itemConfig);
+ tvDevice.Insert(subscriptionNode, itemNode, subscriptionConfig.Items, itemConfig);
+ UpdateSignals(itemNode);
+ Modified = true;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Gets the data type name of the node.
+ ///
+ private bool GetDataTypeName(NodeId nodeId, out string dataTypeName)
+ {
+ if (nodeId == null)
+ throw new ArgumentNullException("nodeId");
+ if (opcSession == null)
+ throw new InvalidOperationException("OPC session must not be null.");
+
+ try
+ {
+ ReadValueIdCollection nodesToRead = new ReadValueIdCollection
+ {
+ new ReadValueId
+ {
+ NodeId = nodeId,
+ AttributeId = Attributes.DataType
+ }
+ };
+
+ opcSession.Read(null, 0, TimestampsToReturn.Neither, nodesToRead,
+ out DataValueCollection results, out DiagnosticInfoCollection diagnosticInfos);
+ ClientBase.ValidateResponse(results, nodesToRead);
+ ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
+
+ DataValue dataTypeValue = results[0];
+ INode dataType = opcSession.NodeCache.Find((NodeId)dataTypeValue.Value);
+
+ if (dataType == null)
+ {
+ throw new ScadaException(Localization.UseRussian ?
+ "Не удалось получить тип данных от OPC-сервера." :
+ "Unable to get data type from OPC server.");
+ }
+
+ if (KnownTypes.TryGetValue(dataType.DisplayName.Text.ToLowerInvariant(), out Type type))
+ {
+ dataTypeName = type.FullName;
+ return true;
+ }
+ else
+ {
+ ScadaUiUtils.ShowError(string.Format(KpPhrases.UnknownDataType, dataType.DisplayName.Text));
+ dataTypeName = "";
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ ScadaUiUtils.ShowError(KpPhrases.GetDataTypeError + ":" + Environment.NewLine + ex.Message);
+ dataTypeName = "";
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the next command number.
+ ///
+ private int GetNextCmdNum()
+ {
+ if (maxCmdNum == null)
+ {
+ maxCmdNum = deviceConfig.Commands.Any() ?
+ deviceConfig.Commands.Max(x => x.CmdNum) :
+ 0;
+ }
+
+ return (++maxCmdNum).Value;
+ }
+
+ ///
+ /// Sets the enabled property of the connection buttons.
+ ///
+ private void SetConnButtonsEnabled()
+ {
+ if (opcSession == null)
+ {
+ btnConnect.Enabled = true;
+ btnDisconnect.Enabled = false;
+ }
+ else
+ {
+ btnConnect.Enabled = false;
+ btnDisconnect.Enabled = true;
+ }
+ }
+
+ ///
+ /// Sets the enabled property of the buttons that manipulate the server tree.
+ ///
+ private void SetServerButtonsEnabled()
+ {
+ btnViewAttrs.Enabled = opcSession != null && tvServer.SelectedNode != null;
+ }
+
+ ///
+ /// Sets the enabled property of the buttons that manipulate the device tree.
+ ///
+ private void SetDeviceButtonsEnabled()
+ {
+ ServerNodeTag serverNodeTag = tvServer.SelectedNode?.Tag as ServerNodeTag;
+ bool deviceNodeTagDefined = tvDevice.SelectedNode?.Tag != null;
+
+ btnAddItem.Enabled = serverNodeTag != null && serverNodeTag.NodeClass == NodeClass.Variable;
+ btnMoveUpItem.Enabled = deviceNodeTagDefined && tvDevice.SelectedNode.PrevNode != null;
+ btnMoveDownItem.Enabled = deviceNodeTagDefined && tvDevice.SelectedNode.NextNode != null;
+ btnDeleteItem.Enabled = deviceNodeTagDefined;
+ }
+
+ ///
+ /// Sets the node image as open or closed folder.
+ ///
+ private void SetFolderImage(TreeNode treeNode)
+ {
+ if (treeNode.ImageKey.StartsWith("folder_"))
+ treeNode.SetImageKey(treeNode.IsExpanded ? FolderOpenImageKey : FolderClosedImageKey);
+ }
+
+ ///
+ /// Gets the top parent of the specified node.
+ ///
+ private TreeNode GetTopParentNode(TreeNode treeNode)
+ {
+ if (treeNode == null)
+ {
+ return null;
+ }
+ else
+ {
+ TreeNode parentNode = treeNode.Parent;
+
+ while (parentNode != null)
+ {
+ treeNode = parentNode;
+ parentNode = treeNode.Parent;
+ }
+
+ return treeNode;
+ }
+ }
+
+ ///
+ /// Update signals if 2 elements are reversed.
+ ///
+ private void SwapSignals(TreeNode treeNode1, TreeNode treeNode2)
+ {
+ if (treeNode1?.Tag is ItemConfig itemConfig1 &&
+ treeNode2?.Tag is ItemConfig itemConfig2 &&
+ itemConfig1.Tag is ItemConfigTag itemConfigTag1 &&
+ itemConfig2.Tag is ItemConfigTag itemConfigTag2)
+ {
+ int signal1 = itemConfigTag1.Signal;
+ itemConfigTag1.Signal = itemConfigTag2.Signal;
+ itemConfigTag2.Signal = signal1;
+ ctrlItem.ShowSignal();
+ }
+ }
+
+ ///
+ /// Update signals starting from the specified node.
+ ///
+ private void UpdateSignals(TreeNode startNode)
+ {
+ TreeNode startSubscrNode = startNode?.FindClosest(typeof(SubscriptionConfig));
+
+ if (startSubscrNode != null)
+ {
+ // define initial signal
+ int signal = 1;
+ TreeNode subscrNode = startSubscrNode.PrevNode;
+
+ while (subscrNode != null)
+ {
+ if (subscrNode.LastNode?.Tag is ItemConfig itemConfig &&
+ itemConfig.Tag is ItemConfigTag tag)
+ {
+ signal = tag.Signal + tag.Length;
+ break;
+ }
+
+ subscrNode = subscrNode.PrevNode;
+ }
+
+ // recalculate signals
+ subscrNode = startSubscrNode;
+
+ while (subscrNode != null)
+ {
+ foreach (TreeNode itemNode in subscrNode.Nodes)
+ {
+ if (itemNode.Tag is ItemConfig itemConfig &&
+ itemConfig.Tag is ItemConfigTag tag)
+ {
+ tag.Signal = signal;
+ signal += tag.Length;
+ }
+ }
+
+ subscrNode = subscrNode.NextNode;
+ }
+
+ ctrlItem.ShowSignal();
+ }
+ }
+
+ ///
+ /// Validates the certificate.
+ ///
+ private void CertificateValidator_CertificateValidation(CertificateValidator validator,
+ CertificateValidationEventArgs e)
+ {
+ if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
+ e.Accept = true;
+ }
+
+
+ private void FrmConfig_Load(object sender, EventArgs e)
+ {
+ // translate the form
+ if (Localization.LoadDictionaries(appDirs.LangDir, "KpOpcUa", out string errMsg))
+ Translator.TranslateForm(this, GetType().FullName, toolTip);
+ else
+ ScadaUiUtils.ShowError(errMsg);
+
+ Text = string.Format(Text, kpNum);
+ KpPhrases.Init();
+
+ // load a configuration
+ configFileName = DeviceConfig.GetFileName(appDirs.ConfigDir, kpNum);
+
+ if (File.Exists(configFileName) && !deviceConfig.Load(configFileName, out errMsg))
+ ScadaUiUtils.ShowError(errMsg);
+
+ // display the configuration
+ ConfigToControls();
+ SetConnButtonsEnabled();
+ SetServerButtonsEnabled();
+ SetDeviceButtonsEnabled();
+ Modified = false;
+ }
+
+ private void FrmConfig_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ if (Modified)
+ {
+ DialogResult result = MessageBox.Show(CommPhrases.SaveKpSettingsConfirm,
+ CommonPhrases.QuestionCaption, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
+
+ switch (result)
+ {
+ case DialogResult.Yes:
+ if (!deviceConfig.Save(configFileName, out string errMsg))
+ {
+ ScadaUiUtils.ShowError(errMsg);
+ e.Cancel = true;
+ }
+ break;
+ case DialogResult.No:
+ break;
+ default:
+ e.Cancel = true;
+ break;
+ }
+ }
+ }
+
+ private async void btnConnect_ClickAsync(object sender, EventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(deviceConfig.ConnectionOptions.ServerUrl))
+ ScadaUiUtils.ShowError(KpPhrases.ServerUrlRequired);
+ else if (await ConnectToOpcServer())
+ BrowseServerNode(null);
+ }
+
+ private void txtServerUrl_TextChanged(object sender, EventArgs e)
+ {
+ if (!changing)
+ {
+ deviceConfig.ConnectionOptions.ServerUrl = txtServerUrl.Text;
+ Modified = true;
+ }
+ }
+
+ private void btnDisconnect_Click(object sender, EventArgs e)
+ {
+ DisconnectFromOpcServer();
+ }
+
+ private void btnSecurityOptions_Click(object sender, EventArgs e)
+ {
+ if (new FrmSecurityOptions(deviceConfig.ConnectionOptions).ShowDialog() == DialogResult.OK)
+ Modified = true;
+ }
+
+ private void btnViewAttrs_Click(object sender, EventArgs e)
+ {
+ if (opcSession != null &&
+ tvServer.SelectedNode?.Tag is ServerNodeTag serverNodeTag)
+ {
+ new FrmNodeAttr(opcSession, serverNodeTag.OpcNodeId).ShowDialog();
+ }
+ }
+
+ private void tvServer_AfterSelect(object sender, TreeViewEventArgs e)
+ {
+ SetServerButtonsEnabled();
+ SetDeviceButtonsEnabled();
+ }
+
+ private void tvServer_BeforeExpand(object sender, TreeViewCancelEventArgs e)
+ {
+ BrowseServerNode(e.Node);
+ }
+
+ private void tvServer_KeyDown(object sender, KeyEventArgs e)
+ {
+ TreeNode selectedNode = tvServer.SelectedNode;
+
+ if (e.KeyCode == Keys.Enter && AddItem(selectedNode))
+ {
+ // go to the next node
+ if (selectedNode.NextNode != null)
+ tvServer.SelectedNode = selectedNode.NextNode;
+ else if (selectedNode.Parent?.NextNode != null)
+ tvServer.SelectedNode = selectedNode.Parent.NextNode;
+ }
+ }
+
+ private void tvServer_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
+ {
+ if (e.Button == MouseButtons.Left)
+ AddItem(tvServer.SelectedNode);
+ }
+
+ private void btnAddItem_Click(object sender, EventArgs e)
+ {
+ AddItem(tvServer.SelectedNode);
+ }
+
+ private void btnAddSubscription_Click(object sender, EventArgs e)
+ {
+ // add a new subscription
+ SubscriptionConfig subscriptionConfig = new SubscriptionConfig();
+ TreeNode subscriptionNode = CreateSubscriptionNode(subscriptionConfig);
+ tvDevice.Insert(subscriptionsNode, subscriptionNode,
+ deviceConfig.Subscriptions, subscriptionConfig);
+ ctrlSubscription.SetFocus();
+ Modified = true;
+ }
+
+ private void btnMoveUpItem_Click(object sender, EventArgs e)
+ {
+ // move up the selected item
+ TreeNode selectedNode = tvDevice.SelectedNode;
+ object deviceNodeTag = selectedNode?.Tag;
+
+ if (deviceNodeTag is SubscriptionConfig)
+ {
+ tvDevice.MoveUpSelectedNode(deviceConfig.Subscriptions);
+ UpdateSignals(selectedNode);
+ }
+ else if (deviceNodeTag is ItemConfig)
+ {
+ if (selectedNode.Parent.Tag is SubscriptionConfig subscriptionConfig)
+ {
+ tvDevice.MoveUpSelectedNode(subscriptionConfig.Items);
+ SwapSignals(selectedNode, selectedNode.NextNode);
+ }
+ }
+ else if (deviceNodeTag is CommandConfig)
+ {
+ tvDevice.MoveUpSelectedNode(deviceConfig.Commands);
+ }
+
+ Modified = true;
+ }
+
+ private void btnMoveDownItem_Click(object sender, EventArgs e)
+ {
+ // move down the selected item
+ TreeNode selectedNode = tvDevice.SelectedNode;
+ object deviceNodeTag = tvDevice.SelectedNode?.Tag;
+
+ if (deviceNodeTag is SubscriptionConfig)
+ {
+ tvDevice.MoveDownSelectedNode(deviceConfig.Subscriptions);
+ UpdateSignals(selectedNode);
+ }
+ else if (deviceNodeTag is ItemConfig)
+ {
+ if (selectedNode.Parent.Tag is SubscriptionConfig subscriptionConfig)
+ {
+ tvDevice.MoveDownSelectedNode(subscriptionConfig.Items);
+ SwapSignals(selectedNode, selectedNode.PrevNode);
+ }
+ }
+ else if (deviceNodeTag is CommandConfig)
+ {
+ tvDevice.MoveDownSelectedNode(deviceConfig.Commands);
+ }
+
+ Modified = true;
+ }
+
+ private void btnDeleteItem_Click(object sender, EventArgs e)
+ {
+ // delete the selected item
+ TreeNode selectedNode = tvDevice.SelectedNode;
+ object deviceNodeTag = selectedNode?.Tag;
+
+ if (deviceNodeTag is SubscriptionConfig)
+ {
+ TreeNode nextSubscrNode = selectedNode.NextNode;
+ tvDevice.RemoveNode(selectedNode, deviceConfig.Subscriptions);
+ UpdateSignals(nextSubscrNode);
+ }
+ else if (deviceNodeTag is ItemConfig)
+ {
+ if (selectedNode.Parent.Tag is SubscriptionConfig subscriptionConfig)
+ {
+ TreeNode subscrNode = selectedNode.Parent;
+ tvDevice.RemoveNode(selectedNode, subscriptionConfig.Items);
+ UpdateSignals(subscrNode);
+ }
+ }
+ else if (deviceNodeTag is CommandConfig)
+ {
+ tvDevice.RemoveNode(selectedNode, deviceConfig.Commands);
+ maxCmdNum = null; // need to recalculate maximum command number
+ }
+
+ Modified = true;
+ }
+
+ private void tvDevice_AfterSelect(object sender, TreeViewEventArgs e)
+ {
+ SetDeviceButtonsEnabled();
+
+ // show parameters of the selected item
+ gbEmptyItem.Visible = false;
+ ctrlSubscription.Visible = false;
+ ctrlItem.Visible = false;
+ ctrlCommand.Visible = false;
+ object deviceNodeTag = e.Node?.Tag;
+
+ if (deviceNodeTag is SubscriptionConfig subscriptionConfig)
+ {
+ ctrlSubscription.SubscriptionConfig = subscriptionConfig;
+ ctrlSubscription.Visible = true;
+ }
+ else if (deviceNodeTag is ItemConfig itemConfig)
+ {
+ ctrlItem.ItemConfig = itemConfig;
+ ctrlItem.Visible = true;
+ }
+ else if (deviceNodeTag is CommandConfig commandConfig)
+ {
+ ctrlCommand.CommandConfig = commandConfig;
+ ctrlCommand.Visible = true;
+ }
+ else
+ {
+ gbEmptyItem.Visible = true;
+ }
+ }
+
+ private void tvDevice_AfterExpand(object sender, TreeViewEventArgs e)
+ {
+ SetFolderImage(e.Node);
+ }
+
+ private void tvDevice_AfterCollapse(object sender, TreeViewEventArgs e)
+ {
+ SetFolderImage(e.Node);
+ }
+
+ private void ctrlItem_ObjectChanged(object sender, ObjectChangedEventArgs e)
+ {
+ Modified = true;
+ TreeNode selectedNode = tvDevice.SelectedNode;
+ TreeUpdateTypes treeUpdateTypes = (TreeUpdateTypes)e.ChangeArgument;
+
+ if (e.ChangedObject is SubscriptionConfig subscriptionConfig)
+ {
+ if (treeUpdateTypes.HasFlag(TreeUpdateTypes.CurrentNode))
+ selectedNode.Text = GetDisplayName(subscriptionConfig.DisplayName, KpPhrases.EmptySubscription);
+ }
+ else if (e.ChangedObject is ItemConfig itemConfig)
+ {
+ if (treeUpdateTypes.HasFlag(TreeUpdateTypes.CurrentNode))
+ selectedNode.Text = GetDisplayName(itemConfig.DisplayName, KpPhrases.EmptyItem);
+
+ if (treeUpdateTypes.HasFlag(TreeUpdateTypes.UpdateSignals))
+ UpdateSignals(selectedNode);
+ }
+ else if (e.ChangedObject is CommandConfig commandConfig)
+ {
+ if (treeUpdateTypes.HasFlag(TreeUpdateTypes.CurrentNode))
+ selectedNode.Text = GetDisplayName(commandConfig.DisplayName, KpPhrases.EmptyCommand);
+ }
+ }
+
+ private void btnSave_Click(object sender, EventArgs e)
+ {
+ if (deviceConfig.Save(configFileName, out string errMsg))
+ Modified = false;
+ else
+ ScadaUiUtils.ShowError(errMsg);
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.resx b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.resx
new file mode 100644
index 000000000..eab561227
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmConfig.resx
@@ -0,0 +1,379 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 107, 17
+
+
+
+ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
+ LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+ ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACQ
+ HgAAAk1TRnQBSQFMAgEBBwEAAVABAQFQAQEBEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA
+ AwABIAMAAQEBAAEgBgABIHoAAwcBCQP3Af8D9wH/AwcBCewAAwcBCQP3Af8BRgGqAdYB/wFGAaoB1gH/
+ A/cB/wMHAQmYAAP+Af8D+AH/A/YB/wP4Af8D/gH/OAADBwEJA/cB/wFGAaoB1gH/AQABVgHCAf8BAAFW
+ AcIB/wFGAaoB1gH/A/cB/wMHAQmQAAP6Af8B8AHpAe0B/wG9AYUBpAH/AZABAwE7Af8BvQGFAaQB/wHw
+ AekB7QH/A/oB/zAAA/YB/wP2Af8D9gH/AUYBqgHWAf8BAAFWAcIB/wEAAVYBwgH/AQABVgHCAf8BRgGq
+ AdYB/wP3Af8UAAP5Af8D9gH/A/YB/wP5Af9oAAP4Af8B1gG3AckB/wGdARwBTQH/AbQBTAGZAf8BkAED
+ ATsB/wG0AUwBmQH/AZ0BHAFNAf8B1gG3AckB/wP4Af8sAAP2Af8BAAFWAcIB/wEAAVYBwgH/AQABVgHC
+ Af8BAAFWAcIB/wEAAVYBwgH/AQABVgHCAf8BRgGqAdYB/wP3Af8MAAP5Af8D9gH/Ad0ByAGxAf8BrwFP
+ AQ4B/wGtAUsBBwH/AdoBwwGpAf8B9gL1Af8D+QH/XAAD9gH/AbABQgGSAf8BnAEbAUwB/wHZAb8BzQH/
+ AfEB7wHwAf8BkAEDATsB/wHxAe8B8AH/AdkBvwHNAf8BnAEbAUwB/wGwAUIBkgH/A/YB/xgAAwcBCQMH
+ AQkIAAP2Af8BAAFWAcIB/wP2Af8D9gH/A/YB/wFGAaoB1gH/AUYBqgHWAf8D9wH/AwcBCQgAA/YB/wHd
+ AcgBsQH/Aa8BTwEOAf8BnAEsAQAB/wGcASwBAAH/AZwBLAEAAf8BnAEsAQAB/wGtAUsBBwH/AdoBwwGp
+ Af8B9gL1Af8D+QH/VAAD9gH/AZABAwE7Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8BkAEDATsB/wHx
+ Ae8B8AH/AfEB7wHwAf8B8QHvAfAB/wGQAQMBOwH/A/YB/xQAAwcBCQP3Af8D9wH/AwcBCQQAA/YB/wEA
+ AVYBwgH/A/YB/wP2Af8BRgGqAdYB/wFGAaoB1gH/A/YB/wMgAS4MAAP2Af8BnAEsAQAB/wGcASwBAAH/
+ AZwBLAEAAf8BnAEsAQAB/wGcASwBAAH/AZwBLAEAAf8BnAEsAQAB/wGcASwBAAH/Aa0BSwEHAf8B2gHD
+ AakB/wP2Af9QAAP2Af8BkAEDATsB/wHxAe8B8AH/AfEB7wHwAf8B3wHLAdYB/wGQAQMBOwH/Ad8BywHW
+ Af8B8QHvAfAB/wHxAe8B8AH/AZABAwE7Af8D9gH/EAADBwEJA/cB/wFGAaoB1gH/AUYBqgHWAf8D9wH/
+ AwcBCQP2Af8BAAFWAcIB/wP2Af8BRgGqAdYB/wEAAVYBwgH/AQABVgHCAf8BRgGqAdYB/wP3Af8DBwEJ
+ CAAD9gH/AZwBLAEAAf8BnAEsAQAB/wGcASwBAAH/AZwBLAEAAf8BnAEsAQAB/wGcASwBAAH/AZwBLAEA
+ Af8BnAEsAQAB/wGcASwBAAH/AZwBLAEAAf8D9gH/UAAD9gH/AZABAwE7Af8B6wHjAecB/wHBAY4BqwH/
+ AbQBTAGZAf8B5QHXAd8B/wG0AUwBmQH/AcEBjgGrAf8B6wHjAecB/wGQAQMBOwH/A/YB/wwAAwcBCQP3
+ Af8BRgGqAdYB/wEAAVYBwgH/AQABVgHCAf8BRgGqAdYB/wP2Af8D9gH/AQABVgHCAf8D9gH/AUYBqgHW
+ Af8BAAFWAcIB/wEAAVYBwgH/AQABVgHCAf8BRgGqAdYB/wP3Af8IAAP2Af8BnAEsAQAB/wGcASwBAAH/
+ AZwBLAEAAf8BnAEsAQAB/wGcASwBAAH/AZwBLAEAAf8BnAEsAQAB/wGcASwBAAH/AZwBLAEAAf8BnAEs
+ AQAB/wP2Af9QAAP2Af8BkAEDATsB/wGoATQBiAH/AdkBvwHNAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw
+ Af8B2QG/Ac0B/wGoATQBiAH/AZABAwE7Af8D9gH/DAAD9wH/AUYBqgHWAf8BAAFWAcIB/wEAAVYBwgH/
+ AQABVgHCAf8BAAFWAcIB/wEAAVYBwgH/AQABVgHCAf8BAAFWAcIB/wEAAVYBwgH/AQABVgHCAf8BAAFW
+ AcIB/wEAAVYBwgH/AQABVgHCAf8BRgGqAdYB/wP3Af8IAAP2Af8BnAEsAQAB/wGcASwBAAH/AcABlQE+
+ Af8B7AHmAeEB/wHtAegB5AH/AcQBnAFJAf8BnAEtAQAB/wGcASwBAAH/AZwBLAEAAf8BnAEsAQAB/wP2
+ Af9QAAP2Af8BsAFCAZIB/wGcARsBTAH/AdkBvwHNAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B2QG/
+ Ac0B/wGcARsBTAH/AbABQgGSAf8D9gH/DAAD9wH/AUYBqgHWAf8BAAFWAcIB/wEAAVYBwgH/AQABVgHC
+ Af8BAAFWAcIB/wEAAVYBwgH/AUYBqgHWAf8D9gH/A/YB/wP2Af8D9gH/AUYBqgHWAf8BRgGqAdYB/wP3
+ Af8DBwEJCAAD9gH/Ad0ByAGxAf8BrwFPAQ4B/wHAAZUBPgH/AewB5gHhAf8B8QHvAfAB/wHxAe8B8AH/
+ Ae0B6AHkAf8BxAGcAUkB/wGcASwBAAH/AZwBLAEAAf8D9gH/VAAD+AH/AdYBtwHJAf8BnQEcAU0B/wG0
+ AUwBmQH/AeUB1wHfAf8BtAFMAZkB/wGdARwBTQH/AdYBtwHJAf8D+AH/EAADBwEJA/cB/wFGAaoB1gH/
+ AQABVgHCAf8BAAFWAcIB/wEAAVYBwgH/AQABVgHCAf8BAAFWAcIB/wFGAaoB1gH/A/cB/wMHAQkDBwEJ
+ A/cB/wP3Af8DBwEJEAAD+QH/A/YB/wHdAcgBsQH/Aa8BTwEOAf8BwAGVAT4B/wHsAeYB4QH/Ae0B6AHk
+ Af8BxAGcAUkB/wGtAUsBBwH/AdoBwwGpAf8D9gH/WAAD+gH/AfAB6QHtAf8BvQGFAaQB/wGQAQMBOwH/
+ Ab0BhQGkAf8B8AHpAe0B/wP6Af8YAAMHAQkD9wH/AUYBqgHWAf8BAAFWAcIB/wEAAVYBwgH/AQABVgHC
+ Af8BAAFWAcIB/wFGAaoB1gH/A/cB/wMHAQkEAAMHAQkDBwEJHAAD+QH/A/YB/wHdAcgBsQH/Aa8BTwEO
+ Af8BrQFLAQcB/wHaAcMBqQH/AfYC9QH/A/kB/2AAA/4B/wP4Af8D9gH/A/gB/wP+Af8gAAMHAQkD9wH/
+ AUYBqgHWAf8BAAFWAcIB/wEAAVYBwgH/AUYBqgHWAf8D9wH/AwcBCTQAA/kB/wP2Af8D9gH/A/kB/6AA
+ AwcBCQP3Af8BRgGqAdYB/wFGAaoB1gH/A/cB/wMHAQnsAAMHAQkD9wH/A/cB/wMHAQn/AG0AAdcB7QH1
+ Af8BuAHeAe0B/wGTAcwB4gH/AUYBugHYAf8BHgGlAcsB/wHHAdcB3QH/bAAB3gHdAekB/wGsAagBxgH/
+ ATUBLgGNAf8BCQEAAUMB/wEGAQABPgH/ASkBIAGDAf8BowGeAbwB/wHaAdgB5AH/EAABFgGjAcoB/wEW
+ AaMBygH/ARYBowHKAf8BFgGjAcoB/wEWAaMBygH/ARYBowHKAf8BFgGjAcoB/wEWAaMBygH/ARYBowHK
+ Af8BFgGjAcoB/wEWAaMBygH/AT0BnwHEAf8BUgGsAcwB/wG7AdMB5AH/EAABQAG+Ad8B/wE9Ab4B3AH/
+ ATkBvQHZAf8BOAG9AdgB/wE4Ab8B2AH/ARcBowHKAf8BEgGQAbIB/wEVAZMBtQH/ARkBpQHLAf8BGAGk
+ AcsB/wEXAaQBygH/ATEBsAHRAf8BqgHWAecB/1AAAbUBswHPAf8BQwE+AZ4B/wFHAUEBnwH/ATsBNgGY
+ Af8BHQEVAYEB/wEVAQ0BUQH/ASsBIwGIAf8BLgEmAYcB/wEmARwBgAH/AaMBngG8Af8MAAEaAaYBzAH/
+ AZAB5wH2Af8BhwHkAfQB/wGAAeEB8wH/AVIB3wHyAf8BTgHdAfEB/wFLAdsB8AH/AUoB2gHwAf8BTAHb
+ AfAB/wFPAdwB8AH/ARoBpgHMAf8BKwHMAeoB/wEuAcYB5QH/AVIBrAHMAf8QAAFEAcAB4QH/AYAB1AHk
+ Af8BUAHRAeIB/wFLAc0B4AH/AUYBygHeAf8BGAGkAcsB/wEYAaIBvQH/ARwBpQHBAf8BJAHBAd4B/wEk
+ AcEB3gH/ASQBwQHeAf8BIgG9AdsB/wExAbAB0QH/LAADLAFEAUgBAAFHAf8DLwFJFAABvAG6AdYB/wFO
+ AUoBrAH/AZABjgG7Af8BswGxAdAB/wHqAekB8AH/AvwB/QH/A/wB/wHnAeYB7QH/AaIBngG9Af8BRQE9
+ AZgB/wEiARgBUgH/AaMBngG8Af8IAAEfAakBzgH/AY8B6AH2Af8BhgHlAfUB/wFVAeEB8wH/AU8B3gHy
+ Af8BSwHcAfEB/wFIAdwB8AH/AUcB2gHwAf8BSAHaAfAB/wFNAdsB8AH/AR8BqQHOAf8BMQHPAesB/wHT
+ AaUBHwH/ATwBnwHEAf8QAAFHAcIB4gH/AYYB2QHnAf8BgQHVAeUB/wFSAdIB4gH/AU0BzgHgAf8BGwGm
+ AcwB/wEaAaMBvgH/ARwBpQHBAf8BJAHBAd4B/wEkAcEB3gH/ASQBwQHeAf8B0wGlAR8B/wEYAaQBywH/
+ KAADJwE6AakBJgGoAf8BgAEGAVUB/wFLAQABSwH/Ay0BRQwAAuAB7gH/AoYBuQH/AZoBmQHGAf8B1gHU
+ AecZ/wHHAcQB1wH/AUQBPQGYAf8BJgEcAYAB/wHVAdMB4QH/BAABJAGsAdEB/wGQAekB9gH/AYcB5QH1
+ Af8BVQHiAfQB/wFPAeAB8gH/AUsB3gHyAf8BSQHcAfEB/wFHAdsB8QH/AUkB2gHwAf8BTAHcAfEB/wEk
+ AawB0QH/ATkB0gHsAf8B3QG5ATMB/wE8AaEBxQH/EAABSgHEAeQB/wGNAd0B6gH/AYgB2gHoAf8BggHW
+ AeUB/wFTAdMB4wH/AR0BpwHNAf8BIAGmAb8B/wEhAagBwgH/ASgBwwHfAf8BJAHBAd4B/wEkAcEB3gH/
+ Ad0BuQEzAf8BGgGlAcwB/yQAAysBQgGqASYBqQH/AcwBPQHLAf8BVAEEAVQB/wGWAQ4BlQH/AVEBAQFQ
+ Af8DKgFBCAACuAHZAf8BlQGWAccB/wLBAd8h/wGiAZ4BvQH/AS4BJgGHAf8BowGeAbwB/wQAASkBrwHT
+ Af8BkwHrAfgB/wGKAegB9wH/AYIB5QH1Af8BUgHiAfQB/wFPAeAB8wH/AUsB3gHyAf8BSQHdAfEB/wFL
+ AdwB8QH/AU8B3QHxAf8BKQGvAdMB/wFBAdUB7gH/A+wB/wE8AaIBxgH/EAABTgHGAeUB/wGUAeIB7QH/
+ AY4B3gHrAf8BiQHbAegB/wGEAdcB5gH/ASABqQHPAf8BKAGqAcEB/wEoAasBxAH/ATEBxwHhAf8BLAHF
+ AeAB/wEoAcMB3wH/A+wB/wEbAaYBzQH/CAABqwHwAfcB/wGrAeIB5QH/AawBzgHMAf8BrQG7AbUB/wGu
+ AacBnAH/BAADMQFNAakBJgGoAf8B0wFBAdIB/wHUAUQB0wH/AVUBBgFUAf8BnwESAZ8B/wGOAQsBjQH/
+ AVMBAwFSAf8DBwEJBAABmQGbAc0B/wGYAZoBzgH/AvAB9yH/AecB5gHtAf8BKwEjAYcB/wEpASABgwH/
+ BAABLwGzAdYB/wGXAe4B+QH/AY8B6gH4Af8BhwHoAfYB/wGBAeUB9QH/AVQB4wH0Af8BUAHhAfMB/wFO
+ AeAB8gH/AVAB3wHyAf8BUwHgAfIB/wEvAbMB1gH/AUoB2QHvAf8D7AH/ATsBowHGAf8QAAFRAcgB5wH/
+ AZoB5gHwAf8BlQHjAe0B/wGQAd8B6wH/AYoB2wHpAf8BIwGrAdAB/wEwAa4BxAH/ATABrwHGAf8BPAHN
+ AeQB/wE2AcoB4wH/ATEBxwHhAf8D7AH/AR4BqAHOAf8gAAGqASgBqQH/AdIBQAHRAf8B1QFFAdUB/wG9
+ AUYBvAH/Ad4BmAHdAf8BjwEcAY4B/wGdARIBnAH/AVQBBAFUAf8DBAEFBAABiQGLAcYB/wGOAZIBzwH/
+ A/0h/wP8Af8BFQENAVIB/wEGAQABPgH/BAABNQG3AdkB/wGcAfAB+gH/AZQB7QH4Af8BjAHqAfcB/wGH
+ AegB9gH/AYMB5gH1Af8BVQHkAfUB/wFUAeMB9AH/AVQB4gH0Af8BgQHiAfQB/wE1AbcB2QH/AVQB3QHx
+ Af8D7AH/ATsBpQHHAf8QAAFTAcoB6AH/AaAB6gHyAf8BmwHnAfAB/wGWAeMB7gH/AZEB4AHsAf8BJgGt
+ AdIB/wE6AbIBxgH/ATkBtAHJAf8BSQHTAegB/wFDAdAB5gH/ATwBzQHkAf8D7AH/ASABqQHPAf8EAANZ
+ AfUBqwHlAekB/wGsAtUB/wGsAcQBwQH/Aa0BtQGsAf8BrgGkAZgB/wQAAagBJQGnAf8B0gFDAdEB/wG8
+ AUUBuwH/Ae8BpQHuAf8B+wGfAfoB/wHvAZ0B7gH/AZcBKAGWAf8BUgEEAVIB/wMEAQUEAAGPAZEBzwH/
+ AZUBlwHUAf8D/SH/AvwB/QH/AR0BFQGBAf8BCQEAAUMB/wQAATsBugHcAf8BoAHyAfsB/wGYAe8B+QH/
+ AZEB7QH5Af8BjAHrAfgB/wGIAekB9wH/AYQB5wH2Af8BggHlAfUB/wGCAeUB9QH/AYUB5QH0Af8BOwG6
+ AdwB/wGIAeEB8wH/AYYB4QHyAf8BOgGnAcgB/xAAAYABywHpAf8BpgHuAfUB/wGhAesB8wH/AZ0B6AHx
+ Af8BmAHkAe8B/wEpAa8B1AH/AUMBuAHJAf8BQwG4AcsB/wGCAdoB7AH/AVAB1wHqAf8BSQHTAegB/wFD
+ AdAB5gH/ASIBqwHQAf8gAAGnASYBpgH/AbwBRQG7Af8B7wGlAe4B/wH7AZsB+gH/AfsBmAH6Af8B+wGZ
+ AfoB/wHnAZgB5gH/AZEBJAGQAf8DAgEDBAABqAGrAdwB/wGnAaoB3gH/AfAB8QH6If8B6gHpAfAB/wE7
+ ATYBmAH/ATUBLAGNAf8EAAFBAb4B3wH/AaQB9QH8Af8BnAHyAfsB/wGWAe8B+gH/AZEB7QH5Af8BjQHr
+ AfgB/wGKAekB9wH/AYcB5wH2Af8BhwHmAfYB/wGIAeUB9AH/AUIBvwHgAf8BhAHVAegB/wFBAbMB0AH/
+ AaQBzAHfAf8QAAGCAc0B6wH/AasB8QH3Af8BpwHvAfUB/wGjAewB8wH/AZ4B6AHxAf8BLQGyAdUB/wFM
+ AbwBywH/AUwBvQHOAf8BkAHiAfAB/wGJAd4B7gH/AYIB2gHsAf8BNQG8AdoB/wGXAc4B5AH/DAABqwHw
+ AfcB/wGsAtUB/wGuAbQBrAH/Aa4BmQGKAf8DAQECAc8BiAHPAf8B4gGvAeIB/wH7AasB+gH/AfsBmAH6
+ Af8B+wGYAfoB/wHqAZAB6QH/Ac8BiAHPAf8DIwE0CAAByAHKAeoB/wGtAbAB3wH/AtAB7SH/AbMBsQHQ
+ Af8BRgFBAZ8B/wGYAZMBtwH/BAABRgHCAeIB/wGoAfYB/AH/AaEB9AH8Af8BmwHyAfsB/wGVAfAB+gH/
+ AZIB7gH5Af8BjwHsAfgB/wGMAeoB9wH/AYoB6QH3Af8BjQHoAfcB/wFTAdIB6wH/AUUBwQHhAf8B0QHn
+ AfAB/xQAAYQBzgHsAf8BrwH0AfkB/wGsAfIB9wH/AagB7wH2Af8BpAHsAfQB/wExAbQB1wH/AYABwAHO
+ Af8BVQHBAdAB/wGeAekB8wH/AZcB5QHyAf8BkAHiAfAB/wEqAa8B1AH/JAADQAFuAc8BiAHPAf8B7gG+
+ Ae4B/wH7Aa4B+gH/AewBkQHrAf8BzwGIAc8B/wMvAUoMAAHpAeoB9wH/AasBrgHfAf8BuQG8AeYB/wHi
+ AeQB8xn/AdYB1AHnAf8BkAGOAbsB/wFDAT4BngH/AdYB1QHjAf8EAAFMAcUB5AH/AasB+AH9Af8BpQH2
+ Af0B/wGfAfQB/AH/AZoB8gH7Af8BlwHxAfoB/wGTAe8B+gH/AZEB7QH5Af8BjwHsAfgB/wGRAesB+AH/
+ AZQB6wH3Af8BTAHFAeQB/xgAAYYBzwHtAf8BswH3AfoB/wGwAfUB+QH/Aa0B8wH4Af8BqQHwAfYB/wE0
+ AbYB2QH/AYcBxAHQAf8BhwHFAdIB/wGsAe8B9wH/AaUB7AH1Af8BngHpAfMB/wEsAbEB1QH/KAADNwFb
+ Ac8BiAHPAf8B4wGsAeMB/wHPAYgBzwH/AyoBQRQAAdQB1gHwAf8BqQGtAd8B/wG5AbwB5gH/AtAB7QH/
+ AfAB8QH6Af8D/QH/A/0B/wLwAfcB/wLBAd8B/wGaAZkBxgH/AU4BSgGsAf8BtQGzAc8B/wgAAVEByAHn
+ Af8BrwH6Af4B/wGpAfgB/gH/AaQB9gH9Af8BoAH1AfwB/wGdAfMB+wH/AZoB8QH7Af8BlwHwAfoB/wGV
+ Ae8B+QH/AZcB7gH5Af8BmQHuAfgB/wFRAcgB5wH/GAABhwHQAe0B/wG0AfcB+wH/AbQB9wH7Af8BsQH1
+ AfoB/wGuAfMB+AH/ATgBuQHbAf8BjAHHAdEB/wGOAckB1AH/AbcB9AH6Af8BsQHyAfkB/wGsAe8B9wH/
+ AS8BswHXAf8sAAM3AVsBzwGIAc8B/wMpAT4cAAHUAdYB8AH/AasBrgHfAf8BqwGwAd8B/wGnAasB3gH/
+ AZUBlwHUAf8BkAGSAc8B/wGYAZoBzgH/AZUBlgHHAf8ChgG5Af8BvAG6AdYB/wwAAYABywHpAf8BtAH7
+ Av8BrgH6Af4B/wGqAfkB/gH/AacB+AH9Af8BpAH2AfwB/wGhAfUB/AH/AZ4B8wH7Af8BnQHxAfoB/wGe
+ AfAB+gH/AaAB8QH6Af8BgAHLAekB/xgAAYcB0AHtAf8BtAH3AfsB/wG0AfcB+wH/AaoB7wH3Af8BkAHb
+ Ae0B/wE8AbsB3QH/AZIBzAHVAf8BswHrAfEB/wG/AfkB/AH/AbsB9wH7Af8BtwH0AfoB/wEyAbUB2AH/
+ MAADCwEPJAAB6QHqAfcB/wHIAcoB6gH/AagBqwHcAf8BjwGRAc4B/wGJAYsBxwH/AZkBmwHNAf8CuAHZ
+ Af8C4AHuAf8QAAGEAc4B6wH/AbgB/AL/AbUB+wL/AbEB+gH+Af8BrgH5Af4B/wGsAfgB/QH/AakB9wH9
+ Af8BpwH2AfwB/wGmAfUB/AH/AaYB9AH8Af8BkwHdAfIB/wGEAc4B6wH/GAABhwHQAe0B/wGdAeMB9AH/
+ AYkB0wHtAf8BhwHQAesB/wGaAd8B8AH/AZYB0wHeAf8BvgH2AfoB/wHCAfoB/QH/AcIB+gH9Af8BwgH6
+ Af0B/wGzAfEB+AH/AUoBwAHfAf+IAAGHAdAB7QH/AYcB0AHtAf8BhwHQAe0B/wGHAdAB7QH/AYcB0AHt
+ Af8BhwHQAe0B/wGHAdAB7QH/AYcB0AHtAf8BhwHQAe0B/wGHAdAB7QH/AYcB0AHtAf8BtgHfAeoB/xgA
+ AZIB1AHvAf8BkAHTAe4B/wGCAc0B6gH/AVUBywHpAf8BUgHJAegB/wFPAccB5gH/AUsBxQHkAf8BRwHC
+ AeIB/wFEAcAB4QH/AUABvQHfAf8BUAHEAeEB/wG3Ad8B7gH/CAABQgFNAT4HAAE+AwABKAMAAUADAAEg
+ AwABAQEAAQEGAAEBFgAD/wEAA/8BwwL/AgAD/wGBAv8CAAH4AT8B/wEAAv8CAAHwAR8B/gEAAfgBfwIA
+ AeABDwH+AQAB4AEfAgABwAEHAeYBAAHAAQcCAAHAAQcBwgEBAcABAwIAAcABBwGAAQABwAEDAgABwAEH
+ AgABwAEDAgABwAEHAgABwAEDAgABwAEHAgABwAEDAgAB4AEPAQABAQHgAQMCAAHwAR8BgAETAfgBBwIA
+ AfgBPwHAAT8B/gEfAgAC/wHgAX8C/wIAAv8B8AP/AgAG/wHgAX8C/wHwAQ8BAAEDAcABAQL/AeABBwEA
+ AQMBwAEBAf8BxwHAAQMBAAEDAcABAQH/AYMBgAEBAQABAwHAAQEB/wEBAYABAQEAAQMBwAEBAYIBAAGA
+ AQEBAAEDAcABAQH+AQABgAEBAQABAwHAAQEBAgEAAYABAQEAAQMBwAEBAf4BAAGAAQEBAAEDAcABAQHA
+ AQEBgAEBAQABBwHAAQMB/gEDAYABAQEAAQ8BwAEDAf8BBwHAAQMBAAEPAcABAwH/AY8B4AEHAQABDwHA
+ AQMB/wHfAfABDwEAAQ8BwAEDBP8BAAEPAcABAws=
+
+
+
+ 17, 17
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGsSURBVDhPpdK9L4NBHAdwb5U+T6vkErQJ6cuDkFAq
+ PTQS0UosTQfpZDEYiEXMdpNJOljEaJGQLo2hgz9AFFUSEolYGFEdPM2d7z3tE89VIxHDJ73fy/2ePvdc
+ A+f8XyoLSsYgCzFoqm36jdjcDKtsJc7Z0pyO9YKRq9OMfAsEYN3MmcllyLDleQ5lrDdBHkKJHWJsKSYe
+ svc9oFL0wBRkWDLM2eI0xzoNjmpdYVF/GjWxeRuc8gATJa2wweb7dTEI6wMIQqo6NAXt1j3yAIGSRhiC
+ nPFKieAnvCM+AqW2XwoklMyyJNVZYkQ8+QFIvb4fCQMlPrgCVgp18PeQSwy5q9crJ8QdoMQLBT7tLj8P
+ u8TdCEPmZdgYsgaN1j21A9w80pUrRn280KfeZz22WeQcVbsXAUXPawpOt94A8Z0j3YdQPPcrb2dee1Kq
+ UeJ6GnLe32jqSbqzuRexcaDWAfHSZBe/DCg8r6nHiCt34LsuXq/vtMf2gX83YcTIm0Xx/bceB538WlNv
+ sR4wGyS4na+j7fv4pWbdWhyHHZiBNpAOy9Inrr5qxj8b/oQ3fAFDkWN6+72aNAAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAE3SURBVDhPpZHdR0NxGMf3v9T0dtVfkLrqIiUR0cvV
+ SB2WTTW6SES66SJTOiyZRI0SG0k6zBYZmyOtOdWml8Ok3axpyzrn2++Jrs7T0VkXn5uP7/O5eVwA/gUr
+ ncDKuXgRvvNnSCd5eI5vMLafEdq6I1g5c/aIvUIdm9onVrMGmqR1oa07gpWTUQ3b9wYCqoHRRBVunyy0
+ dUewciKiQr4z4E/XMXz6ipZAWGjrjmAlBYKaCSn10XhgLWfCk3xvPLCSNTEerzgL0Pv8io6p2C2WroER
+ pYyBozzaFiNwz4bQ7N0QM5tAsVxDfyiJ6csqvKkahmI6endVdAUVtIrA1dOLmNkEiELpDR3LhxiM6ug7
+ yKFnK4H2+R32mLAI4qFU+Y50yxfoXAj/ekywkviJ2B0TrHQCK/8OXF8UzfqGPgQWkAAAAABJRU5ErkJg
+ gg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADxSURBVDhPY/j//z9FGKsgDN968w1IYZeDYayCIPzy
+ 49f/ZYde/D9w7z2Qi10NCGMVhGmed/fvf8PqRf8P3n4OFMZUB8IYAsia80//+O8088h/rfIFOA1B4SBr
+ br3273/k/k//PZZc/G8/Zf9/jdK5WA1B4YA0F+9/Cjak9vL//3En/vz3X3/vv2b96v+qQANU8qcBleEx
+ AIZBBoBcADNAvWIRUBhTHQhjFQQZMPnW3/+55/6CDVAtmgUUxlQHwlgFYeFQcXEgDVh2/zflBoCiEISV
+ cyYBhTHVgTBWQZABIE3IGF0NDGMVJAVjFSQe/2cAAHyD/HHFkIDGAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJKSURBVDhPnZDJTxNhGMbnT/PghYsXY6IxxoOih5q4
+ YaVaNq20IZKCResCtkhbqDYQg7uJRgNItEGgwQRo6WJLl+k6nU6n68P7TdFWuBCbvHl7eH6/95mPSy3r
+ cdBJ+N+AflzrcPySDqi+AyqvUS+/RL00jZrsQq3oRFWaRLXgQFV0QPCZoGT3CuI/egl+RfA0wc8JJkiy
+ ETiOSv4pyrkxlLNsW6Fk9wqiizcJnkFddqKmgFYCx1DJPSbQjFLmPkppM+T0I7DsPkF4Xk1XXxBsp6oW
+ VAQCc2aUMyaUUsOQk0bI/DCK/AhYdp8g9PUSVZ4imCoLT+gqu3hPAYuJuyjGB1CMDYJf7ULwy0Vkoyv/
+ SDj/JxXVthE8SlcfNK7ygwooRfWQtvUQIwYEPl9AbE2H5VkV/G7bXwnn/dBOsJW+9eEuzK4aCNahELml
+ jBi5A5bLBw0Qgv1YnDzbFKy/PU21RyGnRpTKUkyPwvZtFMI9EH93Qwyx6QPL5XzdSCaT+GZvbwp+zZ5o
+ vHLC2IAjfQqYD96AEOiE4Ge7CyyXWe9QBAsTLQLPzFGqbaKHGmjUDWmRD2ggbF2ji1eR87KtAcul11QN
+ wbNzTcGK6wik+BDB9FihHrq2C3sJ3ryM7AaTqMFyydUzOPXeh/nxFsGSs42qG0nQT7VJsKVRrma9Vwhm
+ Atqb18Fy/M+TaLN7MGdpEbgdh+nFhyCG9STopW/WUuVOgkiy0UFbTTINWC7hPobIwnG4Xdqm4PvEIRx0
+ 5iznFTjs+dgU/PnzfwNuBxoXNpfwri/ZAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGpSURBVDhPpZBrK4NhGMd9Bi+QwyRFpByWwzBee+Wr
+ SMY2bTnmtJkN0Zw1bDYURTmELEk+weZUjgkxe/Cw+rvuoZWuF+LF77m7f8///l93dxSAf8FKwcFSOfYX
+ 1PC6i7HlLCLF51gp2FsoQ0iaRSjowpqjkBSfY6XA6ynBe9CN0N04VsbzSPE5Vgq2XUVUQDe4H8PicA4p
+ PsdKwfpUPl1fFNgxP5hNis+FP3vzZdj1lGLHrcKmU4UNRz4OliupwInQrR3bngpMWzIwaU7DSGcqhtpS
+ MNicTEe/CrweNT0YhZ9mwo8WRuyJt9t+yDdWvF3bIF9Z8XppxsuFCX2NikjBlktFIQvkixYKtIZX+bwJ
+ 8pkR8mk9Xo61RC2koxpIfg2eabUaEyIFqxNKLI/mYtGeg7mBLLh7M7E+VQDppA7SYRWWhpQ0MRG9dKjH
+ EA+LPg5mXWykgGPSlI4gTQ34q9Hf8DmNg5WC4fYUmq5FwKeBlab+/P8NKwX2VgVN1+HRp0W3PoYUn2Ol
+ oK8hCQ9+Ax58enTp/lBgo2ub62NhqotBR200KT7Hyt+DqA9BTvIqX1elLAAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAATVJREFUOE/V
+ kltKw0AYhbMn8dEFiMtwEV2A4A6sisFbL4iCWC8PVvGCFVu1ChGUin0IqNgqpHZyTybHf2JSkYzgo/5w
+ noZzvvPPjPJ3prxzAaHi9jkKm3UsbZxiYf0E6tox5lYOMFPeR75YxVRhF7lJFYnta4Q5iqJYnEfwggi2
+ x8HsEAYL0O35eH7zoL+4GB4dzwaUthqxeWhkLJbjcuQmpjN6eHLkAcuVM4RE9onsfCMHRPahd7zYfKfb
+ 8oBF2lmY0wYGC6UNtLYlD5inC8uSP3dOyVrbRPPelAeoq0dk5oMGwixrUL/tywNm6akE+TUldwTZTcgW
+ rlqMzAw17YcA8c5dIxg0ELVlDQ6v3+UB+VI1IdNtP6Y7W2i2TDSodu2mT+Ye9i4NeYD4XeLgt0ps/38U
+ 5QOks5Ivm3oJagAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAgZJREFUOE/N
+ kPtLk3EUxv2HIiNiIYWkxNqIlV2GzFsuxxqjZNOERdlNK5qIlZGmlW3JnMpi5q3ctHmJdCrqtqaChGGR
+ 2VKEFay2T+/khRi89lM/9MDhe/jyPB/OOWn/XO+mfLrlxQCLgQlmx72MDbjo62ym47H1hGjZXkH/kH4p
+ NMn3yALxzRCJ9XESawNEwnae1F2gyVouF63Smp8ZI7oW5EOon5pKA9UVRVwz57HytopVfyVVZRpEq7Sm
+ R3pIbExw1aShQndYlvw7f1qx+1njDRILFiz6I38HDHfbYa2f8mJ5irG14SaJsBlTUfb2gJFuW4an4wGv
+ 2u/T33aPntY6umy1PG+5TeejWzgfVuNouI7t7qUMMZKqZJiID769Ft5hYZIh+OKFz71C74F1Hytz7TRb
+ y76KkVS9dNbDhhAOGojP6Am5VFwp3cPUCy3Ml0PAKPByEW4ivUZyZCJeYv5C4v4CzLp08tU7MOn38mvW
+ wM/JYj72qjCfOigNcD+tgdU+osM5RAeVrA5q0ObtIjJaQmxay4/Rkyy7sigtyJIGJA/FJzebfZlEPXK6
+ m9VbAOcdBbE3aqJeJe8dMs5qMqUBbY1VELYSaEpnqes4c+5CxuxH8dsUzDuyCbbI8NXuxJi7Txpgr7+s
+ vmg8xp/KwXJGRUWJcmvvc/kHhPD+ZB0SI/+F0tJ+A2jpZys9ITxEAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAP5JREFUOE9j
+ GGbg////LUD8BIgPArE9VMweygeJt4AV4gL//v17mrrkwv+uXbf///79+wRIM4gG8UHiIHmoUuzgy5cv
+ xzu2Xvtv0rjzP4j++fPnSWQ+SB6qFDs4evRowMOHD6+1rT//36BszX9kGiQOkocqxQk4li9fHnv37t2b
+ LcuP/tfJnPMfRIP4IHGQPEQZDgDyM8jZjfN3/9eI7v6PTIPEQfJQpdgBMMAO10/f9F/Ft/o/iL58+fI9
+ ZD5IHqoUOwAqeBaS1/+/duIqsLO7urryQDSIDxIHyUOVYgdPnz6d8PHjx1f37t27BPUzH4gG8UHiIHmI
+ ylGAAAwMAKVS7Q2vRMy7AAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
+ JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAABLklE
+ QVQ4T2P4//8/RRirICkYqyAMz9ky4f+yAzM8sMnBMFZBGJ68qvN/y/Tq/zM39KZgkwdhrIIw3LOw6f+O
+ h0vxGoLCWbRn6n+Qs0E2gzS3zKj5v/vpiv/TjjfgNASFA9J88uOW/0ferfu//9Wq/zufLPm/7cHi/z0H
+ y/43bMqFGLKxNxlZD4oBIJuPvd/4f+2jyf8X3+j+P+NsI0Tz1pz/JauS/qdMCvufXpYAVIrDAJCz971c
+ +b9tc/H/2lVZ/0sWJP3Pmh4F1uxX7vxfRkn6v6mjSRCyHhQDQH5W1lb6r2Om/d/Uwfi/jafV/4TeQLhm
+ MydTX2T1IIzCQcf1kyrwagZhDAFkDDIApFlVVwWrZhDGKgjDIAOA3jHGJgfDWAVJwVgFScFYBYnH/xkA
+ pYfWoJzxMfMAAAAASUVORK5CYII=
+
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.Designer.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.Designer.cs
new file mode 100644
index 000000000..41f47e610
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.Designer.cs
@@ -0,0 +1,102 @@
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ partial class FrmNodeAttr
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.listView = new System.Windows.Forms.ListView();
+ this.btnClose = new System.Windows.Forms.Button();
+ this.colName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.colValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.SuspendLayout();
+ //
+ // listView
+ //
+ this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.colName,
+ this.colValue});
+ this.listView.FullRowSelect = true;
+ this.listView.GridLines = true;
+ this.listView.HideSelection = false;
+ this.listView.Location = new System.Drawing.Point(12, 12);
+ this.listView.MultiSelect = false;
+ this.listView.Name = "listView";
+ this.listView.ShowItemToolTips = true;
+ this.listView.Size = new System.Drawing.Size(310, 308);
+ this.listView.TabIndex = 0;
+ this.listView.UseCompatibleStateImageBehavior = false;
+ this.listView.View = System.Windows.Forms.View.Details;
+ //
+ // btnClose
+ //
+ this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnClose.Location = new System.Drawing.Point(247, 326);
+ this.btnClose.Name = "btnClose";
+ this.btnClose.Size = new System.Drawing.Size(75, 23);
+ this.btnClose.TabIndex = 1;
+ this.btnClose.Text = "Close";
+ this.btnClose.UseVisualStyleBackColor = true;
+ //
+ // colName
+ //
+ this.colName.Text = "Name";
+ this.colName.Width = 120;
+ //
+ // colValue
+ //
+ this.colValue.Text = "Value";
+ this.colValue.Width = 160;
+ //
+ // FrmNodeAttr
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.btnClose;
+ this.ClientSize = new System.Drawing.Size(334, 361);
+ this.Controls.Add(this.btnClose);
+ this.Controls.Add(this.listView);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "FrmNodeAttr";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Node Attributes";
+ this.Load += new System.EventHandler(this.FrmNodeAttr_Load);
+ this.Shown += new System.EventHandler(this.FrmNodeAttr_Shown);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ListView listView;
+ private System.Windows.Forms.Button btnClose;
+ private System.Windows.Forms.ColumnHeader colName;
+ private System.Windows.Forms.ColumnHeader colValue;
+ }
+}
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.cs
new file mode 100644
index 000000000..7b7c2980b
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.cs
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Form for viewing OPC node attributes
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using Opc.Ua;
+using Opc.Ua.Client;
+using Scada.UI;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ ///
+ /// Form for viewing OPC node attributes.
+ /// Форма просмотра атрибутов OPC-узла.
+ ///
+ public partial class FrmNodeAttr : Form
+ {
+ private Session opcSession; // the OPC session
+ private NodeId nodeId; // the node whose attributes are shown
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ private FrmNodeAttr()
+ {
+ InitializeComponent();
+ colName.Name = "colName";
+ colValue.Name = "colValue";
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public FrmNodeAttr(Session opcSession, NodeId nodeId)
+ : this()
+ {
+ this.opcSession = opcSession ?? throw new ArgumentNullException("opcSession");
+ this.nodeId = nodeId ?? throw new ArgumentNullException("nodeId");
+ }
+
+
+ ///
+ /// Reads available attributes from OPC server.
+ ///
+ private void ReadAttributes()
+ {
+ try
+ {
+ // request attributes
+ ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
+
+ foreach (uint attributeId in Attributes.GetIdentifiers())
+ {
+ nodesToRead.Add(new ReadValueId
+ {
+ NodeId = nodeId,
+ AttributeId = attributeId
+ });
+ }
+
+ opcSession.Read(null, 0, TimestampsToReturn.Neither, nodesToRead,
+ out DataValueCollection results, out DiagnosticInfoCollection diagnosticInfos);
+ ClientBase.ValidateResponse(results, nodesToRead);
+ ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
+
+ // display attributes
+ for (int i = 0; i < nodesToRead.Count; i++)
+ {
+ ReadValueId readValueId = nodesToRead[i];
+ DataValue dataValue = results[i];
+
+ if (dataValue.StatusCode != StatusCodes.BadAttributeIdInvalid)
+ {
+ listView.Items.Add(new ListViewItem(new string[] {
+ Attributes.GetBrowseName(readValueId.AttributeId),
+ FormatAttribute(readValueId.AttributeId, dataValue.Value)
+ }));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ ScadaUiUtils.ShowError(KpPhrases.ReadAttrError + ":" + Environment.NewLine + ex.Message);
+ }
+ }
+
+ ///
+ /// Formats the attribute value.
+ ///
+ private string FormatAttribute(uint attributeId, object value)
+ {
+ switch (attributeId)
+ {
+ case Attributes.NodeClass:
+ return value == null ?
+ "(null)" :
+ Enum.ToObject(typeof(NodeClass), value).ToString();
+
+ case Attributes.DataType:
+ if (value is NodeId dataTypeId)
+ {
+ INode dataType = opcSession.NodeCache.Find(dataTypeId);
+ if (dataType != null)
+ return dataType.DisplayName.Text;
+ }
+ return string.Format("{0}", value);
+
+ case Attributes.ValueRank:
+ if (value is int valueRank)
+ {
+ switch (valueRank)
+ {
+ case ValueRanks.Scalar: return "Scalar";
+ case ValueRanks.OneDimension: return "OneDimension";
+ case ValueRanks.OneOrMoreDimensions: return "OneOrMoreDimensions";
+ case ValueRanks.Any: return "Any";
+ default: return string.Format("{0}", valueRank);
+ }
+ }
+ return string.Format("{0}", value);
+
+ case Attributes.MinimumSamplingInterval:
+ if (value is double minimumSamplingInterval)
+ {
+ if (minimumSamplingInterval == MinimumSamplingIntervals.Indeterminate)
+ return "Indeterminate";
+ else if (minimumSamplingInterval == MinimumSamplingIntervals.Continuous)
+ return "Continuous";
+ else
+ return string.Format("{0}", minimumSamplingInterval);
+ }
+ return string.Format("{0}", value);
+
+ case Attributes.AccessLevel:
+ case Attributes.UserAccessLevel:
+ byte accessLevel = Convert.ToByte(value);
+ List accessList = new List();
+
+ if ((accessLevel & AccessLevels.CurrentRead) != 0)
+ accessList.Add("Readable");
+
+ if ((accessLevel & AccessLevels.CurrentWrite) != 0)
+ accessList.Add("Writeable");
+
+ if ((accessLevel & AccessLevels.HistoryRead) != 0)
+ accessList.Add("History");
+
+ if ((accessLevel & AccessLevels.HistoryWrite) != 0)
+ accessList.Add("History Update");
+
+ if (accessList.Count == 0)
+ accessList.Add("No Access");
+
+ return string.Join(" | ", accessList);
+
+ case Attributes.EventNotifier:
+ byte notifier = Convert.ToByte(value);
+ List bits = new List();
+
+ if ((notifier & EventNotifiers.SubscribeToEvents) != 0)
+ bits.Add("Subscribe");
+
+ if ((notifier & EventNotifiers.HistoryRead) != 0)
+ bits.Add("History");
+
+ if ((notifier & EventNotifiers.HistoryWrite) != 0)
+ bits.Add("History Update");
+
+ if (bits.Count == 0)
+ bits.Add("No Access");
+
+ return string.Join(" | ", notifier);
+
+ default:
+ return string.Format("{0}", value);
+ }
+ }
+
+
+ private void FrmNodeAttr_Load(object sender, EventArgs e)
+ {
+ Translator.TranslateForm(this, GetType().FullName);
+ }
+
+ private async void FrmNodeAttr_Shown(object sender, EventArgs e)
+ {
+ await Task.Run(() => ReadAttributes());
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.resx b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.resx
new file mode 100644
index 000000000..1af7de150
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmNodeAttr.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.Designer.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.Designer.cs
new file mode 100644
index 000000000..239853a75
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.Designer.cs
@@ -0,0 +1,214 @@
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ partial class FrmSecurityOptions
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.lblSecurityMode = new System.Windows.Forms.Label();
+ this.cbSecurityMode = new System.Windows.Forms.ComboBox();
+ this.cbSecurityPolicy = new System.Windows.Forms.ComboBox();
+ this.lblSecurityPolicy = new System.Windows.Forms.Label();
+ this.cbAuthenticationMode = new System.Windows.Forms.ComboBox();
+ this.lblAuthenticationMode = new System.Windows.Forms.Label();
+ this.pnlUsername = new System.Windows.Forms.Panel();
+ this.lblUsername = new System.Windows.Forms.Label();
+ this.txtUsername = new System.Windows.Forms.TextBox();
+ this.txtPassword = new System.Windows.Forms.TextBox();
+ this.lblPassword = new System.Windows.Forms.Label();
+ this.btnOK = new System.Windows.Forms.Button();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.pnlUsername.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // lblSecurityMode
+ //
+ this.lblSecurityMode.AutoSize = true;
+ this.lblSecurityMode.Location = new System.Drawing.Point(9, 9);
+ this.lblSecurityMode.Name = "lblSecurityMode";
+ this.lblSecurityMode.Size = new System.Drawing.Size(74, 13);
+ this.lblSecurityMode.TabIndex = 0;
+ this.lblSecurityMode.Text = "Security mode";
+ //
+ // cbSecurityMode
+ //
+ this.cbSecurityMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbSecurityMode.FormattingEnabled = true;
+ this.cbSecurityMode.Location = new System.Drawing.Point(12, 25);
+ this.cbSecurityMode.Name = "cbSecurityMode";
+ this.cbSecurityMode.Size = new System.Drawing.Size(260, 21);
+ this.cbSecurityMode.TabIndex = 1;
+ this.cbSecurityMode.SelectedIndexChanged += new System.EventHandler(this.cbSecurityMode_SelectedIndexChanged);
+ //
+ // cbSecurityPolicy
+ //
+ this.cbSecurityPolicy.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbSecurityPolicy.FormattingEnabled = true;
+ this.cbSecurityPolicy.Location = new System.Drawing.Point(12, 65);
+ this.cbSecurityPolicy.Name = "cbSecurityPolicy";
+ this.cbSecurityPolicy.Size = new System.Drawing.Size(260, 21);
+ this.cbSecurityPolicy.TabIndex = 3;
+ //
+ // lblSecurityPolicy
+ //
+ this.lblSecurityPolicy.AutoSize = true;
+ this.lblSecurityPolicy.Location = new System.Drawing.Point(9, 49);
+ this.lblSecurityPolicy.Name = "lblSecurityPolicy";
+ this.lblSecurityPolicy.Size = new System.Drawing.Size(75, 13);
+ this.lblSecurityPolicy.TabIndex = 2;
+ this.lblSecurityPolicy.Text = "Security policy";
+ //
+ // cbAuthenticationMode
+ //
+ this.cbAuthenticationMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbAuthenticationMode.FormattingEnabled = true;
+ this.cbAuthenticationMode.Location = new System.Drawing.Point(12, 105);
+ this.cbAuthenticationMode.Name = "cbAuthenticationMode";
+ this.cbAuthenticationMode.Size = new System.Drawing.Size(260, 21);
+ this.cbAuthenticationMode.TabIndex = 5;
+ this.cbAuthenticationMode.SelectedIndexChanged += new System.EventHandler(this.cbAuthenticationMode_SelectedIndexChanged);
+ //
+ // lblAuthenticationMode
+ //
+ this.lblAuthenticationMode.AutoSize = true;
+ this.lblAuthenticationMode.Location = new System.Drawing.Point(9, 89);
+ this.lblAuthenticationMode.Name = "lblAuthenticationMode";
+ this.lblAuthenticationMode.Size = new System.Drawing.Size(104, 13);
+ this.lblAuthenticationMode.TabIndex = 4;
+ this.lblAuthenticationMode.Text = "Authentication mode";
+ //
+ // pnlUsername
+ //
+ this.pnlUsername.Controls.Add(this.txtPassword);
+ this.pnlUsername.Controls.Add(this.lblPassword);
+ this.pnlUsername.Controls.Add(this.txtUsername);
+ this.pnlUsername.Controls.Add(this.lblUsername);
+ this.pnlUsername.Location = new System.Drawing.Point(12, 132);
+ this.pnlUsername.Name = "pnlUsername";
+ this.pnlUsername.Size = new System.Drawing.Size(260, 75);
+ this.pnlUsername.TabIndex = 6;
+ //
+ // lblUsername
+ //
+ this.lblUsername.AutoSize = true;
+ this.lblUsername.Location = new System.Drawing.Point(-3, 0);
+ this.lblUsername.Name = "lblUsername";
+ this.lblUsername.Size = new System.Drawing.Size(55, 13);
+ this.lblUsername.TabIndex = 0;
+ this.lblUsername.Text = "Username";
+ //
+ // txtUsername
+ //
+ this.txtUsername.Location = new System.Drawing.Point(0, 16);
+ this.txtUsername.Name = "txtUsername";
+ this.txtUsername.Size = new System.Drawing.Size(260, 20);
+ this.txtUsername.TabIndex = 1;
+ //
+ // txtPassword
+ //
+ this.txtPassword.Location = new System.Drawing.Point(0, 55);
+ this.txtPassword.Name = "txtPassword";
+ this.txtPassword.Size = new System.Drawing.Size(260, 20);
+ this.txtPassword.TabIndex = 3;
+ this.txtPassword.UseSystemPasswordChar = true;
+ //
+ // lblPassword
+ //
+ this.lblPassword.AutoSize = true;
+ this.lblPassword.Location = new System.Drawing.Point(-3, 39);
+ this.lblPassword.Name = "lblPassword";
+ this.lblPassword.Size = new System.Drawing.Size(53, 13);
+ this.lblPassword.TabIndex = 2;
+ this.lblPassword.Text = "Password";
+ //
+ // btnOK
+ //
+ this.btnOK.Location = new System.Drawing.Point(116, 223);
+ this.btnOK.Name = "btnOK";
+ this.btnOK.Size = new System.Drawing.Size(75, 23);
+ this.btnOK.TabIndex = 7;
+ this.btnOK.Text = "OK";
+ this.btnOK.UseVisualStyleBackColor = true;
+ this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
+ //
+ // btnCancel
+ //
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.Location = new System.Drawing.Point(197, 223);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.Size = new System.Drawing.Size(75, 23);
+ this.btnCancel.TabIndex = 8;
+ this.btnCancel.Text = "Cancel";
+ this.btnCancel.UseVisualStyleBackColor = true;
+ //
+ // FrmSecurityOptions
+ //
+ this.AcceptButton = this.btnOK;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.btnCancel;
+ this.ClientSize = new System.Drawing.Size(284, 258);
+ this.Controls.Add(this.btnCancel);
+ this.Controls.Add(this.btnOK);
+ this.Controls.Add(this.pnlUsername);
+ this.Controls.Add(this.cbAuthenticationMode);
+ this.Controls.Add(this.lblAuthenticationMode);
+ this.Controls.Add(this.cbSecurityPolicy);
+ this.Controls.Add(this.lblSecurityPolicy);
+ this.Controls.Add(this.cbSecurityMode);
+ this.Controls.Add(this.lblSecurityMode);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "FrmSecurityOptions";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Security Options";
+ this.Load += new System.EventHandler(this.FrmSecurityOptions_Load);
+ this.pnlUsername.ResumeLayout(false);
+ this.pnlUsername.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label lblSecurityMode;
+ private System.Windows.Forms.ComboBox cbSecurityMode;
+ private System.Windows.Forms.ComboBox cbSecurityPolicy;
+ private System.Windows.Forms.Label lblSecurityPolicy;
+ private System.Windows.Forms.ComboBox cbAuthenticationMode;
+ private System.Windows.Forms.Label lblAuthenticationMode;
+ private System.Windows.Forms.Panel pnlUsername;
+ private System.Windows.Forms.TextBox txtPassword;
+ private System.Windows.Forms.Label lblPassword;
+ private System.Windows.Forms.TextBox txtUsername;
+ private System.Windows.Forms.Label lblUsername;
+ private System.Windows.Forms.Button btnOK;
+ private System.Windows.Forms.Button btnCancel;
+ }
+}
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.cs
new file mode 100644
index 000000000..7cd11463c
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.cs
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Security options form
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2018
+ * Modified : 2019
+ */
+
+using Opc.Ua;
+using Scada.Comm.Devices.OpcUa.Config;
+using Scada.UI;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ ///
+ /// Security options form.
+ /// Форма параметров безопасности.
+ ///
+ public partial class FrmSecurityOptions : Form
+ {
+ private ConnectionOptions connectionOptions; // the OPC server connection options
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ private FrmSecurityOptions()
+ {
+ InitializeComponent();
+ FillComboBoxes();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public FrmSecurityOptions(ConnectionOptions connectionOptions)
+ : this()
+ {
+ this.connectionOptions = connectionOptions;
+ }
+
+
+ ///
+ /// Fills the combo boxes.
+ ///
+ private void FillComboBoxes()
+ {
+ cbSecurityMode.Items.AddRange(new object[] {
+ MessageSecurityMode.None,
+ MessageSecurityMode.Sign,
+ MessageSecurityMode.SignAndEncrypt });
+
+ cbSecurityPolicy.Items.AddRange(new object[] {
+ SecurityPolicy.None,
+ SecurityPolicy.Basic128Rsa15,
+ SecurityPolicy.Basic256,
+ SecurityPolicy.Basic256Sha256,
+ SecurityPolicy.Aes128_Sha256_RsaOaep,
+ SecurityPolicy.Aes256_Sha256_RsaPss,
+ SecurityPolicy.Https });
+
+ cbAuthenticationMode.Items.AddRange(new object[] {
+ AuthenticationMode.Anonymous,
+ AuthenticationMode.Username });
+ }
+
+ ///
+ /// Sets the controls according to the configuration.
+ ///
+ private void ConfigToControls()
+ {
+ cbSecurityMode.SelectedIndex = (int)connectionOptions.SecurityMode - 1;
+ cbSecurityPolicy.SelectedIndex = (int)connectionOptions.SecurityPolicy;
+ cbAuthenticationMode.SelectedIndex = (int)connectionOptions.AuthenticationMode;
+ txtUsername.Text = connectionOptions.Username;
+ txtPassword.Text = connectionOptions.Password;
+ }
+
+ ///
+ /// Sets the configuration parameters according to the controls.
+ ///
+ private void ControlsToConfig()
+ {
+ connectionOptions.SecurityMode = (MessageSecurityMode)(cbSecurityMode.SelectedIndex + 1);
+ connectionOptions.SecurityPolicy = (SecurityPolicy)cbSecurityPolicy.SelectedIndex;
+ connectionOptions.AuthenticationMode = (AuthenticationMode)cbAuthenticationMode.SelectedIndex;
+ connectionOptions.Username = txtUsername.Text;
+ connectionOptions.Password = txtPassword.Text;
+ }
+
+
+ private void FrmSecurityOptions_Load(object sender, EventArgs e)
+ {
+ Translator.TranslateForm(this, GetType().FullName);
+ ConfigToControls();
+ }
+
+ private void cbSecurityMode_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ // fix the security policy
+ if (cbSecurityMode.SelectedIndex == 0)
+ cbSecurityPolicy.SelectedIndex = 0;
+ else if (cbSecurityPolicy.SelectedIndex == 0)
+ cbSecurityPolicy.SelectedIndex = 1;
+ }
+
+ private void cbAuthenticationMode_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ pnlUsername.Enabled = cbAuthenticationMode.SelectedIndex > 0;
+ }
+
+ private void btnOK_Click(object sender, EventArgs e)
+ {
+ ControlsToConfig();
+ DialogResult = DialogResult.OK;
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.resx b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.resx
new file mode 100644
index 000000000..1af7de150
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/FrmSecurityOptions.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/KpPhrases.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/KpPhrases.cs
new file mode 100644
index 000000000..293d53a75
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/KpPhrases.cs
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ * All rights reserved
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : The phrases used by the library
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ ///
+ /// The phrases used by the library.
+ /// Фразы, используемые библиотекой.
+ ///
+ public static class KpPhrases
+ {
+ // Scada.Comm.Devices.OpcUa.UI.FrmConfig
+ public static string ConnectServerError { get; private set; }
+ public static string DisconnectServerError { get; private set; }
+ public static string BrowseServerError { get; private set; }
+ public static string GetDataTypeError { get; private set; }
+ public static string ServerUrlRequired { get; private set; }
+ public static string EmptyNode { get; private set; }
+ public static string SubscriptionsNode { get; private set; }
+ public static string CommandsNode { get; private set; }
+ public static string EmptySubscription { get; private set; }
+ public static string EmptyItem { get; private set; }
+ public static string EmptyCommand { get; private set; }
+ public static string UnknownDataType { get; private set; }
+
+ // Scada.Comm.Devices.OpcUa.UI.FrmNodeAttr
+ public static string ReadAttrError { get; private set; }
+
+ public static void Init()
+ {
+ Localization.Dict dict = Localization.GetDictionary("Scada.Comm.Devices.OpcUa.UI.FrmConfig");
+ ConnectServerError = dict.GetPhrase("ConnectServerError");
+ DisconnectServerError = dict.GetPhrase("DisconnectServerError");
+ BrowseServerError = dict.GetPhrase("BrowseServerError");
+ GetDataTypeError = dict.GetPhrase("GetDataTypeError");
+ ServerUrlRequired = dict.GetPhrase("ServerUrlRequired");
+ EmptyNode = dict.GetPhrase("EmptyNode");
+ SubscriptionsNode = dict.GetPhrase("SubscriptionsNode");
+ CommandsNode = dict.GetPhrase("CommandsNode");
+ EmptySubscription = dict.GetPhrase("EmptySubscription");
+ EmptyItem = dict.GetPhrase("EmptyItem");
+ EmptyCommand = dict.GetPhrase("EmptyCommand");
+ UnknownDataType = dict.GetPhrase("UnknownDataType");
+
+ dict = Localization.GetDictionary("Scada.Comm.Devices.OpcUa.UI.FrmNodeAttr");
+ ReadAttrError = dict.GetPhrase("ReadAttrError");
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/TreeUpdateTypes.cs b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/TreeUpdateTypes.cs
new file mode 100644
index 000000000..2c970f9e5
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/OpcUa/UI/TreeUpdateTypes.cs
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 Mikhail Shiryaev
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Product : Rapid SCADA
+ * Module : KpOpcUa
+ * Summary : Types of updating the device configuration tree
+ *
+ * Author : Mikhail Shiryaev
+ * Created : 2019
+ * Modified : 2019
+ */
+
+using System;
+
+namespace Scada.Comm.Devices.OpcUa.UI
+{
+ ///
+ /// Types of updating the device configuration tree.
+ /// Типы обновления дерева конфигурации КП.
+ ///
+ [Flags]
+ public enum TreeUpdateTypes
+ {
+ ///
+ /// No update required.
+ ///
+ None = 0,
+
+ ///
+ /// Current node update is required.
+ ///
+ CurrentNode = 1,
+
+ ///
+ /// Signals update is required.
+ ///
+ UpdateSignals = 2
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/Properties/AssemblyInfo.cs b/ScadaComm/OpenKPs/KpOpcUa/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..a6f4c211f
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using Scada.Comm.Devices;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("KpOpcUa")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Rapid SCADA")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("205c7dab-c3ed-4d4f-8f2f-cfe65d6c4044")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion(KpOpcUaView.KpVersion)]
+[assembly: AssemblyFileVersion(KpOpcUaView.KpVersion)]
diff --git a/ScadaComm/OpenKPs/KpOpcUa/Properties/Resources.Designer.cs b/ScadaComm/OpenKPs/KpOpcUa/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..997ab283c
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Scada.Comm.Devices.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Scada.Comm.Devices.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/ScadaComm/OpenKPs/KpOpcUa/Properties/Resources.resx b/ScadaComm/OpenKPs/KpOpcUa/Properties/Resources.resx
new file mode 100644
index 000000000..1af7de150
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/Properties/Resources.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/app.config b/ScadaComm/OpenKPs/KpOpcUa/app.config
new file mode 100644
index 000000000..0cac7f9a1
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpOpcUa/packages.config b/ScadaComm/OpenKPs/KpOpcUa/packages.config
new file mode 100644
index 000000000..5c05762e1
--- /dev/null
+++ b/ScadaComm/OpenKPs/KpOpcUa/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/ScadaComm/OpenKPs/KpSimulator/KpSimulatorView.cs b/ScadaComm/OpenKPs/KpSimulator/KpSimulatorView.cs
index 4f9c018cb..99d3bf27f 100644
--- a/ScadaComm/OpenKPs/KpSimulator/KpSimulatorView.cs
+++ b/ScadaComm/OpenKPs/KpSimulator/KpSimulatorView.cs
@@ -67,12 +67,12 @@ public override string KPDescr
return Localization.UseRussian ?
"Симулятор устройства.\n\n" +
"Команды ТУ:\n" +
- "4 (бинарная) - установить состояние реле,\n" +
+ "4 (бинарная) - установить состояние реле;\n" +
"5 (бинарная) - установить аналоговый выход." :
"Device simulator.\n\n" +
"Commands:\n" +
- "4 (standard) - set relay state.\n" +
+ "4 (standard) - set relay state;\n" +
"5 (standard) - set analog output.";
}
}
diff --git a/ScadaComm/OpenKPs/KpSnmp/Config.cs b/ScadaComm/OpenKPs/KpSnmp/Config.cs
index 7faca7a93..0b48795fa 100644
--- a/ScadaComm/OpenKPs/KpSnmp/Config.cs
+++ b/ScadaComm/OpenKPs/KpSnmp/Config.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,19 +20,20 @@
*
* Author : Mikhail Shiryaev
* Created : 2015
- * Modified : 2016
+ * Modified : 2019
*/
using System;
using System.Collections;
using System.Collections.Generic;
+using System.IO;
using System.Xml;
namespace Scada.Comm.Devices.KpSnmp
{
///
- /// Device communication configuration
- /// Конфигурация связи с КП
+ /// Device communication configuration.
+ /// Конфигурация связи с КП.
///
internal class Config : ITreeNode
{
@@ -369,9 +370,12 @@ public bool Save(string fileName, out string errMsg)
///
/// Получить имя файла конфигурации
///
- public static string GetFileName(string configDir, int kpNum)
+ public static string GetFileName(string configDir, int kpNum, string cmdLine)
{
- return configDir + "KpSnmp_" + CommUtils.AddZeros(kpNum, 3) + ".xml";
+ string shortFileName = string.IsNullOrEmpty(cmdLine) ?
+ "KpSnmp_" + CommUtils.AddZeros(kpNum, 3) + ".xml" :
+ cmdLine.Trim();
+ return Path.Combine(configDir, shortFileName);
}
}
}
diff --git a/ScadaComm/OpenKPs/KpSnmp/FrmConfig.cs b/ScadaComm/OpenKPs/KpSnmp/FrmConfig.cs
index f609215ef..2caceb4d9 100644
--- a/ScadaComm/OpenKPs/KpSnmp/FrmConfig.cs
+++ b/ScadaComm/OpenKPs/KpSnmp/FrmConfig.cs
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Mikhail Shiryaev
+ * Copyright 2019 Mikhail Shiryaev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
*
* Author : Mikhail Shiryaev
* Created : 2015
- * Modified : 2015
+ * Modified : 2019
*/
using Scada.UI;
@@ -31,13 +31,14 @@
namespace Scada.Comm.Devices.KpSnmp
{
///
- /// Device properties form
- /// Форма настройки свойств КП
+ /// Device properties form.
+ /// Форма настройки свойств КП.
///
public partial class FrmConfig : Form
{
private AppDirs appDirs; // директории приложения
private int kpNum; // номер настраиваемого КП
+ private string cmdLine; // командная строка КП
private Config config; // конфигурация КП
private string configFileName; // имя файла конфигурации КП
private bool modified; // признак изменения конфигурации
@@ -53,6 +54,7 @@ private FrmConfig()
appDirs = null;
kpNum = 0;
+ cmdLine = "";
config = new Config();
configFileName = "";
modified = false;
@@ -183,14 +185,15 @@ private void SetButtonsEnabled()
///
/// Отобразить форму модально
///
- public static void ShowDialog(AppDirs appDirs, int kpNum)
+ public static void ShowDialog(AppDirs appDirs, int kpNum, string cmdLine)
{
- if (appDirs == null)
- throw new ArgumentNullException("appDirs");
+ FrmConfig frmConfig = new FrmConfig
+ {
+ appDirs = appDirs ?? throw new ArgumentNullException("appDirs"),
+ kpNum = kpNum,
+ cmdLine = cmdLine
+ };
- FrmConfig frmConfig = new FrmConfig();
- frmConfig.appDirs = appDirs;
- frmConfig.kpNum = kpNum;
frmConfig.ShowDialog();
}
@@ -217,7 +220,7 @@ private void FrmConfig_Load(object sender, EventArgs e)
Text = string.Format(Text, kpNum);
// загрузка конфигурации КП
- configFileName = Config.GetFileName(appDirs.ConfigDir, kpNum);
+ configFileName = Config.GetFileName(appDirs.ConfigDir, kpNum, cmdLine);
if (File.Exists(configFileName) && !config.Load(configFileName, out errMsg))
ScadaUiUtils.ShowError(errMsg);
Modified = false;
diff --git a/ScadaComm/OpenKPs/KpSnmp/KpSnmp.csproj b/ScadaComm/OpenKPs/KpSnmp/KpSnmp.csproj
index e17a2bca4..fb7546e65 100644
--- a/ScadaComm/OpenKPs/KpSnmp/KpSnmp.csproj
+++ b/ScadaComm/OpenKPs/KpSnmp/KpSnmp.csproj
@@ -9,8 +9,9 @@
Properties
Scada.Comm.Devices
KpSnmp
- v4.0
+ v4.6.1
512
+
true
@@ -20,6 +21,7 @@
DEBUG;TRACE
prompt
4
+ false
pdbonly
@@ -28,6 +30,7 @@
TRACE
prompt
4
+ false
@@ -41,13 +44,11 @@
..\..\..\ScadaData\ScadaData\bin\Release\ScadaData.dll
-
- ..\..\..\..\dll\SNMP\SharpSnmpLib.Full.dll
-
-
- ..\..\..\..\dll\SNMP\SharpSnmpLib.Portable.dll
+
+ ..\packages\Lextm.SharpSnmpLib.11.3.0\lib\net452\SharpSnmpLib.dll
+
@@ -119,7 +120,9 @@
-
+
+
+