Основные возможности Dell EMC Cloud для Microsoft Azure |
Метки: author Orest_ua хранение данных серверная оптимизация облачные вычисления it- инфраструктура блог компании мук dell emc облака |
[Из песочницы] Когда 2 + 2 = «4» |
Итоговая таблица для тех кто не хочет много читать | |||
Результат | Ошибка компиляции | False | True |
Языки | C++ Pascal FORTRAN-95 Java C# Go Rust Haskel |
C Lisp Python Lua Ruby |
JavaScript PHP Perl |
cout << (2 + 2 == "4") << endl;
writeln((2 + 2) = "4");
Print *, 2 + 2 == "4"
System.out.println((2 + 2) == "4");
Console.WriteLine((2 + 2) == "4");
fmt.Printf(2 + 2 == "4")
println!("{:?}", 2 + 2 == "4");
main = putStrLn (show ((2 + 2) == "4"))
printf("%i\n",(2 + 2 == "4"));
(write (eq "123" 123))
print (2 + 2 == "4")
print(2 + 2 == "4")
puts 2 + 2 == "4";
console.log(2 + 2 == "4");
echo (2 + 2 == "4");
print 2 + 2 == "4";
print 0x2 + 0x2 == "0x4";
Метки: author igormich88 программирование ненормальное программирование пятничный пост юмор |
Как наука о данных помогает развитию медицины. Лекция в Яндексе |
|
Введение в Pixi.js или пишем мини-игру за 20 минут |
npm install pixi.js
node_module/pixi.js/dist/pixi.js
var width = window.innerWidth; //получаем ширину экрана
var height = window.innerHeight; // получаем высоту экрана
var app; //создаем глобальную переменную нашей игры
var model = {
createCanvas: function() {
app = new PIXI.Application(width, height); //создаем холст
document.body.appendChild(app.view); //выводим его в тело страницы
}
}
model.createCanvas();
var view = {
loadGame: function(){
model.createCanvas();
}
}
view.loadGame(); //запускаем игру
var width = window.innerWidth; //получаем ширину экрана
var height = window.innerHeight; // получаем высоту экрана
var app; //создаем глобальную переменную нашей игры
var colors = [0xFFFF0B, 0xFF700B, 0x4286f4, 0x4286f4, 0xf441e8, 0x8dff6d, 0x41ccc9, 0xe03375, 0x95e032, 0x77c687, 0x43ba5b, 0x0ea3ba]; //массив цветов, 0x вместо #
var model = {
createCanvas: function() {
app = new PIXI.Application(width, height); //создаем холст
document.body.appendChild(app.view); //выводим его в тело страницы
},
drawCircle: function() {
rand = Math.floor(Math.random() * colors.length); //генерим рандомное число(в промежутке от 0 до количества цветов в массиве цветов)
var radius = 50; //радиус круга
var inAreaX = width - 100; //возможные координаты по оси X, которые может занимать круг, ширина страницы минус его диаметр
var circleY = -50; //круг должен создаваться за пределами холста(чтобы глянуть, отрисовался ли круг, измените отрицательное значение на положительное)
var circleX = Math.floor(Math.random()* inAreaX); //создаем круг в рандомном месте по оси X
var circle = new PIXI.Graphics(); //создаем новый графический элемент
circle.lineStyle(0); //начинаем рисовать
circle.beginFill(colors[rand], 1); //задаем рандомный цвет
circle.drawCircle(circleX, circleY, radius); //рисуем кружок, ведь он наш дружок
circle.endFill(); //закончили отрисовку
circle.interactive = true; //делаем круг интерактивным
circle.buttonMode = true; //меняем курсор при наведении
app.stage.addChild(circle); //выводим круг на холсте
circle.on('pointerdown', controller.clearFigure); //добавляем возможность при клике на фигуру удалить её
}
}
var view = {
loadGame: function() {
model.createCanvas();
model.drawCircle();//отрисовываем кружок, пока один раз
}
}
var controller = {
clearFigure: function(){
this.clear(); //удаляем фигуры по которой кликнули
}
}
view.loadGame();
app.stage.addChild(circle);
— это очень важная команда в Pixi, так как в этом фреймворке, чтобы на холсте что-то появилось, кроме создания объекта, его нужно обязательно вывести. В 70% процентах случаев, если вы все сделали правильно, а объект на холсте не отображается, то скорее всего вы забыли его вывести с помощью этой команды.var colors = [0xFFFF0B, 0xFF700B, 0x4286f4, 0x4286f4, 0xf441e8, 0x8dff6d, 0x41ccc9, 0xe03375, 0x95e032, 0x77c687, 0x43ba5b, 0x0ea3ba]; //массив цветов
var gravity = 4;
var figuresAmount = -1; //количество созданных фигур(не ноль, потому как с нуля мы начинаем считать фигуры)
var figure = []; //массив хранящий нашу фигуру
{
— наши добавленные переменные.circle.buttonMode = true; //меняем курсор при наведении
figuresAmount++; //увеличиваем количество созданных шариков
figure.push(circle); //обратиться на прямую к объекту circle мы не можем, поэтому отправляем его в массив
app.stage.addChild(circle); //выводим круг на холсте
app.ticker.add();
model.drawCircle(); //рисуем круг в первый раз
setInterval(model.drawCircle, 500); //рисуем шарик каждые пол секунды
app.ticker.add(function() { //постоянное обновление холста
for (var i = 0; i < figuresAmount; i++) {
figure[i].position.y += gravity; //заставляем гравитацию работать
}
});
} //закрываем функцию loadGame();
circle.buttonMode = true; //меняем курсор при наведении
circle.live = true; //указываем что наш шарик жив и не пал жертвой выстрела
figuresAmount++;
circle.num = figuresAmount; //даем нашему кругу порядковый номер
figure.push(circle); //обратиться на прямую к объекту circle мы не можем, поэтому отправляем его в массив
gameOver: function() {
var style = new PIXI.TextStyle({ //стили для текста
fill: '0xffffff',
fontSize: 36,
});
var gameOverText = new PIXI.Text('Game Over', style); //собственно выводимый текст
gameOverText.x = width / 2; //центрируем относительно экрана
gameOverText.y = height / 2; //центрируем относительно экрана
gameOverText.pivot.x = 50; //выравниваем по оси х
gameOverText.pivot.y = 50; // выравниваем по оси y
app.stage.addChild(gameOverText); //выводим на холсте
}
clearFigure: function() {
this.clear();
figure[this.num].live = false;
}
app.ticker.add(function() { //постоянное обновление холста
for (var i = 0; i < figuresAmount; i++) {
figure[i].position.y += gravity; //заставляем гравитацию работать
if (figure[i].position.y > height && figure[i].live == true) {//проверяет столкнулся ли шарик с низом страницы и если он жив, не пропускает его, а отменяет выводит на экран "игра окончена и завершает действие гравитации"
model.gameOver();
return false;
}
}
Метки: author Dmitriy_Rudenko разработка игр javascript canvas pixi.js gamedev html5 казуальные игры 2d игры pixi |
Старые технологии на новый лад. FreeBSD Jails + CBSD Project |
Метки: author Dorlas системное администрирование серверная оптимизация it- инфраструктура *nix freebsd jail cbsd security containers |
Ваш бренд — на EuroPython 2017 |
Метки: author tyomitch я пиарюсь python europython конференция |
[Из песочницы] Уменьшение операций чтения/записи на Raspberry Pi |
sudo iotop -o -a
sudo apt-get install iotop
sudo nano /etc/rsyslog.conf
$ModLoad imuxsock # provides support for local system logging
$ModLoad imklog # provides kernel logging support
# /etc/rsyslog.conf Configuration file for rsyslog.
#
# For more information see
# /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html
#################
#### MODULES ####
#################
#$ModLoad imuxsock # provides support for local system logging
#$ModLoad imklog # provides kernel logging support
#$ModLoad immark # provides --MARK-- message capability
# provides UDP syslog reception
#$ModLoad imudp
#$UDPServerRun 514
# provides TCP syslog reception
#$ModLoad imtcp
#$InputTCPServerRun 514
###########################
#### GLOBAL DIRECTIVES ####
###########################
#
# Use traditional timestamp format.
# To enable high precision timestamps, comment out the following line.
#
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
#
# Set the default permissions for all log files.
#
$FileOwner root
$FileGroup adm
$FileCreateMode 0640
$DirCreateMode 0755
$Umask 0022
#
# Where to place spool and state files
#
$WorkDirectory /var/spool/rsyslog
#
# Include all config files in /etc/rsyslog.d/
#
$IncludeConfig /etc/rsyslog.d/*.conf
###############
#### RULES ####
###############
#
# First some standard log files. Log by facility.
#
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
#cron.* /var/log/cron.log
daemon.* -/var/log/daemon.log
kern.* -/var/log/kern.log
lpr.* -/var/log/lpr.log
mail.* -/var/log/mail.log
user.* -/var/log/user.log
#
# Logging for the mail system. Split it up so that
# it is easy to write scripts to parse these files.
#
mail.info -/var/log/mail.info
mail.warn -/var/log/mail.warn
mail.err /var/log/mail.err
#
# Logging for INN news system.
#
news.crit /var/log/news/news.crit
news.err /var/log/news/news.err
news.notice -/var/log/news/news.notice
#
# Some "catch-all" log files.
#
*.=debug;\
auth,authpriv.none;\
news.none;mail.none -/var/log/debug
*.=info;*.=notice;*.=warn;\
auth,authpriv.none;\
cron,daemon.none;\
mail,news.none -/var/log/messages
#
# Emergencies are sent to everybody logged in.
#
*.emerg :omusrmsg:*
#
# I like to have messages displayed on the console, but only on a virtual
# console I usually leave idle.
#
#daemon,mail.*;\
# news.=crit;news.=err;news.=notice;\
# *.=debug;*.=info;\
# *.=notice;*.=warn /dev/tty8
# The named pipe /dev/xconsole is for the `xconsole' utility. To use it,
# you must invoke `xconsole' with the `-file' option:
#
# $ xconsole -file /dev/xconsole [...]
#
# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
# busy site..
#
daemon.*;mail.*;\
news.err;\
*.=debug;*.=info;\
*.=notice;*.=warn |/dev/xconsole
sudo iotop -o -a
sudo umount /dev/sdb2
sudo tune2fs -O ^has_journal /dev/sdb2
sudo e2fsck -f /dev/sdb2
dmesg | grep EXT4
[ 5890.967580] EXT4-fs (sdb2): mounted filesystem without journal. Opts: (null)
sudo iotop -o -a
Метки: author Anarhist2017 настройка linux raspberry pi linux |
[Перевод] Как снизить количество брошенных корзин в онлайн продажах |
Метки: author AntoniusFirst графический дизайн веб-дизайн usability ux apm ecommerce |
[recovery mode] Выпуск бета-версии многолинейного TAPI драйвера для Terminal Services и анонс презентации 3CX v15.5 |
C:\ProgramData\3CXMultiLineTapi and edit 3CXTapi.ini
|
[recovery mode] JSF 2 + Maven + Jetty. CDI, форма и AJAX |
@ManagedBean
признана устаревшей. Т.е. чтобы обратиться к своему Java-коду из формы нам потребуется добавить в проект CDI (Context and Dependency Injection) — Weld.pom.xml
добавляем:
...
org.jboss.weld.servlet
weld-servlet
2.4.3.Final
/src/main/webapp/WEB-INF/beans.xml
:
Там прям особая специфика в регистрации listener'а?
/src/main/webapp/WEB-INF/web.xml
добавляем:
org.jboss.weld.environment.servlet.Listener
...
mvn jetty:run
/src/main/webapp/WEB-INF/jetty-env.xml
:
-org.eclipse.jetty.server.handler.ContextHandler
-org.eclipse.jetty.servlet.FilterHolder
-org.eclipse.jetty.servlet.ServletContextHandler
-org.eclipse.jetty.servlet.ServletHolder
BeanManager
javax.enterprise.inject.spi.BeanManager
org.jboss.weld.resources.ManagerObjectFactory
/src/main/webapp/WEB-INF/web.xml
добавляем:
BeanManager
javax.enterprise.inject.spi.BeanManager
...
@ManagedBean
будет работать @Named
./src/main/java/ru/habr/FormData.java
:package ru.habr;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class FormData {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
/src/main/java/ru/habr/FormCtrl.java
:package ru.habr;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
@Named
@RequestScoped
public class FormCtrl {
@Inject
FormData formData;
public String doAction() {
// Очень полезные действия с данными...
System.out.println(formData.getUsername());
System.out.println(formData.getPassword());
return null;
}
}
FormData
все понятно, то на FormCtrl
стоит взглянуть пристальней:@Inject
, данные формы будут доступны в классе-обработчике в поле formData
./src/main/webapp/index.xhtml
добавляем:
...
mvn jetty:run
http://127.0.0.1:8080/
/src/main/webapp/index.xhtml
внесем пару правок:
...
...
xmlns:f="http://xmlns.jcp.org/jsf/core"
. Без этого можно реализовать AJAX, но обрабатывать придется ручками, что долго и неблагодарно.h:commandButton
появился
. В execute
— указываем что необходимо отправить на сервер (всю форму/все поля формы), в render
— что необходимо обновить с сервера после выполнения AJAX.f:ajax
можно писать отдельный. Сейчас достаточно знать только что с его помощью можно отправить/обновить не только текущую форму на странице, а любой элемент страницы.
Метки: author metej java jsf2 jsf servlet jetty maven |
Запуск систем без тестовой эксплуатации |
Метки: author Northeast управление разработкой управление проектами управление продуктом бизнес-модели waterfall качество системы тестовая эксплуатация |
[Из песочницы] Работа в команде. Как помирить дизайнера и верстальщика |
Метки: author idzenski веб-дизайн верстка дизайн frontend kit |
[Из песочницы] 3 cпособа нарушить Single Responsibility Principle |
Метки: author spolispastom разработка под ios программирование ооп objective c srp singleton mvc mvp mvvm ios development |
Задачка: найти треугольник с меньшим периметром |
Метки: author AnROm математика геометрия треугольник с меньшим периметром |
Решение транспортной задачи в среде моделирования Python |
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy
from cvxopt.modeling import variable, op
def count(mass1, mass2, mass3, mass4,mass5,mass6,z):
x_non_negative = (x >= 0) #общее условие для всех х
problem =op(z,[mass1,mass2,mass3,mass4 ,mass5,mass6, x_non_negative])
problem.solve(solver='glpk')
problem.status
print("Минимальная стоимость закупки -",problem.objective.value()[0])
print("Матрица закупок:")
for i in [1,4,7]:
print("|",x.value[i-1],"|", x.value[i],"|", x.value[i+1],"|")
x = variable(9, 'x')
z=(7*x[0] + 3*x[1] +6* x[2] +4*x[3] + 8*x[4] +2* x[5]+x[6] + 5*x[7] +9* x[8])# целевая функция
mass1 = (x[0] + x[1] +x[2] <= 74)# условие для первой строки матрицы закупок
mass2 = (x[3] + x[4] +x[5] <= 40)#условие для второй строки матрицы закупок
mass3 = (x[6] + x[7] + x[8] <= 36)# #условие для третьей строки матрицы закупок
mass4 = (x[0] + x[3] + x[6] == 20)# условия для столбца
mass5 = (x[1] +x[4] + x[7] == 45)#условия для столбца
mass6 = (x[2] + x[5] + x[8] == 30)#условия для столбца
count (mass1, mass2, mass3, mass4, mass5,mass6,z)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy
from cvxopt.modeling import variable, op
def count(mass1, mass2, mass3, mass4,mass5,mass6,mass7,z):
x_non_negative = (x >= 0)
problem =op(z,[mass1,mass2,mass3,mass4 ,mass5,mass6 ,mass7,x_non_negative])
problem.solve(solver='glpk')
problem.status
print("Минимальная стоимость закупки -",problem.objective.value()[0])
print("Матрица закупок:")
for i in [1,4,7]:
print("|",x.value[i-1],"|", x.value[i],"|", x.value[i+1],"|")
x = variable(9, 'x')
z=(7*x[0] + 3*x[1] +6* x[2] +4*x[3] + 8*x[4] +2* x[5]+x[6] + 5*x[7] +9* x[8])
mass1 = (x[0] + x[1] +x[2] <= 74)
mass2 = (x[3] + x[4] +x[5] <= 40)
mass3 = (x[6] + x[7] + x[8] <= 36)
mass4 = (x[0] + x[3] + x[6] == 20)
mass5 = (x[1] +x[4] + x[7] == 45)
mass6 = (x[2] + x[5] + x[8] == 30)
mass7 = (x[1] == 30)
count(mass1, mass2, mass3, mass4,mass5,mass6,mass7,z)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy
from cvxopt.modeling import variable, op
def count(mass1, mass2, mass3, mass4,mass5,mass6,z):
x_non_negative = (x >= 0)
problem =op(z,[mass1,mass2,mass3,mass4 ,mass5,mass6, x_non_negative])
problem.solve(solver='glpk')
problem.status
print("Минимальная стоимость закупки -",problem.objective.value()[0])
print("Матрица закупок:")
for i in [1,4,7]:
print("|",x.value[i-1],"|", x.value[i],"|", x.value[i+1],"|")
x = variable(9, 'x')
z=(7*x[0] + 3*x[1] +6* x[2] +4*x[3] + 8*x[4] +2* x[5]+x[6] + 5*x[7] +9* x[8])
mass1 = (x[0] + x[1] +x[2] <= 74)
mass2 = (x[3] + x[4] +x[5] <= 40)
mass3 = (x[6] + x[7] + x[8] <= 36)
mass4 = (x[0] + x[3] + x[6] == 20)
mass5 = (x[1] +x[4] + x[7] == 30)
mass6 = (x[2] + x[5] + x[8] == 30)
count(mass1, mass2, mass3, mass4,mass5,mass6,z)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy
from cvxopt.modeling import variable, op
def count(mass1, mass2, mass3, mass4,mass5,mass6,z):
x_non_negative = (x >= 0)
problem =op(z,[mass1,mass2,mass3,mass4 ,mass5,mass6, x_non_negative])
problem.solve(solver='glpk')
problem.status
print("Минимальная стоимость закупки -",problem.objective.value()[0])
print("Матрица закупок:")
for i in [1,4,7]:
print("|",x.value[i-1],"|", x.value[i],"|", x.value[i+1],"|")
x = variable(9, 'x')
z=(7*x[0] + 3*x[1] +6* x[2] +4*x[3] + 8*x[4] +2* x[5]+x[6] + 5*x[7] +9* x[8])
mass1 = (x[0] + x[1] +x[2] <= 74)
mass2 = (x[3] + x[4] +x[5] == 40)
mass3 = (x[6] + x[7] + x[8] <= 36)
mass4 = (x[0] + x[3] + x[6] == 20)
mass5 = (x[1] +x[4] + x[7] == 45)
mass6 = (x[2] + x[5] + x[8] == 30)
count(mass1, mass2, mass3, mass4,mass5,mass6,z)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy
from cvxopt.modeling import variable, op
def count(mass1, mass2, mass3, mass4,mass5,mass6,z):
x_non_negative = (x >= 0)
problem =op(z,[mass1,mass2,mass3,mass4 ,mass5,mass6, x_non_negative])
problem.solve(solver='glpk')
problem.status
print("Минимальная стоимость закупки -",problem.objective.value()[0])
print("Матрица закупок:")
for i in [1,4,7]:
print("|",x.value[i-1],"|", x.value[i],"|", x.value[i+1],"|")
x = variable(9, 'x')
z=(7*x[0] + 3*x[1] +6* x[2] +4*x[3] + 8*x[4] +2* x[5]+x[6] + 5*x[7] +9* x[8])
mass1 = (x[0] + x[1] +x[2] <= 74)
mass2 = (x[3] + x[4] +x[5] == 30)
mass3 = (x[6] + x[7] + x[8] == 30)
mass4 = (x[0] + x[3] + x[6] == 20)
mass5 = (x[1] +x[4] + x[7] == 45)
mass6 = (x[2] + x[5] + x[8] == 30)
count(mass1, mass2, mass3, mass4,mass5,mass6,z)
Метки: author Scorobey python решатель cvxopt среда моделирования python закрытая транспортная задача |
[Из песочницы] Синхронизация структуры базы данных между приложениями |
Каждый, кто когда-либо разрабатывал приложения, использующие базу данных, наверняка сталкивался с проблемой обновления структуры БД при разворачивании и обновлении приложения.
Чаще всего используется простой подход — создание набора SQL-скриптов для модификации структуры БД от версии к версии. Конечно, есть такой мощный инструмент, как Red gate, но он во-первых небесплатный, во-вторых не решает проблему полной автоматизации обновления.
Технология migrations, впервые появившаяся в ОРМ Hibernate и реализованная в Linq, очень хороша и удобна, но подразумевает стратегию разработки структуры БД code first, что весьма трудоемко для уже существующих проектов, а использование в БД триггеров, хранимых процедур и функций делает задачу перехода на code first практически невыполнимой.
В данной статье предлагается альтернативный подход к решению этой задачи, использующий хранение эталонной структуры БД в XML-файле и автоматическую генерацию SQL-скрипта на основе сравнения эталонной и существующей структуры. Итак, начнем...
Для экспериментов будем использовать БД DbSyncSample. Скрипт для создания БД приведен ниже.
USE [DbSyncSample]
GO
/****** Object: Table [dbo].[Orders] Script Date: 06/01/2017 10:37:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Orders](
[Id] [int] IDENTITY(1,1) NOT NULL,
[OrderNumber] [nvarchar](50) NULL,
[OrderTime] [datetime] NULL,
[TotalCost] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_Orders_OrderNumber] ON [dbo].[Orders]
(
[OrderNumber] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Details] Script Date: 06/01/2017 10:37:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Details](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Descript] [nvarchar](150) NULL,
[OrderId] [int] NULL,
[Cost] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_Details] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Trigger [Details_Modify] Script Date: 06/01/2017 10:37:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[Details_Modify]
ON [dbo].[Details]
AFTER INSERT,UPDATE
AS
BEGIN
UPDATE Orders
SET TotalCost = s.Total
FROM (
SELECT i.OrderId OId, SUM(d.Cost) Total
FROM Details d
JOIN inserted i ON d.OrderId=i.OrderId
GROUP BY i.OrderId
) s
WHERE Id=s.OId
END
GO
/****** Object: Trigger [Details_Delete] Script Date: 06/01/2017 10:37:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[Details_Delete]
ON [dbo].[Details]
AFTER DELETE
AS
BEGIN
UPDATE Orders
SET TotalCost = s.Total
FROM (
SELECT i.OrderId OId, SUM(d.Cost) Total
FROM Details d
JOIN deleted i ON d.OrderId=i.OrderId
GROUP BY i.OrderId
) s
WHERE Id=s.OId
END
GO
/****** Object: Default [DF_Details_Cost] Script Date: 06/01/2017 10:37:43 ******/
ALTER TABLE [dbo].[Details] ADD CONSTRAINT [DF_Details_Cost] DEFAULT ((0)) FOR [Cost]
GO
/****** Object: Default [DF_Orders_TotalCost] Script Date: 06/01/2017 10:37:43 ******/
ALTER TABLE [dbo].[Orders] ADD CONSTRAINT [DF_Orders_TotalCost] DEFAULT ((0)) FOR [TotalCost]
GO
/****** Object: ForeignKey [FK_Details_Orders] Script Date: 06/01/2017 10:37:43 ******/
ALTER TABLE [dbo].[Details] WITH CHECK ADD CONSTRAINT [FK_Details_Orders] FOREIGN KEY([OrderId])
REFERENCES [dbo].[Orders] ([Id])
GO
ALTER TABLE [dbo].[Details] CHECK CONSTRAINT [FK_Details_Orders]
GO
Для экспериментов создаем консольное приложение. Подключаем к нему nuget-пакет Shed.DbSync.
Структуру БД в виде XML получаем следующим образом:
class Program
{
private const string OrigConnString = "data source=.;initial catalog=FiocoKb;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
static void Main(string[] args)
{
// получаем XML со структурой БД
var db = new Shed.DbSync.DataBase(OrigConnString);
var xml = db.GetXml();
File.WriteAllText("DbStructure.xml", xml);
}
}
После запуска программы в файле DbStructure.xml видим следующее:
0
1
int
4
false
true
false
2
nvarchar
100
true
false
false
3
datetime
8
true
false
false
4
decimal
9
false
false
false
1
CLUSTERED
true
true
false
1
1
false
2
NONCLUSTERED
false
false
false
2
1
false
1
4
((0))
1
int
4
false
true
false
2
nvarchar
300
true
false
false
3
int
4
true
false
false
4
decimal
9
false
false
false
1
CLUSTERED
true
true
false
1
1
false
1
2137058649
1
3
1
NO_ACTION
NO_ACTION
4
((0))
CREATE TRIGGER [dbo].[Details_Modify]
ON dbo.Details
AFTER INSERT,UPDATE
AS
BEGIN
UPDATE Orders
SET TotalCost = s.Total
FROM (
SELECT i.OrderId OId, SUM(d.Cost) Total
FROM Details d
JOIN inserted i ON d.OrderId=i.OrderId
GROUP BY i.OrderId
) s
WHERE Id=s.OId
END
SQL_TRIGGER
CREATE TRIGGER [dbo].[Details_Delete]
ON dbo.Details
AFTER DELETE
AS
BEGIN
UPDATE Orders
SET TotalCost = s.Total
FROM (
SELECT i.OrderId OId, SUM(d.Cost) Total
FROM Details d
JOIN deleted i ON d.OrderId=i.OrderId
GROUP BY i.OrderId
) s
WHERE Id=s.OId
END
SQL_TRIGGER
Теперь научимся использовать полученный XML. Создаем еще одну пустую БД DbSyncSampleCopy, в код нашей консольной программы добавляем следующее:
class Program
{
private const string OrigConnString = "data source=.;initial catalog=DbSyncSample;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
private const string TargetConnString = "data source=.;initial catalog=DbSyncSampleCopy;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
static void Main(string[] args)
{
// получаем XML со структурой эталонной БД
var dborig = new Shed.DbSync.DataBase(OrigConnString);
var xml = dborig.GetXml();
File.WriteAllText("DbStructure.xml", xml);
// если нужно предварительно очистить структуру целевой БД, используем
// Shed.DbSync.DataBase.ClearDb(TargetConnString);
// обновляем структуру целевой БД
var dbcopy = Shed.DbSync.DataBase.CreateFromXml(xml);
dbcopy.UpdateDb(TargetConnString);
// на самом деле можно обойтись одной строкой:
// dborig.UpdateDb(TargetConnString);
// dbcopy создаем только для демонстрации создания объекта базы из XML
}
}
После запуска программы можно убедиться, что в DbSyncSampleCopy появилась структура таблиц, идентичная эталонной БД. Эксперименты с изменением эталонной структуры и обновлением целевой оставляю читателю.
В сценариях тестирования может понадобиться создание тестовой БД каждый раз с нуля. В этом случае будет полезно использовать функцию Shed.DbSync.DataBase.ClearDb(string connString)
Слежение за структурой оформим в виде отдельной функции, которую следует вызывать при старте/рестарте приложения либо в другом месте по желанию разработчика.
static void SyncDb()
{
// автоматическое слежение за структурой БД
Shed.DbSync.DataBase.Syncronize(OrigConnString,
@"Struct\DbStructure.xml", // путь к файлу структуры
@"Struct\Logs", // путь к папке логов синхронизации
@"Struct\update_script.sql" // (необяз.) в случае определения этого параметра
// в него будет записан скрипт, сгенерированный
// для обновления БД
);
}
Слежение производится при помощи параметра (тега) Version в XML. Сценарий использования процедуры такой:
Эксперименты оставляю читателям. Успехов вам!
Метки: author chiefivs программирование .net ms sql синхронизация баз данных database syncronize dbsync sql shed shed.dbsync |
«Интересная тема для обсуждения — относительность тестового покрытия»: T-Systems о тестировании |
Метки: author phillennium тестирование веб-сервисов тестирование it-систем блог компании jug.ru group t-systems тестирование конференция гейзенбаг |
[Из песочницы] Электронная подпись в Битрикс24. Теория и опыт внедрения |
Хорошо отлаженный механизм быстрого реагирования на изменяющуюся реальность — одно из правил успешного бизнеса. Тенденции современности говорят быть мобильнее, быстрее, удобнее для клиента, для самих себя. Но мы тратим множество часов на подписание документов, требующих участия не одной и не двух сторон. Еще дольше, когда работники за сотни километров. Путь к мобильности может лежать через ЭЦП — Электронную Цифровую Подпись.
В этой статье поговорим о видах, плюсах, минусах ЭЦП, возможностях интеграции и как на практике происходит внедрение ЭЦП на корпоративный портал Битрикс24.
ЭЦП инструмент удобный и давно знакомый почти каждому, кто работает с удаленным документооборотом. Однако реализовать ЭЦП на Корпоративном портале Битрикс24 оказалось задачей нетривиальной. Наше знакомство с подобной интеграцией началось с двух клиентских задач:
Однако вначале давайте разберёмся «что это такое — ЭЦП» и «с чем её едят».
Личная подпись — один из идентификаторов человека. Законодательно и безнаказанно повторить её может только этот человек, только его рука. А если она (рука) не дееспособна, а важная сделка не должна сорваться — проводится специальная нотариальная процедура и подпись ставит доверенный человек.
Не будем скрывать — подпись подделывают. Дело мастерства и привычки. Хоть и выработаны технологии сверки — по нажиму, толщине, линиям — гарантом является только обладатель подписи. Любая непонятная другим закорючка признаётся легитимной со слов её автора.
Значимые сделки требуют второй «защиты личной подписи». Человека просят предъявить документ, удостоверяющий личность, с фотографией, поставить подпись в присутствии подтверждающего лица — нотариуса. Последний ручается — подписант выполнит пункты договора, доверенности, заявления.
Но участники процесса не всегда находятся в досягаемости друг от друга. Есть почта, но это затратно и долго.
Здесь и перейдем к электронной подписи. Она бывает трех видов (ФЗ от 06.04.2011 № 63-ФЗ «Об электронной подписи»).
Простая электронная подпись (ПЭП). Это привычная для нас пара «логин-пароль». Явные недостатки ПЭП начинаются с низкой надежности и заканчиваются отсутствием механизма защиты от изменений. Иногда её применение возможно даже в суде (например, документ, переданный по электронной почте, там ведь есть логин-пароль), но это сложно и далеко не во всех случаях срабатывает.
Неквалифицированная электронная подпись. Часто используют приставку «усиленная», но в законодательстве такого определения нет. Она, в отличие от ПЭП:
Квалифицированная электронная подпись. Также используют приставку «усиленная». Это то же самое, что и неквалифицированная, но с двумя важными дополнениями:
![]() |
Везде по тексту под ЭЦП понимается именно усиленная квалифицированная или усиленная неквалифицированная подпись. |
Подведём промежуточный итог. Неквалифицированная подпись будет действительна только между лицами, которые заключили соглашение и признают подпись действительной. Квалифицированной подписью можно заверить практически любые документы. Они будут легитимны. Работа с УЦ — это аналог нотариального заверения. Исключение составляют счет-фактуры для зачета НДС. Электронные счет-фактуры передаются только через операторов электронного документооборота. Даже счет-фактура, подписанная по всем правилам ЭЦП, отправленная по электронной почте, не будет легитимной.
Вопрос выбора того или иного вида подписи зависит только от потребностей бизнес-процессов.
Следующий вопрос — что мы получаем на выходе? Документ с факсимиле, с водяным знаком? Какой формат документа с ЭЦП можно получить?
Есть два возможных исхода:
Контейнер («обычная» подпись). Как правило, это архив формата «*.sig». Там находятся подписанные документы, данные о сертификате, с помощью которого сделана подпись и сама подпись. Файл такого формата открывается только с помощью специального ПО. Использовать привычные Adobe Reader или MS Office не получится — они не приспособлены для работы с криптографией. Кроме самих данных, подписи и сертификата, пользователь может сохранить неподписанную копию документа.
«Встроенная» подпись. Мы получаем файл того же формата, что подали на подпись. В специальных областях подписанного документа размещаются данные о сертификате. Такая подпись возможна только для файлов формата:
PDF. Подпись невидима в теле документа, но отображается на панели подписей. По желанию, её можно визуализировать, например, в виде штампа. При клике на него видны данные о подписи. Кстати, именно в таком виде, как правило, подписывают и присылают документы ИФНС (также используется вариант xml).
MS Office. Подпись размещается в специальной области документа и видна на панели подписей или в свойствах файла.
XML. Подпись вместе с данными о ней содержится в области значений XML и встраивается в тот же XML-конверт, не изменяя начальные данные.
![]() |
Вариант «встроенной подписи» наиболее удобен в использовании. Именно о нем пойдет речь в материале. |
Один документ разные стороны могут подписать исключительно единообразно: либо только ЭЦП, либо только в бумажном виде (например, договор между Заказчиком и Исполнителем). Подпись в бумажном виде не действительна в электронном, а подпись в электронном виде не действительна в бумажном. Это два разных документа.
Документ можно подписать сколько угодно раз. То есть сначала подписал сотрудник, потом бухгалтер, потом Генеральный директор.
Несколько документов, подписанных ЭЦП, можно в одном пакете (архиве) подписать общей совершенно другой ЭЦП. Другие же документы в пакете с ЭЦП также остаются легитимными.
Документы можно подписывать ЭЦП в автоматическом режиме. Предположим, сформировали счет на оплату в CRM — он автоматически подписался. Можно его отправлять клиенту, счет легитимен.
С видами подписей и вариантами результата разобрались. Теперь перейдем к выбору инструментов. Держим в уме, что есть конкретная задача — интегрировать ЭЦП на корпоративном портале Битрикс24.
Важный шаг — выбор компании поставщика ПО для реализации ЭЦП. Выбор пал на признанного лидера отрасли с широким инструментарием, которому нет аналога в РФ — КриптоПро. Для решения нашей задачи поставщик предложил несколько инструментов.
Криптопровайдер КриптоПро CSP устанавливается на ПК пользователя локально. Он подписывает документы, обращаясь к сертификату в реестре, на eToken или RuToken, смарт-картах и других носителях.
Процесс подписи
«Автоматический» режим (т.е. встроив в портал). При загрузке файла на сервер и нажатии на «Подписать» с помощью плагина для браузера будет сделан вызов к КриптоПро CSP. Он подпишет файл выбранными ключами, которые записаны в реестре, либо на eToken клиента. В таком режиме можно сделать только «обычную» (контейнер) и XML подпись, так как передается только хэш документа. Подписать внутрь PDF или документ MS Office не получится.
В «ручном» режиме (т.е. выйдя из портала) подпись генерирует сам клиент с помощью того ПО, которым он пользуется. Например, для подписи документа MS Office должны быть установлены MS Office, КриптоПро CSP и КриптоПро Office Signature. В интерфейсе MS Office пользователь добавляет подпись к документу, используя КриптоПро в качестве криптопровайдера. После этого подписанный файл загружается в личный кабинет функцией «приложить документ».
Плюсы
Минусы
КриптоПро DSS позволяет через браузер создать ЭЦП. Установка программы на ПК пользователя не требуется, подписывать документы можно с любого устройства в любом месте. Сертификаты с ключами хранятся на защищенном модуле в специально развернутых серверах КриптоПро.
В КриптоПро DSS можно настроить способ аутентификации пользователя:
Как происходит процесс подписи документа
Пользователь нажимает «Подписать». Генерируется запрос к КриптоПро DSS на подпись. Портал просит ввести ПИН-код доступа к сертификату (если установлено такое требование), и пользователь получает одноразовый пароль по SMS. После правильного ввода документ будет подписан и сохранён на портале, соответственно, отправлен далее по запланированному маршруту. Клиент видит только конечный результат, переходить в другое окно ему не требуется. Что важно и удобно, результат приходит в том же формате pdf -> pdf, docx -> docx.
Плюсы
Минусы
Это «облегчённая» версия КриптоПро DSS. Она предназначена для подписания документов ЭЦП в браузере с использованием ПО. Отличие от DSS — в качестве криптопровайдера используется ПО установленное на ПК пользователя, что позволяет использовать usb-Token и Смарт-Карты для подписи.
DSS lite — «режим» DSS (т.е. идет в составе продукта DSS). Процесс подписи происходит аналогично DSS. Нажатие «подписать» отправляет файл на сервер DSS lite, тот формирует хэш документа и запрашивает доступ к криптопровайдеру (CSP, установленный на ПК пользователя) с помощью плагина. Выгодное отличие — вы получаете pdf и документы MS Office в исходном виде со встроенной в сам документ подписью, а не в контейнере.
Плюсы
Минусы
![]() |
Кстати, функциональность DSS и DSS lite позволяет добавлять визуальный штамп/печать/подпись по вашему выбору на документ. Сама ЭЦП невидима, а штамп — инструмент визуализации. |
ЭЦП — действительно нужное решение, прежде всего, для неизменности передаваемых документов, мобильности бизнес-процессов и сохранения времени.
Такую задачу поставили и нам — реализовать подпись на корпоративном портале 1С-Битрикс24. Нужен правильный функционал, удобство в использовании и грамотное вложение бюджета.
Но, как известно, теория часто не сопоставима с практикой. Решение реальных задач оказывается сложнее, но интереснее.
Перед крупной биофармацевтической компанией стояло две задачи:
Автоматизировать бизнес-процесс согласования документов.
Особенность — возможность выбора определенных участников согласования, его последовательность. Реализовать проект требовалось на стандартном модуле бизнес-процессов корпоративного портала «1С-Битрикс24». Для заказчика было важно разработать наглядный, простой и интуитивный интерфейс.
Эта задача решается без каких-либо сложностей. Но нельзя опустить тот факт, что стандартный модуль бизнес-процессов особой красотой и наглядностью не обладает. Поэтому совет, если вам нужен красивый сложный бизнес-процесс «под вас», то лучше не использовать стандартный модуль. Он накладывает рамки, стесняет и лишает простора.
Реализовать ЭЦП на корпоративном портале «1С-Битрикс24» для ведения авансовой отчетности.
В компании имеется отдел, главная задача которого распространять товар через промоушн, лоббирование интересов и другие виды личного воздействия на врачей, фармацевтов, представителей медицинской отрасли.
Для этого есть штат торговых представителей, разбросанных по всей стране, от Калининграда до Владивостока. Каждый агент продвигает продукт на своей, закреплённой территории. Чаще это его место жительства. Он несет издержки — встречи, транспортные расходы, питание — их возмещает компания. Процедура возмещения издержек — не что иное, как сдача авансовой отчетности, так знакомая нам по командировкам.
Ключевым и первичным моментом было пошагово описать бизнес-процесс сдачи авансовой отчетности. Хотя он стандартизирован и закреплен законодательно, но у каждой организации есть своя специфика. О ней мы поговорим позже. Идеальный же бизнес-процесс командировки, каким его задумало государство, выглядит так:
Генеральный директор подписывает приказ о направлении сотрудника в командировку. Часто эти приказы подписываются после командировки задним числом, но это не меняет дальнейший юридический ход процесса.
Командированный сотрудник получает денежные средства под отчет. Так он становится должником перед компанией.
Торговый представитель едет в командировку, сохраняет все билеты, чеки и другие подтверждающие документы. Это ключевой этап для возмещения средств. Все бумаги с информацией о расходах — первичная бухгалтерская документация, основание для увеличения расходов или уменьшения налогооблагаемой базы (прибыли организации). Разумеется, в законодательстве четко прописано, какие именно расходы могут быть зачтены, а какие нет. Все жестко стандартизировано.
Сотрудник по возвращению из командировки составляет авансовый отчет, чтобы с него «списали» долг. К авансовому отчету сотрудник прикладывает все подтверждающие документы: чеки, билеты и прочее.
Любой авансовый отчет, согласно российскому законодательству, в обязательном порядке имеет три подписи: командированного сотрудника, главного бухгалтера и генерального директора. В маленьких организациях генеральный директор может совмещать должность главного бухгалтера, а в больших — появляется подпись руководителя сотрудника.
После сдачи авансового отчета, сотрудник обязан вернуть остаток средств или получить недостающие.
Процесс, который нам нужно было автоматизировать для клиента идентичен по функциям, но имеет свои особенности:
Сотрудники находятся в командировках перманентно — все рабочее время. Даже посещение главного офиса — командировка, ведь сотрудники имеют постоянное место жительства, как правило, не в Москве.
Если подотчетные деньги на исходе, то следующий транш может быть выдан только после отчета по предыдущим. Именно тут возникало много сложностей, например:
Переложение этого процесса систематизации и оптимизации авансовой отчетности в ИТ-среду было поэтапным.
Изначально мы посмотрели на задачу через призму законодательства. Какие ограничения оно накладывает, что можно, а что нельзя. Вердикт: сдавать авансовую отчетность в цифровом формате не запрещено.
Так как данный бизнес-процесс планировалось реализовать на корпоративном портале Битрикс24, мы изучили вопрос интеграции ЭЦП в портал. Что важно, весь процесс должен проходить в режиме одного окна, т.е. из портала.
![]() |
Как оказалось, до этого никто ничего подобного не делал. По словам КриптоПро — признанных лидеров в области криптографической защиты информации и ЭЦП в России — мы первые реализовали интеграцию с их сервисом на PHP. |
Но самая большая сложность была вот в чем. Торговые представители пользуются только одним девайсом — корпоративным iPad. Подобные мобильные устройства не очень любят работать с сертификатами в реестрах или на внешних ключах (токенах, смарт-картах), по-настоящему удобных решений не так много. Мы остановились на интеграции с КриптоПро DSS.
В итоге автоматизированный бизнес-процесс авансовой отчетности работает следующим образом:
Каждому торговому представителю, выдается сертификат ЭЦП, его личный, именной (в принципе, он другим быть и не может).
При необходимости сдать авансовый отчет, сотрудник на своем планшете iPad, заходит на корпоративный портал компании и попадет в свой личный баланс.
Там он создает новый авансовый отчет, пошагово заполняя все строчки. Расходы он записывает с чеков, которые находятся в его руках. Параллельно агент фотографирует чеки на тот же iPad, и прикрепляет их к онлайн авансовому отчету.
В этот момент в 1С создаётся черновик авансового отчета, которому присваивается порядковый номер, это очень важный момент.
Когда все заполнено, форма авансового отчета создается автоматически. Она типовая, поэтому вероятность, что форма будет меняться — минимальна.
Сотрудник проверяет форму и подписывает её ЭЦП. Сам процесс подписи происходит по следующему алгоритму:
Пользователь на корпоративном портале жмет кнопку «подписать ЭЦП».
Документ через SOAP протокол без открытия дополнительных окон отправляется в КриптоПро DSS. КриптоПро DSS идентифицирует пользователя тремя шагами:
Логин/пароль от сервиса DSS — для удобства они совпадают с логин/паролем от портала. Получается, что вводить ничего не надо, пользователь сразу переходит ко второму шагу проверки.
ПИН-код, состоящий из 4 или 5 цифр (на выбор пользователя). Теперь сервис DSS может использовать сертификат, который хранится в его реестре на сервере.
Одноразовый пароль — SMS на мобильный телефон. Один документ – одно SMS.
Кстати, КриптоПро позволяют оставить в качестве проверки только ПИН-код или только SMS для экономии времени. Бухгалтеры заказчика так и поступили, оставив лишь ПИН-код.
Подписанный документ отправляется к руководителю сотрудника. Он проверяет форму отчета, лимиты, сверяет с фотографиями чеков. Если что-то не так — возвращает на доработку сотруднику с комментариями. Всё в порядке — подписывает документ с помощью облачного ЭЦП и отправляет на следующий шаг согласования.
Бухгалтер также подписывает документ ЭЦП и отправляет генеральному директору. На этом этапе бухгалтеру остается только провести уже заполненный и сохраненный авансовый отчет в 1С. С этого момента может выдаваться новый транш.
Генеральный директор подписывает отчет ЭЦП. На самом деле подпись директора уже формальность, необходимая по законодательству — отчет проверен руководителем, бухгалтером и документы для новой командировки могут уже готовиться.
После этого сотруднику выдаются новые денежные средства, и его виртуальный счет на корпоративном портале пополняется, обновляется сальдо.
Сотруднику приходит уведомление, что авансовый отчет подписан. Он отправляет оригиналы чеков в головной офис, и бухгалтер сверяет их с отчетом. Как правило, все совпадает, но если вскрываются факты жульничества и чека не хватает (хотя на фото он есть), авансовый отчет в 1С правится, а к сотруднику применяются меры воздействия.
![]() |
В процессе интеграции портала и КриптоПро DSS на стороне КриптоПро возникли сложности. Их ПО почему-то отказывалось работать по своей же документации. Но, в конечном счете, КриптоПро проявили профессионализм и исправили ошибки. |
На данный момент внедрение такой системы отработано, она исправна. Разработан уникальный модуль интеграции, позволяющий по протоколу soap передавать данные. Для пользователя это означает, что не надо переходить по ссылкам, не надо покидать окна корпоративного портала для подписи документов. Это очень удобно. Для компаний с большим количеством удаленных сотрудников, с многоэтапной подписью документов интегрированная подобным образом ЭЦП — спасательный круг в море бумажной волокиты и курьерской службы.
Метки: author AREALIDEA с-битрикс bitrix24 eds криптопро эцп битрикс24 |
Orange Pi на автомойке ч.3 |
sudo systemctl stop serial-getty@ttyS0.service
sudo systemctl mask serial-getty@ttyS0.service
Section "Device"
Identifier "default"
Driver "fbdev"
Option "Rotate" "CW"
EndSection
hid_multitouch
Section "ServerFlags"
Option "blank time" "0"
Option "standby time" "0"
Option "suspend time" "0"
Option "off time" "0"
EndSection
void pinMode(int pin, int mode)
{
FILE *file;
char path[80];
file=fopen("/sys/class/gpio/export" , "w");
fprintf(file, "%d", pin);
fclose(file);
sprintf(path, "/sys/class/gpio/gpio%d/direction", pin);
file=fopen(path , "w");
fprintf(file, "%s", mode ? "out" : "in");
fclose(file);
}
void digitalWrite(int pin, int value)
{
FILE *file;
char path[40];
sprintf(path, "/sys/class/gpio/gpio%d/value", pin);
file=fopen(path , "w");
fputc(value?'1':'0', file);
fclose(file);
}
Метки: author paul_155 программирование микроконтроллеров orange pi автомойка |
Восстановление данных с внешнего жесткого диска Seagate FreeAgent Go |
Метки: author hddmasters хранение данных восстановление данных восстановление информации залипание головок hdd винчестер жесткий диск |